Your SlideShare is downloading. ×
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Desarrollo de proyectos tecnológicos con java
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Desarrollo de proyectos tecnológicos con java

1,401

Published on

Desarrollo de proyectos tecnológicos con Java

Desarrollo de proyectos tecnológicos con Java

Published in: Education
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,401
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
3
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. 2Esta obra de ´scar Belmonte et al. est´ bajo una licencia Creative O aCommons Reconocimiento-NoComercial-CompartirIgual 3.0 Unported
  • 2. ´Indice general1. Introducci´no 13 1.1. Origen del lenguaje de programaci´n Java . . . . . . . . . . . . . o 13 1.2. Caracter´ıstica de Java . . . . . . . . . . . . . . . . . . . . . . . . 14 1.3. El entorno de desarrollo integrado Eclipse . . . . . . . . . . . . . 15 1.3.1. Principales caracter´ısticas del entorno de desarrollo Eclipse 16 1.3.2. Descarga e instalaci´n de Eclipse . . . . . . . . . . . . . . o 16 1.3.3. Configurando el aspecto de Eclipse: Perspectivas y Vistas 16 1.3.4. El primer ejemplo . . . . . . . . . . . . . . . . . . . . . . 18 1.4. Herramientas de desarrollo . . . . . . . . . . . . . . . . . . . . . . 21 1.4.1. A˜adiendo nueva funcionalidad a Eclipse: los plug-ins . . n 222. Clases 23 2.1. Definici´n de una clase . . . . . . . . . . . . . . o . . . . . . . . . . 24 2.2. Miembros de una clase . . . . . . . . . . . . . . . . . . . . . . . . 25 2.2.1. Atributos de una clase . . . . . . . . . . . . . . . . . . . . 25 2.2.2. M´todos de una clase. . . . . . . . . . . e . . . . . . . . . . 26 2.2.3. Constructores. . . . . . . . . . . . . . . . . . . . . . . . . 28 2.2.4. Sobrecarga de m´todos y constructores . e . . . . . . . . . . 32 2.3. Tipos de datos en Java. . . . . . . . . . . . . . . . . . . . . . . . 33 2.3.1. Arrays de datos en Java. . . . . . . . . . . . . . . . . . . . 34 2.4. Estructuras de control. . . . . . . . . . . . . . . . . . . . . . . . . 36 2.4.1. Estructuras de control de repetici´n. . . o . . . . . . . . . . 37 2.4.2. Estructuras de control de selecci´n. . . . o . . . . . . . . . . 39 2.5. Modificadores de acceso. . . . . . . . . . . . . . . . . . . . . . . . 40 2.6. Modificadores static y final. . . . . . . . . . . . . . . . . . . . 42 2.7. El recolector de basura. . . . . . . . . . . . . . . . . . . . . . . . 43 2.8. Finalizaci´n. . . . . . . . . . . . . . . . . . . . o . . . . . . . . . . 44 2.9. Comentarios. Comentarios de documentaci´n. . o . . . . . . . . . . 453. Herencia e Interfaces 51 3.1. Herencia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.2. Extensi´n de una clase. . . . . . . . . . . . . . . . . o . . . . . . . 52 3.2.1. Sobrescribir atributos. . . . . . . . . . . . . . . . . . . . . 54 3.2.2. Sobrescribir m´todos. . . . . . . . . . . . . . e . . . . . . . 56 3.2.3. La palabra reservada super. . . . . . . . . . . . . . . . . . 59 3.2.4. El constructor por defecto y la clase Object. . . . . . . . 59 3.2.5. El operador instanceof. . . . . . . . . . . . . . . . . . . 60 3.2.6. El modificador final. . . . . . . . . . . . . . . . . . . . . 61 3
  • 3. 4 ´ INDICE GENERAL 3.2.7. M´todos static. . . . e . . . . . . . . . . . . . . . . . . . . 62 3.3. Clases abstractas. . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 3.4. Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 3.5. Enumeraciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.6. Paquetes en Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 3.7. Clases e interface anidados . . . . . . . . . . . . . . . . . . . . 714. Subversion 75 4.1. ¿Qu´ es un sistema de control de versiones? . . e . . . . . . . . . . 76 4.2. Principales caracter´ ısticas de Subversion . . . . . . . . . . . . . . 76 4.3. Creaci´n de un repositorio . . . . . . . . . . . . o . . . . . . . . . . 77 4.4. Trabajo con repositorios . . . . . . . . . . . . . . . . . . . . . . . 78 4.4.1. Obteniendo informaci´n del repositorio o . . . . . . . . . . 82 4.5. Integraci´n con Eclipse . . . . . . . . . . . . . . o . . . . . . . . . . 845. Excepciones 87 5.1. ¿Qu´ es una excepci´n? . . . . . e o . . . . . . . . . . . . . . . . . . 87 5.1.1. Tipos de excepciones . . . . . . . . . . . . . . . . . . . . . 88 5.2. C´mo se gestiona una excepci´n o o . . . . . . . . . . . . . . . . . . 88 5.3. Creaci´n de excepciones propias . o . . . . . . . . . . . . . . . . . . 916. Pruebas unitarias con JUnit 93 6.1. ¿Qu´ son las pruebas unitarias? . . . . . . . . . . . . . . . . . e . . 94 6.1.1. Principios FIRST para el dise˜o de pruebas unitarias n . . 94 6.2. Pruebas unitarias con JUnit . . . . . . . . . . . . . . . . . . . . . 95 6.2.1. Creaci´n de clases de prueba . . . . . . . . . . . . . . o . . 95 6.2.2. La anotaci´n @Test . . . . . . . . . . . . . . . . . . . o . . 96 6.2.3. Las anotaciones @Before y @After . . . . . . . . . . . . . 98 6.2.4. Las anotaciones @BeforeClass y @AfterClass . . . . . . 99 6.2.5. Pruebas con bater´ de datos de entrada . . . . . . . . ıa . . 100 6.2.6. Ejecutar varias clases de prueba. Test Suites . . . . . . . 101 6.3. Cobertura de las pruebas . . . . . . . . . . . . . . . . . . . . . . 102 6.3.1. EclEmma y su plug-in para Eclipse . . . . . . . . . . . . . 1037. Entrada y Salida 105 7.1. Flujos (Streams) . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 7.2. Flujos de bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 7.3. Flujos de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . 108 7.4. Conexi´n entre flujos de bytes y de caracteres o . . . . . . . . . . . 109 7.5. El sistema de ficheros y flujos a ficheros . . . . . . . . . . . . . . 110 7.5.1. El sistema de ficheros . . . . . . . . . . . . . . . . . . . . 110 7.5.2. Flujos a ficheros . . . . . . . . . . . . . . . . . . . . . . . 110 7.6. Serializaci´n . . . . . . . . . . . . . . . . . . . o . . . . . . . . . . . 1128. Algunas clases de utilidad del paquete est´ndar a 117 8.1. La clase Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 8.2. Trabajo con cadenas de caracteres . . . . . . . . . . . . . . . . . 120 8.2.1. La clase String . . . . . . . . . . . . . . . . . . . . . . . 120 8.2.2. Las clases StringBuffer y StringBuilder . . . . . . . . 121 8.3. Clases recubridoras . . . . . . . . . . . . . . . . . . . . . . . . . . 122
  • 4. ´INDICE GENERAL 5 8.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 8.5. Trabajo con fechas . . . . . . . . . . . . . . . . . . . . . . . . . . 128 8.5.1. La clase Date . . . . . . . . . . . . . . . . . . . . . . . . . 128 8.5.2. Las clases Calendar y GregorianCalendar . . . . . . . . 129 8.6. Matem´ticas . . . . . . . . . . . . . . . . . . . . . a . . . . . . . . 129 8.6.1. La clase Math . . . . . . . . . . . . . . . . . . . . . . . . . 129 8.6.2. La clase Random . . . . . . . . . . . . . . . . . . . . . . . 1309. Programaci´n con gen´ricos o e 133 9.1. ¿Qu´ son los tipos de datos gen´ricos? . . . . . . . . . e e . . . . . . 133 9.2. M´todos gen´ricos . . . . . . . . . . . . . . . . . . . . e e . . . . . . 134 9.3. Clases gen´ricas . . . . . . . . . . . . . . . . . . . . . . e . . . . . . 135 9.4. Ampliaci´n del tipo gen´rico . . . . . . . . . . . . . . o e . . . . . . 138 9.4.1. Tipos gen´ricos con l´ e ımite superior . . . . . . . . . . . . . 139 9.4.2. Comodines . . . . . . . . . . . . . . . . . . . . . . . . . . 139 9.5. Borrado de tipo y compatibilidad con c´digo heredado o . . . . . . 14110.Construcci´n de proyectos con Ant o 143 10.1. Qu´ es Ant . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 144 10.2. Definici´n del proyecto . . . . . . . . . . . o . . . . . . . . . . . . . 144 10.2.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 10.2.2. Tareas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 10.3. Compilar el c´digo fuente de un proyecto o . . . . . . . . . . . . . 146 10.4. Propiedades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 10.5. Estructuras path-like . . . . . . . . . . . . . . . . . . . . . . . . 147 10.6. Ejecuci´n de las Pruebas Unitarias . . . . o . . . . . . . . . . . . . 148 10.7. Generaci´n de la documentaci´n . . . . . o o . . . . . . . . . . . . . 150 10.8. Empaquetado de la aplicaci´n . . . . . . . o . . . . . . . . . . . . . 151 10.9. Ejecuci´n y limpieza . . . . . . . . . . . . o . . . . . . . . . . . . . 15111.Interfaces gr´ficas de usuario a 153 11.1. APIs para la programaci´n de interfaces gr´ficos de usuario en o a Java: AWT y Swing . . . . . . . . . . . . . . . . . . . . . . . . . 154 11.2. Contenedores y Componentes . . . . . . . . . . . . . . . . . . . . 155 11.3. Gestores de Aspecto (Layout Managers) . . . . . . . . . . . . . . 155 11.4. Detecci´n de eventos: Escuchadores . . . . . . . . . . . . . . . . . o 157 11.5. Algunos componentes Swing . . . . . . . . . . . . . . . . . . . . . 162 11.5.1. JLabel, muestra texto o iconos . . . . . . . . . . . . . . . 162 11.5.2. JButton, botones que el usuario puede pulsar . . . . . . . 162 11.5.3. JTextField, campos de introducci´n de texto . . . . . . . o 163 11.5.4. JRadioButton, botones de opciones . . . . . . . . . . . . 164 11.5.5. JCheckBox, botones de selecci´n m´ltiple . . . . . . . . . o u 166 11.5.6. JList, listas de selecci´n . . . . . . . . . . . . . . . . . . o 166 11.6. El patr´n de dise˜o Modelo/Vista/Controlador . . . . . . . . . . o n 16812.Applets 173 12.1. ¿Qu´ son los Applets? . . . . . . . . . . . . e . . . . . . . . . . . . 173 12.2. Ciclo de vida de un Applet . . . . . . . . . . . . . . . . . . . . . 174 12.3. C´digo HTML para contener un Applet . . o . . . . . . . . . . . . 175 12.4. Lectura de par´metros de la p´gina HTML a a . . . . . . . . . . . . 176
  • 5. 6 ´ INDICE GENERAL 12.5. Convertir una aplicaci´n Swing en un Applet . . . . . . . . . . . 176 o 12.6. Comunicaci´n entre Applets . . . . . . . . . . . . . . . . . . . . . 177 o13.Control de errores con MyLyn y Bugzilla 181 13.1. Sistema de control de tareas MyLyn . . . . . . . . . . . . . . . . 182 13.1.1. Cual es el objetivo de MyLyn . . . . . . . . . . . . . . . . 182 13.1.2. Trabajar con MyLyn . . . . . . . . . . . . . . . . . . . . . 182 13.2. Sistema de gesti´n de errores Bugzilla . . . . . . . . . . . . . . . o 188 13.2.1. Cual es el objetivo de Bugzilla . . . . . . . . . . . . . . . 188 13.2.2. Instalaci´n de Bugzilla . . . . . . . . . . . . . . . . . . . . o 188 13.2.3. Trabajar con Bugzilla . . . . . . . . . . . . . . . . . . . . 195 13.3. Acceso a Bugzilla desde MyLyn y Eclipse . . . . . . . . . . . . . 199 13.3.1. Beneficios de la combinaci´n de Bugzilla y MyLyn desde o Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 13.3.2. Trabajo con MyLyn y Bugzilla desde Eclipse . . . . . . . 20114.Programaci´n concurrente con Hilos o 207 14.1. ¿Qu´ es un hilo? Utilidades. Consideraciones sobre el uso de hilos 208 e 14.2. Creaci´n de hilos en Java . . . . . . . . . . . . . . . . . . . . . . 209 o 14.2.1. Creaci´n de un Hilo extendiendo a la clase Thread . . . . 209 o 14.2.2. Creaci´n de un Hilo mediante una clase interna . . . . . . 210 o 14.2.3. Creaci´n de un Hilo mediante una clase interna an´nima . 211 o o 14.3. Ciclo de vida de un hilo . . . . . . . . . . . . . . . . . . . . . . . 212 14.4. Control de hilos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 14.5. Sincronizaci´n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 o 14.5.1. Sincronizac´n utilizando los cerrojos intr´ o ınsecos . . . . . . 215 14.5.2. Sincronizaci´n utilizando el interface Lock . . . . . . . 218 o15.Programaci´n para la Red o 221 15.1. Trabajo con URLs . . . . . . . . . . . . . . . . . . . . . . . . . . 222 15.1.1. ¿Qu´ es una URL? . . . . . . . e . . . . . . . . . . . . . . . 222 15.1.2. Leer desde una URL . . . . . . . . . . . . . . . . . . . . . 223 15.1.3. Escribir a una URL . . . . . . . . . . . . . . . . . . . . . 223 15.2. Trabajo con Sockets . . . . . . . . . . . . . . . . . . . . . . . . . 225 15.2.1. ¿Qu´ es un Socket? . . . . . . . e . . . . . . . . . . . . . . . 225 15.2.2. Sockets bajo el protocolo TCP . . . . . . . . . . . . . . . 225 15.2.3. Sockets bajo el protocolo UDP . . . . . . . . . . . . . . . 22716.Patrones de dise˜ o n 231 16.1. Principios de POO . . . . . . . . . . . . . . . . . . . . . . . . . . 232 16.2. ¿Qu´ son los patrones de dise˜o? . . . . . e n . . . . . . . . . . . . . 233 16.3. ¿Qu´ es el acoplamiento entre clases y por e qu´ hay que evitarlo? e 233 16.4. Grupos de patrones de dise˜o . . . . . . . n . . . . . . . . . . . . . 233 16.5. El patr´n de dise˜o Singleton . . . . . . . o n . . . . . . . . . . . . . 233 16.5.1. Situaci´n que intenta resolver . . . o . . . . . . . . . . . . . 234 16.5.2. Ejemplo de implementaci´n . . . . o . . . . . . . . . . . . . 234 16.6. El patr´n de dise˜o Factory Method . . . o n . . . . . . . . . . . . . 235 16.6.1. Situaci´n que intenta resolver . . . o . . . . . . . . . . . . . 235 16.6.2. Ejemplo de implementaci´n . . . . o . . . . . . . . . . . . . 236 16.7. El patr´n de dise˜o Abstract Factory . . . o n . . . . . . . . . . . . . 238
  • 6. ´INDICE GENERAL 7 16.7.1. Situaci´n que intenta resolver o . . . . . . . . . . . . . . . . 238 16.7.2. Ejemplo de implementaci´n . o . . . . . . . . . . . . . . . . 238 16.8. El patr´n de dise˜o Strategy . . . . . o n . . . . . . . . . . . . . . . . 244 16.8.1. Situaci´n que intenta resolver o . . . . . . . . . . . . . . . . 245 16.8.2. Ejemplo de implementaci´n . o . . . . . . . . . . . . . . . . 245 16.9. El patr´n de dise˜o Observer . . . . o n . . . . . . . . . . . . . . . . 247 16.9.1. Situaci´n que intenta resolver o . . . . . . . . . . . . . . . . 247 16.9.2. Ejemplo de implementaci´n . o . . . . . . . . . . . . . . . . 248 16.10. l patr´n de dise˜o Decorator . . . . E o n . . . . . . . . . . . . . . . . 249 16.10.1.Situaci´n que intenta resolver o . . . . . . . . . . . . . . . . 250 16.10.2.Ejemplo de implementaci´n . o . . . . . . . . . . . . . . . . 250A. build.xml 255B. Aplicaci´n Hipoteca o 259C. Ejemplo sincronizaci´n o 265
  • 7. 8 ´ INDICE GENERAL
  • 8. PrefacioLa escritura de un libro es una tarea ingente. La motivaci´n para abordarla debe oser, al menos, tan grande como la tarea que se desea acometer. Para nosotros,la motivaci´n ha consistido en escribir un libro que se distinguiera del resto de olibros que abordan el aprendizaje del lenguaje de programaci´n Java. o Por un lado, existen excelentes libros que muestran c´mo programar en Java. oPor otro lado existen excelentes libros, en n´mero inferior, que muestran c´mo u outilizar herramientas de ayuda y soporte al desarrollo de proyectos en Java.Pensamos que, entre ellos, exist´ cabida para escribir un libro que abordase el ıaaprendizaje de Java al mismo tiempo que las herramientas imprescindibles deayuda al desarrollo. Dentro de nuestra Universidad, la Jaume I, hemos impartido, y seguimoshaci´ndolo, cursos sobre el lenguaje de programaci´n Java para todo tipo de e oalumnado: desde alumnos de las distintas titulaciones de inform´tica, alumnos aextranjeros en el Master Europeo Erasmus Mundus sobre tecnolog´ Geoes-ıaspaciales, hasta profesionales que quieren mantener al d´ su conocimiento y ıamejorar sus expectativas laborales. Esta experiencia nos ha dado la confianzasuficiente como para animarnos a escribir el presente libro. Y, a pesar del contacto casi diario con Java y sus tecnolog´ reconocemos ıas,que a´n nos queda mucho por aprender, que el mundo que brinda el aprendizaje ude Java es inmenso y que se renueva constantemente. Esto ultimo es s´ ´ ıntomade que la comunidad alrededor de esta tecnolog´ est´ viva y posee un gran ıa aentusiasmo.Objetivos del libroDos son los objetivos principales del este libro: Presentar el lenguaje de programaci´n Java. o Presentar algunas de las herramientas de desarrollo que ayudan en el desa- rrollo de proyectos utilizando Java. Con un poco m´s de detalle, en el primer objetivo hemos pretendido no s´lo a opresentar el lenguaje de programaci´n, adem´s indicamos unas directrices para o acrear c´digo de calidad, c´digo que sea f´cil leer, f´cil mantener y que se puede o o a aprobar de manera autom´tica. a El segundo de los objetivos es casi una necesidad imperiosa a los equipos dedesarrollo que siguen utilizando como herramienta de control de versiones undirectorio compartido. O a aquellos equipos de desarrollo que siguen probando 9
  • 9. 10 ´ INDICE GENERALsus aplicaciones de manera manual. O para aquellos equipos de desarrollo queutilizan como sistema de seguimiento de errores el correo electr´nico. Y un largo oetc´tera de pr´cticas desaconsejadas. e aC´mo est´ organizado este libro o aLa Figura 1 muestra la organizaci´n en cap´ o ıtulos del presente libro. Cada unode los recuadros representa un cap´ ıtulo. Los cap´ ıtulos se han agrupado en dosgrandes bloques. En el primero de ellos Java b´sico hemos agrupado los cap´ a ıtulosque consideramos introductorios, y que representan el n´cleo de la programaci´n u oorientada a objetos en Java. En el segundo grupo Java avanzado aparecen loscap´ıtulos que consideramos aspectos avanzados del lenguaje con respecto a loscap´ıtulos del primer grupo. En ambos grupos hay cap´ ıtulos que no aparecen en la l´ ınea principal delflujo, estos cap´ ıtulos son los que presentan herramientas que consideramos degran utilidad en el desarrollo de proyectos inform´ticos utilizando tecnolog´ a ıasJava. El orden de introducci´n de estas herramientas ha sido fuente de largas oconversaciones: ¿Es conveniente introducir al principio la herramienta JUnitsiguiendo una orientaci´n hacia el desarrollo guiado por pruebas? ¿Debemos odelegar hasta el segundo bloque de cap´ ıtulos el dedicado a la construcci´n de oproyectos con Ant? Hemos optado por seguir un orden quiz´s m´s conservado y a amenos arriesgado, intentando presentar las herramientas en el momento en queconceptualmente se entienda cual es la necesidad que vienen a cubrir. Esperamosque esta ordenaci´n haga el tr´nsito suave entre el aprendizaje de Java como o alenguaje de programaci´n y las herramientas de ayuda al desarrollo. oQuien deber´ leer este libro ıaEl publico objetivo de este libro son los desarrolladores que quieran aprenderel lenguaje de programaci´n Java y ya posean conocimientos de programaci´n o oestructurada y orientaci´n a objetos. Los conceptos del lenguaje son presentados odesde la base, suponiendo que es la primera vez que el lector se aproxima allenguaje de programaci´n Java. o Pero este libro tambi´n est´ pensado para aquellas personas que conocen el e alenguaje de programaci´n Java y a´n no han descubierto la gran cantidad de o uherramientas de ayuda que existen en el desarrollo de proyecto.AgradecimientosLa secci´n de agradecimientos es posiblemente una de las m´s complicadas de o aescribir. Debe tener un equilibrio entre el espacio dedicado a ella y el reconoci-miento a todas las personas, que de un modo u otro han contribuido a que unlibro tenga su forma final. Para no dejarnos por citar el nombre de nadie, preferimos ampliar nuestroagradecimiento a colectivos. En primer lugar a nuestro alumnos, por que a finde cuentas es a ellos a los que va dirigido este libro. Con sus preguntas, aprecia-ciones, comentarios y dudas nos han ayudado a darnos cuenta de donde estabanlos escollos en la lectura de este libro.
  • 10. ´INDICE GENERAL 11 Figura 1: Organizaci´n del libro. o
  • 11. 12 ´ INDICE GENERAL Tambi´n a nuestros compa˜eros de la Universidad, porque con sus comen- e ntarios y rectificaciones nos han ayudado a eliminar errores en los contenidos. Y finalmente a nuestros amigos por su ´nimo constante para que esta labor allegase a buen puerto. A todos ellos gracias.
  • 12. Cap´ ıtulo 1Introducci´n oContenidos 1.1. Origen del lenguaje de programaci´n Java . . . . o 13 1.2. Caracter´ ıstica de Java . . . . . . . . . . . . . . . . . 14 1.3. El entorno de desarrollo integrado Eclipse . . . . 15 1.3.1. Principales caracter´ ısticas del entorno de desarrollo Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.3.2. Descarga e instalaci´n de Eclipse . . . . . . . . . . . o 16 1.3.3. Configurando el aspecto de Eclipse: Perspectivas y Vistas . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.3.4. El primer ejemplo . . . . . . . . . . . . . . . . . . . 18 1.4. Herramientas de desarrollo . . . . . . . . . . . . . 21 1.4.1. A˜adiendo nueva funcionalidad a Eclipse: los plug-ins n 22Introducci´n oEn este cap´ıtulo de introducci´n se muestran los or´ o ıgenes del lenguaje de pro-gramaci´n Java, sus principales caracter´ o ısticas y las diferencias con respecto aC++, lenguaje del que hereda gran parte de la sintaxis. En la segunda parte del cap´ıtulo se presenta en Entorno de Desarrollo Inte-grado Eclipse, los fundamentos para crear proyectos Java utilizando este Entornoy c´mo se puede enriquecer su funcionalidad mediante la instalaci´n de plug-ins. o o1.1. Origen del lenguaje de programaci´n Java oEl lenguaje de programaci´n Java tiene sus or´ o ıgenes en un lenguaje de progra-maci´n anterior, llamado Oak (roble en ingl´s), que naci´ de un proyecto interno o e oen Sun Microsystems en el a˜o 1991 llamado Green project. n Oak fue creado con el objetivo de ser el lenguaje de programaci´n con el oque programar dispositivos electr´nicos dom´sticos, en particular aparatos de o etelevisi´n inteligentes e interactivos. Oak ten´ entre otras, las siguientes carac- o ıa,ter´ ısticas de inter´s: e 13
  • 13. 14 CAP´ ´ ITULO 1. INTRODUCCION Orientado a objetos y de prop´sito general. o Robusto. Sintaxis parecida a C++. Independiente del hardware. El proyecto de televisi´n inteligente e interactiva nunca se materializ´. De o omodo simult´neo, a principios de la d´cada de los 90 surgi´ Internet y con a e oella, la aparici´n de los primeros navegadores web. Los l´ o ıderes del Green projectfueron conscientes de la importancia que iba a tener Internet y orientaron sulenguaje de programaci´n Oak para que programas escritos en este lenguaje de oprogramaci´n se pudiesen ejecutar dentro del navegador web Mozilla. Y este fue oel inicio de Java, as´ llamado porque cuando se intent´ registrar el nombre Oak ı oeste ya estaba registrado. Las nuevas caracter´ ısticas generales que se a˜adieron son: n Seguridad, ya que los programas que se ejecutan en un navegador se des- cargan desde Internet. Potencia, ya no se ten´ la restricci´n de la ejecuci´n en dispositivos de ıa o o electr´nica de consumo. o Java se ha consolidado como lenguaje de programaci´n gracias a que su ocurva de aprendizaje es relativamente suave para programadores provenientesde C++. Adem´s, la ventaja de que un programa escrito en Java se puede aejecutar en una gran cantidad de plataformas ha hecho de ´l un interesante elenguaje de programaci´n por su ((universalidad)). o1.2. Caracter´ ıstica de JavaJava es un lenguaje de programaci´n orientado a objetos y de prop´sito gene- o oral que toma de otros lenguajes de programaci´n algunas ideas fundamentales, oen particular toma de Smalltalk el hecho de que los programas Java se ejecu-tan sobre una m´quina virtual . Y del lenguaje de programaci´n C++ toma su a osintaxis. El uso de la m´quina virtual garantiza la independencia de la plataforma en aJava. Si disponemos de una m´quina virtual para nuestra plataforma, podremos aejecutar el mismo programa escrito en Java sin necesidad de volverlo a compilar. En el proceso de compilaci´n de un programa en Java, se genera un c´digo o ointermedio, llamado bytecode, que la m´quina virtual interpreta y traduce a allamadas nativas del sistema sobre el que se ejecuta la m´quina virtual. As´ a ı,una m´quina virtual para una plataforma Windows 7 de 64 bits, traducir´ los a abytecodes a c´digo nativo para esta plataforma, y otra m´quina virtual para una o aplataforma Linux de 64 bits traducir´ los mismos bytecodes a c´digo nativo para a oesta otra plataforma. Los bytecodes son los mismos en ambos casos, las m´quinas avirtuales sobre las que se ejecutan son nativas de la plataforma correspondiente. Puede parecer que este paso de traducir los bytecodes a c´digo nativo de la oplataforma suponga una p´rdida de rendimiento en la ejecuci´n de los programas e oen Java, pero esto no es as´ gracias a la introducci´n de la tecnolog´ JIT (Just ı o ıa
  • 14. 1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE 15In Time compilation). La idea b´sica de esta tecnolog´ es que la primera vez a ıaque se llama a un m´todo, este se interpreta generando c´digo nativo de la e oplataforma sobre la que se ejecuta la m´quina virtual, pero una vez generado aeste c´digo nativo, se almacena, de tal modo que la siguiente vez que se llama oal mismo m´todo no es necesaria su interpretaci´n ya que el c´digo nativo para e o oese m´todo se almacen´ previamente. e o Otras caracter´ ısticas generales de Java son: Seguridad desde el punto de vista del programador: • Comprobaci´n estricta de tipos. o • Gesti´n de excepciones. o • No existen punteros. • Recolector de basura. Seguridad desde el punto de vista del usuario de aplicaciones: • Los programas se ejecutan sobre una m´quina virtual. a • Espacio de nombre. Soporta programaci´n concurrente de modo nativo. o Los tipos de datos est´n estandarizados. a S´lo se admite herencia simple. o1.3. El entorno de desarrollo integrado EclipseUn entorno integrado de desarrollo o IDE de sus siglas en ingl´s (emphIntegrated eDevelop Environment) nos permite escribir c´digo de un modo c´modo. La o ocomodidad reside en que los entornos de desarrollo integrados son mucho m´s aque un simple editor de textos. Algunas caracter´ ısticas comunes a los IDE son: Coloreado de la sintaxis. Herramientas de b´squeda. u Asistentes para la escritura de c´digo. o Ejecuci´n de aplicaciones sin abandonar el entorno. o Herramientas de depuraci´n de c´digo. o o Junto a estas caracter´ ısticas, los modernos IDE poseen algunas otras real-mente espectaculares como por ejemplo: Conexi´n con sistemas de control de versiones. o Conexi´n con sistema de seguimiento de errores. o Facilidades para la creaci´n de tareas. o Herramientas avanzadas para el an´lisis de c´digo. a o
  • 15. 16 CAP´ ´ ITULO 1. INTRODUCCION Herramientas de an´lisis de rendimiento. a Conexi´n a gestores de bases de datos. o Eclipse es un IDE orientado al desarrollo de proyectos con tecnolog´ Java, ıaaunque no es el unico lenguaje de programaci´n al que da soporte. Eclipse es ´ ouna herramienta de software libre, mantenido por la Eclipse Foundation.1.3.1. Principales caracter´ ısticas del entorno de desarrollo EclipseEclipse re´ne todas las caracter´ u ısticas comunes a los modernos IDE enumeradasm´s arriba. Adem´s posee un sistema de plug-ins con los que se pueden a˜adir a a nnuevas funcionalidades. Por ejemplo, mediante un plug-in nos podemos conectaral sistema de control de versiones Subversion1.3.2. Descarga e instalaci´n de Eclipse oEclipse se puede descargar desde el sitio web http://www.eclipse.org. Existenversiones para las principales plataformas y sistemas operativos. Una particularidad de Eclipse es que no necesita instalaci´n. Una vez des- ocargado el fichero comprimido, lo unico que debemos hacer para empezar a ´utilizarlo es descomprimirlo en alg´n directorio y seguidamente ejecutar el bi- unario correspondiente a nuestra plataforma. La Figura 1.1 muestra la p´gina ade inicio de Eclipse. Los iconos que muestra esta pantalla son enlaces a sitiosde informaci´n sobre Eclipse. La pantalla de inicio se puede cerrar pulsando el oaspa que se encuentra a la derecha de la leyenda Welcome.1.3.3. Configurando el aspecto de Eclipse: Perspectivas y VistasEl interface gr´fico de usuario de Eclipse cuenta con dos conceptos fundamen- atales: las Perspectivas y las Vistas. Una Perspectiva es un contenedor de Vistas. En una misma Perspectivapodemos agrupar m´s de una Vista. a Las Vistas por su lado, son componentes visual que pueden mostrar desde uneditor de c´digo, hasta un ´rbol de jerarqu´ de clases en Java. En la figura 1.2 o a ıase muestra el aspecto de Eclipse al mostrar la Perspectiva por defecto orientadaal desarrollo de aplicaciones Java. Esta perspectiva contiene varias vistas, por ejemplo la vista Package Explorerdonde se muestra informaci´n de la configuraci´n de nuestros proyectos. La vista o oProblems muestra un listado con los errores y avisos presentes en el c´digo de onuestro proyecto. Cada una de estas vistas est´ orientada a presentar un tipo ade informaci´n de nuestro proyecto o tareas relacionadas con ´l. o e El aspecto de las perspectivas se puede configurar. Te habr´s dado cuenta aque cada una de estas vistas est´ etiquetada con un nombre dentro de una asolapa, si haces click sobre una de estas solapas sin soltar el bot´n del rat´n, o opuedes trasladar la vista a cualquier otra posici´n dentro de la perspectiva. Esto ote permite organizar las vistas dentro de las perspectivas seg´n tus preferencias. u Existe una gran cantidad de vistas, puedes acceder a cualquiera de ellas atrav´s de la opci´n Show view del men´ Window. e o u
  • 16. 1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE 17 Figura 1.1: Pantalla inicial de EclipseFigura 1.2: Perspectiva inicial orientada al desarrollo de proyectos Java2 SE.
  • 17. 18 CAP´ ´ ITULO 1. INTRODUCCIONFigura 1.3: Ventana para la creaci´n de un proyecto Java utilizando Eclipse. o Figura 1.4: Estructura m´ ınima de un proyecto en Eclipse.1.3.4. El primer ejemploVamos a crear un primer proyecto Java con Eclipse. Para ello, simplemente hazclick con el bot´n derecho en la vista Package Explorer, y sobre el men´ emer- o ugente que aparecer´ selecciona New → Project, se abrir´ una ventana como la a amostrada en la Figura 1.3. En esta ventana lo unico que vamos a introducir es ´el nombre del proyecto. Una vez introducido el nombre del proyecto, pulsa el bot´n Finish, ver´s o aque el aspecto de Eclipse se actualiza para mostrar el nuevo proyecto reci´n ecreado. En la vista Package Explorer aparecer´ el nombre del nuevo proyecto areci´n creado. La vista de proyecto sigue una estructura de ´rbol, que puedes e adesplegar, el resultado se muestra en la Figura 1.4 El siguiente paso que vamos a dar es crear una nueva clase en nuestro proyec-to. Esta clase va a ser muy sencilla, y unicamente nos va a servir para conocer ´cual es el procedimiento de creaci´n, edici´n, compilaci´n y ejecuci´n utilizando o o o oEclipse. Para crear una nueva clase, haz click con el bot´n derecho del rat´n o osobre el nombre del proyecto reci´n creado, se abrir´ un men´ emergente, se- e a ulecciona la opci´n New → Class, presta atenci´n al icono que se dibuja a la o oizquierda de esta opci´n, y ver´s que ese mismo icono la encuentras en la barrar o ade herramientas en la parte superior de la ventana de Eclipse. Otro procedi-miento, m´s r´pido, de crear una nueva clase en Eclipse es pulsar directamente a a
  • 18. 1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE 19 Figura 1.5: Creaci´n de una nueva clase Java en Eclipse. oese icono en la barra de herramientas. Al seleccionar esta opci´n, se abrir´ la nueva ventana mostrada en la Figura o a1.5. En esta ventana vamos a introducir tres piezas de informaci´n: o Un nombre de paquete en min´sculas (en el Cap´ u ıtulo 3 conocer´s con a detalle el significado de los paquetes en Java. Un nombre de clase con la primera letra de cada palabra en may´sculas u y sin espacios entre ellas. Selecciona la opci´n public static void main(String[] args) o Esta tres piezas de informaci´n aparecen en la Figura 1.5. Recuerda intro- oducir el nombre del paquete y de la clase utilizando may´sculas y min´sculas u utal y como se muestra en la Figura 1.5, en el Cap´ ıtulo 2 conoceremos algunasde estas convenciones de codificaci´n Java. Finalmente pulsa el bot´n Finish. o oVer´s que de nuevo se actualiza la estructura del proyecto, ahora podr´s ver que a ase ha creado, bajo el nodo del ´rbol src un nuevo nodo con nombre hola y bajo a´l el nodo HolaMundo.java. Adem´s, se ha abierto una nueva vista del editore ade c´digo tal y como muestra la Figura 1.6. o Hagamos una peque˜a modificaci´n sobre este c´digo. A˜ade la siguiente n o o nl´ ınea tal y como se muestra en la Figura 1.7. Esta instrucci´n sirve para mostrar ouna cadena de texto por consola. Una vez escrita la nueva l´ ınea de c´digo graba el fichero, para ello pulsa la ocombinaci´n de teclas Ctrl + S. El siguiente paso va a ser ejecutar el programa, opara ello haz click con el bot´n derecho del rat´n sobre el editor de c´digo o o oy en el men´ emergente que aparecer´ selecciona la opci´n Run As → Java u a oApplication.
  • 19. 20 CAP´ ´ ITULO 1. INTRODUCCION Figura 1.6: Aspecto del editor de c´digo Java. o Figura 1.7: El primer programa en Java Hola mundo.
  • 20. 1.4. HERRAMIENTAS DE DESARROLLO 21Figura 1.8: Resultado de le ejecuci´n del primer programa en Java Hola mundo. o En la vista Console podr´s ver el resultado de la ejecuci´n del programa, tal a oy como muestra la Figura 1.8 Por defecto, cada vez que haces modificaciones en el c´digo de definici´n o ode una clase y grabas el fichero, Eclipse compila autom´ticamente el c´digo a omodificado. De este modo la compilaci´n se realiza de modo transparente a omedida que vamos trabajando en nuestro proyecto. Con estos sencillos pasos hemos creado nuestro primer proyecto en Java, yuna unica clase en la que hemos introducido una l´ ´ ınea de c´digo que muestra oun mensaje de texto por consola. El trabajo con Eclipse es realmente sencillo.1.4. Herramientas de desarrolloCuando nos planteamos desarrollar un proyecto inform´tico ser´ de gran ayuda a aelegir una serie de herramientas que nos faciliten actividades tales como el con-trol de versiones de nuestro c´digo o la prueba autom´tica de nuestros m´todos. o a e En el estado actual de madurez de las tecnolog´ de desarrollo de proyectos ıasinform´ticos, resulta impensable iniciar el desarrollo de un proyecto sin planificar ael control de versiones, la gesti´n y seguimiento de errores, las herramientas de odespliegue de la aplicaci´n, y un largo etc´tera. o e Afortunadamente, en la actualidad, contamos con excelentes herramientasde software libre que cubren este tipo de tareas. E incluso, en algunos casos,existe m´s de una soluci´n de software libre, con lo que podemos evaluar varias a ode ellas y seleccionar la que mejor se adapte a nuestra forma de trabajar antesde empezar a utilizar. En este libro vamos a presentar algunas de estar herramientas. Las hemoselegido porque, a trav´s de nuestra propia experiencia, nos han parecido las m´s e aadecuadas a nuestro caso, pero como lector no debes seguir ciegamente nuestraelecci´n. Mejor a´n, t´mala como punto de partida que le permita evaluar otras o u oalternativas. La r´pida evoluci´n de la tecnolog´ inform´tica permite vaticinar a o ıa aque seguir´n apareciendo cada vez m´s y mejores alternativas. a a En particular vamos a presentar las siguientes herramientas:Subvesion Es una herramienta para la gesti´n de versiones. oJUnit Es un framework de pruebas autom´ticas de c´digo. a oAnt Es una herramienta de construcci´n de proyectos. oMyLyn Es una herramienta de gesti´n de tareas. oBugzilla Es una herramienta de gesti´n y seguimiento de errores. o Cada una de las anteriores herramientas cuentan con una gran popularidaddentro de la comunidad de desarrollo de proyectos inform´ticos. Todas ellas a
  • 21. 22 CAP´ ´ ITULO 1. INTRODUCCIONcuentan con otras excelentes alternativas. Todas ellas se pueden utilizar en pro-yectos que utilicen un lenguaje de programaci´n alternativo a Java, o existen oversiones de ellas para otros lenguajes de programaci´n. o1.4.1. A˜ adiendo nueva funcionalidad a Eclipse: los plug- n insAfortunadamente, desde Eclipse se puede interaccionar con todas las herramien-tas expuestas en la secci´n anterior. o Eclipse cuenta con un sistema de plug-ins de tal modo que podemos aumen-tar sus ya de por s´ numerosas y potentes funcionalidades con otras nuevas. ı As´ por ejemplo, podemos instalar un plug-in de Eclipse para poder realizar ı,el control de versiones de nuestro c´digo sin necesidad de abandonar Eclipse. En oel Cap´ ıtulo 4 se mostrar´ c´mo instalar el plug-in para Eclipse y c´mo trabajar a o ocon ´l. eLecturas recomendadas Un escueto resumen sobre lo que significa el lenguaje de programaci´n o Java se puede encontrar en [11]. Una referencia completa sobre el entorno de desarrollo Eclipse se puede encontrar en la p´gina web http://www.eclipse.org. a
  • 22. Cap´ ıtulo 2Clases en JavaContenidos 2.1. Definici´n de una clase . . . . . . . . . . . . . . . . o 24 2.2. Miembros de una clase . . . . . . . . . . . . . . . . 25 2.2.1. Atributos de una clase . . . . . . . . . . . . . . . . . 25 2.2.2. M´todos de una clase. . . . . . . . . . . . . . . . . . e 26 2.2.3. Constructores. . . . . . . . . . . . . . . . . . . . . . 28 2.2.4. Sobrecarga de m´todos y constructores . . . . . . . . e 32 2.3. Tipos de datos en Java. . . . . . . . . . . . . . . . . 33 2.3.1. Arrays de datos en Java. . . . . . . . . . . . . . . . . 34 2.4. Estructuras de control. . . . . . . . . . . . . . . . . 36 2.4.1. Estructuras de control de repetici´n. . . . . . . . . . o 37 2.4.1.1. El bucle for . . . . . . . . . . . . . . . . . 37 2.4.1.2. El bucle while . . . . . . . . . . . . . . . . 38 2.4.1.3. El bucle do...while . . . . . . . . . . . . . 38 2.4.2. Estructuras de control de selecci´n. . . . . . . . . . . o 39 2.4.2.1. Bifurcaciones con la sentencia if...else. . 39 2.4.2.2. M´ltiples caminos con la sentencia switch u 39 2.5. Modificadores de acceso. . . . . . . . . . . . . . . . 40 2.6. Modificadores static y final. . . . . . . . . . . . . 42 2.7. El recolector de basura. . . . . . . . . . . . . . . . . 43 2.8. Finalizaci´n. . . . . . . . . . . . . . . . . . . . . . . o 44 2.9. Comentarios. Comentarios de documentaci´n. . . o 45Introducci´n oLas clases son la piedra angular de los lenguaje de programaci´n orientados a ob- ojetos (POO). Las clases son abstracciones de entidades de la realidad que sirvencomo plantillas para la creaci´n de ejemplares de la clase. A estos ejemplares en oPOO se les llama objetos o instancias de la clase. El proceso de abstracci´n de- opende del contexto en el que se utilizar´n los ejemplares, es decir, no es lo mismo aabstraer la entidad del mundo real ((Persona)) para utilizarla en una aplicaci´n o 23
  • 23. 24 CAP´ ITULO 2. CLASESde gesti´n de los clientes de una cl´ o ınica, que para utilizarla en una aplicaci´n ode seguros o de banca. En general, las caracter´ısticas de la entidad real que nosinterese utilizar en la aplicaci´n y las operaciones que podamos realizar sobre oestas abstracciones ser´n diferentes. a Java es un lenguaje de programaci´n orientado a objetos, y aunque como overemos posee tipos b´sicos para poder manejar enteros o caracteres, todo en aJava es un objeto. De hecho en Java existen dos grandes tipos de datos: tiposde datos primitivos y tipos de datos referencia. En este cap´ ıtulo vamos a ver c´mo, a partir de una abstracci´n, podemos o otranscribirla a c´digo Java para crear una clase, y c´mo a partir de una clase o opodemos crear instancias de esa clase. Finalmente avanzaremos la idea de reutilizaci´n de una clase. En POO la oreutilizaci´n implica escribir una nueva clase sin partir de cero, sino toman- odo como base otra clase cuyo comportamiento ampliamos o modificamos. Losdetalles de c´mo ampliar o extender el comportamiento de una clase base se ointroducir´n en el Cap´ a ıtulo 3.2.1. Definici´n de una clase oSupongamos que queremos programar una aplicaci´n de agenda telef´nica. El o oobjetivo de nuestra agenda telef´nica es gestionar una serie de contactos. Cada ouno de estos contactos representa a una Persona. Dicho de otro modo cadauno de los contactos de la agenda est´ creado a partir de la misma plantilla aPersona, que es la abstracci´n de una persona del mundo real en el contexto de ola aplicaci´n de la agenda telef´nica. o o ¿Qu´ necesitamos especificar para crear un objeto o ejemplar de la clase ePersona? Cada uno de los objetos creados a partir de esta clase contendr´ unaaserie de valores que lo identifican, como el nombre y los apellidos del contactoy su n´mero de tel´fono. El conjunto de todos los valores de un objeto va a u edeterminar su estado en un momento concreto. Por otro lado, sobre cada uno delos objetos vamos a poder llevar a cabo un conjunto de operaciones definidas enla clase. Volviendo al ejemplo de la agenda telef´nica, cada una de las ((Persona)) ode la agenda va a tener una serie de datos de inter´s, que pueden o no variar ea lo largo del tiempo (un contacto de mi agenda puede cambiar de n´mero de utel´fono, pero no es probable que cambie de apellidos), y me va a ofrecer una eserie de operaciones que puedo realizar sobre ella, como por ejemplo consultarsu nombre. Definici´n oAl conjunto de valores definidos en la clase se le llama atributos de la clase. Alconjunto de operaciones que define una clase se le llama m´todos de la clase. eCuando hablamos de miembros de una clase hacemos referencia tanto a losatributos como a los m´todos de la clase. e La definici´n de una clase en Java empieza con la palabra reservada class, y oel conjunto de atributos y m´todos de la clase se define en un bloque delimitado epor llaves, del siguiente modo
  • 24. 2.2. MIEMBROS DE UNA CLASE 251 c l a s s Persona {2 // D e c l a r a c i ´ n de a t r i b u t o s o3 // D e f i n i c i ´ n de m´ todos o e4 } 2.2. Miembros de una clase 2.2.1. Atributos de una clase Ahora que ya sabemos que debemos abstraer una ((Persona)) del mundo real en el contexto de nuestra aplicaci´n la siguiente pregunta es: ¿Cuales son las o caracter´ısticas, o datos, de una persona relevantes en el contexto de una agenda telef´nica? Sin duda uno de estos datos es el n´mero de tel´fono de la perso- o u e na; cada contacto de mi agenda tiene, de manera simplificada, un n´mero de u tel´fono. ¿Qu´ otros datos pueden ser de inter´s almacenar en una agenda te- e e e lef´nica?, parece evidente que, al menos, el nombre y los apellidos de cada uno o de los contactos. Representemos gr´ficamente lo que tenemos hasta ahora a Persona posee: →Nombre; →Apellidos; →Tel´fono; e ¿C´mo se definen estos atributos en la clase? De cada uno de los atributos o debemos especificar su tipo, por ejemplo, en el caso del Nombre, utilizaremos una cadena de caracteres; en el caso del Telefono podemos optar entre representarlo como un n´mero entero o una cadena de caracteres; si queremos almacenar los u n´meros de tel´fono en formato internacional (Ej: (+34) 555 555 555) optaremos u e por representarlos como cadenas de caracteres. Los atributos los declararemos de este modo:1 c l a s s Persona {2 S t r i n g nombre ;3 String apellidos ;4 String telefono ;5 // D e f i n i c i ´ n de m´ todos o e6 } F´ıjate que, al escribir el nombre de la clase hemos empezado la palabra por una letra may´scula, y que al empezar el nombre de un atributo lo hemos u empezado por min´scula. Esta es una convenci´n de codificaci´n en Java que u o o conviene seguir puesto que est´ ampliamente extendida entre los desarrolladores a Java. Veremos m´s reglas de convenci´n en la Secci´n 2.9. F´ a o o ıjate tambi´n que e hemos definido cada atributo en un l´ ınea distinta y que cada l´ ınea acaba con el caracter ;.
  • 25. 26 CAP´ ITULO 2. CLASES Reglas de convenci´n o Seg´n las reglas de convenci´n m´s extendidas en Java, al definir una clase, u o a el nombre de la clase se debe escribir con la primera letra en may´scula y los u nombres de los atributos y m´todos deben empezar por una letra en min´scula. e u Si estos nombres est´n formados por m´s de una palabra, la segunda y siguien- a a tes palabras que constituyen el nombre se escriben con su primera letra en may´scula. Por ejemplo: numeroTelefono. u Veamos ahora c´mo definir las operaciones que podremos realizar sobre las o instancias de la clase Persona. 2.2.2. M´todos de una clase. e Una vez hemos creado una instancia de la clase Persona, ¿C´mo podemos re- o cuperar a partir de ella su nombre?, ¿C´mo podemos recuperar el nombre que o almacenamos en un contacto de nuestra agenda?. Una posibilidad es simplemente leer el valor del atributo, pero como veremos en la secci´n 2.5 el acceso directo a los atributos de una clase est´ desaconsejado. o a La respuesta es: a trav´s de una llamada a un m´todo que devuelva el nombre e e del contacto. En el caso de la recuperaci´n del nombre, el tipo de dato de o retorno es una cadena class String. Un m´todo que cumple este objetivo es e el siguiente:1 String getPersona () {2 return nombre ;3 } Sintaxis La sintaxis de declaraci´n de un m´todo es: o e {modificadores} tipoRetorno nombre(tipo argumento1, tipo argumento2, ...) { Bloque de definici´n del m´todo; o e } En estas pocas l´ ıneas de c´digo hay varias novedades, ve´moslas: o a 1. Un m´todo tiene un nombre que lo identifica, en este caso getNombre. e 2. Delante del nombre del m´todo escribimos el tipo del valor de retorno, en e nuestro caso, como lo que el m´todo devuelve es el nombre cuyo tipo es e un String, este ser´ el tipo del valor retorno. a 3. Detr´s del nombre del m´todo aparecen unos par´ntesis sin nada en su in- a e e terior. Dentro de los par´ntesis se define la lista de argumentos del m´todo. e e Si est´s familiarizado con las matem´ticas, los argumentos de los m´todos a a e tienen el mismo significado que los argumentos de las funciones matem´ti- a cas, por ejemplo seno(45o ) significa que queremos utilizar el c´lculo del a seno sobre el argumento 45 grados. En nuestro caso la lista est´ vac´ lo a ıa, que indica que no necesito especificar ning´n argumento para poder hacer u uso del m´todo. e
  • 26. 2.2. MIEMBROS DE UNA CLASE 27 4. La definici´n del m´todo va dentro de las llaves . o e 5. Para devolver un valor utilizamos la palabra reservada return. Como ves, muchos conceptos nuevos en tan s´lo tres l´ o ıneas de c´digo. Y una o nueva convenci´n de codificaci´n, si un m´todo devuelve el valor de un atributo o o e empieza por la palabra inglesa get, de ah´ que hayamos escrito getNombre(). ı Con lo que ya hemos visto, es sencillo escribir dos nuevos m´todo que de- e vuelvan los apellidos y el n´mero de tel´fono de una Persona. Aqu´ tienes el u e ı c´digo de la clase: o1 c l a s s Persona {2 S t r i n g nombre ;3 String apellidos ;4 String telefono ;56 String getPersona () {7 return nombre ;8 }910 String getApellidos () {11 return a p e l l i d o s ;12 }1314 String getTelefono () {15 return t e l e f o n o ;16 }17 } Listado 2.1: C´digo de la clase Persona o De nuevo, f´ıjate que si un m´todo no recibe argumentos su lista de argu- e mentos est´ vac´ Pero si un m´todo no devuelve ning´n par´metro, hay que a ıa. e u a indicarlo expl´ ıcitamente utilizando la palabra reservada void. Por ejemplo, el siguiente m´todo no devuelve ning´n valor: e u1 void nada ( ) {2 // C´ digo d e l m´ todo o e3 } En muchas ocasiones resulta interesante poder modificar el valor de los atri- butos. Como ya hemos comentado anteriormente, un contacto de mi agenda podr´ cambiar de n´mero de tel´fono, luego parece buena idea que la clase ıa u e Persona me proporcione un m´todo que permita modificar el valor del atributo e telefono, como el que se muestra en el siguiente ejemplo:1 void s e t T e l e f o n o ( S t r i n g n u e v o T e l e f o n o ) {2 t e l e f o n o = nuevoTelefono ;3 } Listado 2.2: M´todo para modificar el valor del tel´fono. e e De nuevo, hemos seguido una convenci´n de codificaci´n: o o Regla de convenci´n o Los m´todos que modifican el valor de los atributos de una clase se nombran e empezando con la palabra inglesa set seguida por el nombre del atributo, cuya primera letra se escribe en may´sculas. u
  • 27. 28 CAP´ ITULO 2. CLASES De modo an´logo, podemos a˜adir a la clase Persona dos nuevos m´todos a n e para poder modificar el valor de los atributos nombre y apellidos, tal y como se muestra a continuaci´n: o1 void setNombre ( S t r i n g nuevoNombre ) {2 nombre = nuevoNombre ;3 }45 void s e t A p e l l i d o s ( S t r i n g n u e v o s A p e l l i d o s ) {6 apellidos = nuevosApellidos ;7 } Listado 2.3: M´todos para modificar el nombre y los apellidos de una Persona e Ya tenemos escritos m´todos que nos permiten leer y modificar los atributos e de la clase Persona. Ahora queremos crear ejemplares de esta clase, para ello necesitamos escribir m´todos especiales que nos sirvan para crear instancias de e la clase, a estos m´todos especiales se les llama constructores de la clase. e 2.2.3. Constructores. Para crear un ejemplar de una clase utilizamos m´todos especiales llamados e constructores de la clase. En las siguientes l´ ıneas de c´digo se muestra c´mo se o o define un constructor de la clase Persona:1 P e r s o n a ( S t r i n g nombre , S t r i n g a p e l l i d o s , String telefono ) {2 t h i s . nombre = nombre ;3 this . a p e l l i d o s = a p e l l i d o s ;4 this . t e l e f o n o = t e l e f o n o ;5 } Listado 2.4: Constructor con par´metros de la clase Persona a Volvemos a tener nuevos conceptos en estas l´ ıneas de c´digo, ve´moslo: o a 1. Un constructor es un m´todo cuyo nombre coincide con el de la clase, e en nuestro caso el nombre del m´todo es Persona que es precisamente el e nombre de la clase. 2. Como cualquier otro m´todo, tiene un lista de argumentos que en este e caso no est´ vac´ si no que indica que va a recibir tres argumentos y los a ıa, tres de tipo String. 3. F´ıjate que los nombres de los tres argumentos coinciden con los nombres de los atributos; la clase tiene declarado un atributo de tipo String llamado nombre y el primer argumento del constructor tambi´n se llama nombre e y es de tipo String. ¿C´mo resolvemos la ambig¨edad entre el nombre o u del atributo y el nombre del argumento?, utilizando la palabra reservada this; si escribimos this.nombre estamos haciendo referencia al atributo, si s´lo escribimos nombre, estamos haciendo referencia al argumento del o m´todo. Veremos con m´s detalle el significado de la palabra reservada e a this en la secci´n 2.3. o 4. Un constructor no devuelve ning´n valor de retorno, ya que estos m´todos u e especiales nos sirven para crear objetos.
  • 28. 2.2. MIEMBROS DE UNA CLASE 29 Escribamos otro constructor para la clase Persona:1 Persona ( ) { } M´s novedades conceptuales en estas l´ a ıneas de c´digo: o 1. La lista de argumentos de este constructor est´ vac´ a ıa. 2. No hemos escrito ninguna l´ ınea de c´digo entre las llaves. o A este constructor tan particular se le llama Constructor por defecto y habla- remos m´s sobre ´l en el cap´ a e ıtulo 3 dedicado a la herencia en Java. De momento qu´date con la idea de que es importante que tus clases definan el constructor e por defecto, de hecho, todas tus clases deber´ definirlo. Si tu clase no pro- ıan porciona ning´n constructor, como en el caso del Listado 2.5, el compilador de u Java crea el constructor por defecto para la clase, de modo que puedas crear instancias a partir de ella.1 class SinConstructores {2 private i n t a ;34 i n t getA ( ) {5 return a ;6 }7 } Listado 2.5: Una clase sin ning´n constructor. El compilador de Java crear´ el u a constructor por defecto por nosotros. Veamos todo el c´digo que hemos escrito para la clase Persona: o1 package agenda ;23 public c l a s s P e r s o n a {4 S t r i n g nombre ;5 String apellidos ;6 String telefono ;78 Persona ( ) { }910 P e r s o n a ( S t r i n g nombre , S t r i n g a p e l l i d o s , String telefono ) {11 t h i s . nombre = nombre ;12 this . a p e l l i d o s = a p e l l i d o s ;13 this . t e l e f o n o = t e l e f o n o ;14 }1516 S t r i n g getNombre ( ) {17 return nombre ;18 }1920 String getApellidos () {21 return a p e l l i d o s ;22 }2324 String getTelefono () {25 return t e l e f o n o ;26 }27 } Listado 2.6: C´digo de la clase Persona o En el Listado 2.6 hemos agrupado los m´todos get/set para cada uno de e los atributos, adem´s hemos modificado la definici´n de los m´todos set para a o e
  • 29. 30 CAP´ ITULO 2. CLASES deshacer la ambig¨edad entre el nombre de los atributos y de los argumentos, u tal y como hemos hecho en el caso del constructor con argumentos. Antes de pasar adelante, escribamos nuestra primera peque˜a aplicaci´n en n o Java para probar todo lo que hemos visto hasta ahora. Vamos a utilizar para ello el entorno integrado de desarrollo Eclipse, inicia pues esta aplicaci´n. Hay varias o opciones para crear un nuevo proyecto en Eclipse, a trav´s del men´ puedes e u elegir File → New → Java Project, o bien puedes pulsar el bot´n de creaci´n o o de proyectos. Eclipse te solicitar´ un nombre para el proyecto, introduce uno a adecuado (por ejemplo ((AgendaTelefonica))), y ya puedes pulsar directamente la tecla Finish. Ver´s que en la columna izquierda de Eclipse, donde se muestra a la vista Package Explorer te aparece una carpeta con el mismo nombre que el proyecto reci´n creado. Eclipse organiza los proyectos en carpetas, el c´digo de e o tu proyecto, ficheros de bibliotecas y recursos necesarios estar´n en la carpeta a del proyecto. Para crear un nueva clase en Eclipse puedes hacerlo a trav´s del men´ File e u → New → Class, o bien pulsando directamente el bot´n de creaci´n de una o o nueva clase. Se abrir´ una ventana de di´logo solicit´ndote un nombre para a a a la nueva clase y el paquete donde se incluir´. Es muy recomendable que cada a clase est´ dentro de un paquete (veremos con m´s detalle el significado de los e a paquetes en Java en la Secci´n 3.6). Seg´n las convenciones de Java, los nombres o u de paquetes se escriben en min´scula. Escribe, por ejemplo, para el nombre del u paquete agenda, y para el nombre de la clase Persona. Ver´s que se abre la a vista del editor de c´digo en Eclipse y que si despliegas la carpeta de proyecto o te aparece el fichero de clase Persona.java. Escribe la definici´n de la clase o seg´n el Listado 2.6. u Lo siguiente que vamos a hacer es escribir una clase para probar nuestra clase Persona, para ello crea en el proyecto una nueva clase y ll´mala PruebaPersona a y como nombre de paquete introduce agenda, y en el cuadro de di´logo de crea- a ci´n de la clase marca la casilla public static void main(String[] args), o con ello Eclipse crear´ de manera autom´tica el m´todo principal main. Escribe a a e el resto de c´digo que aparece en el Listado 2.7. o1 package agenda ;23 public c l a s s PruebaPersona {45 /∗ ∗6 ∗ @param a r g s7 ∗/8 public s t a t i c void main ( S t r i n g [ ] a r g s ) {9 // TODO Auto−g e n e r a t e d method s t u b10 P e r s o n a unaPersona = new P e r s o n a ( ´ s c a r , B e l m o n t e , 1 2 3 4 ) ; O11 System . o u t . p r i n t l n ( M u e s t r a i n f o r m a c i ´ n a c c e d i e n d o d i r e c t a m e n t e a l o s o campos . ) ;12 System . o u t . p r i n t l n ( N o m b r e : + unaPersona . nombre ) ;13 System . o u t . p r i n t l n ( A p e l l i d o s : + unaPersona . a p e l l i d o s ) ;14 System . o u t . p r i n t l n ( T e l e f o n o : + unaPersona . t e l e f o n o ) ; ´1516 System . o u t . p r i n t l n ( M u e s t r a i n f o r m a c i ´ n l l a m a n d o a l o s m ´ t o d o s d e l a o e clase . ) ;17 System . o u t . p r i n t l n ( N o m b r e : + unaPersona . getNombre ( ) ) ;18 System . o u t . p r i n t l n ( A p e l l i d o s : + unaPersona . g e t A p e l l i d o s ( ) ) ;19 System . o u t . p r i n t l n ( T e l e f o n o : + unaPersona . g e t T e l e f o n o ( ) ) ; ´20 }2122 }
  • 30. 2.2. MIEMBROS DE UNA CLASE 31 Listado 2.7: C´digo de la clase Principal o La clase Principal est´ repleta de novedades. Esta clase tiene un unico m´to- a ´ edo public static void main(String[] args), este m´todo es el punto de eentrada a la ejecuci´n de un programa Java. En las siguientes secciones veremos oel significado de todos los modificadores que tiene este m´todo delante de su enombre que es main. En la l´ ınea n´mero 10, vemos c´mo se usa el operador new u opara crear una instancia de la clase, escribimos tras new un constructor de laclase, en este caso Persona(´scar, Belmonte, 1234),new utilizar´ el O aconstructor con tres argumentos de la clase Persona para crear una nueva ins-tancia. F´ıjate que a la izquierda de new tenemos Persona unaPersona =, estoindica que nos guardamos lo que el operador new devuelve en la variable detipo referencia a Persona que llamamos unaPersona, en las siguientes seccionesveremos con m´s detalle qu´ significa el concepto variable de tipo referencia, de a emomento la idea es que, para poder usar la instancia a la Persona reci´n creada eutilizaremos la variable de referencia unaPersona. Reglas de convenci´n oLos nombre de los paquetes y subpaquetes se escriben en min´sculas. u En las l´ ıneas 12-14 recuperamos la informaci´n a partir de la variable de otipo referencia a Persona accediendo directamente a sus atributos (nombre,apellidos, telefono); mientras que en las l´ ıneas 17-19 accedemos a la mismainformaci´n haciendo uso de los m´todos definidos en la clase (getNombre(), o egetApellidos(), getTelefono()). Finalmente, para mostrar informaci´n en forma de texto por consola utili- ozamos System.out.println(Texto). Ejecutemos este primer programa para ver cual es el resultado, para ellohaz click con el bot´n derecho sobre el nombre de la clase Principal.java oque tienes en la columna de la derecha en Eclipse (Package Explorer ) y enel men´ emergente que te aparecer´ selecciona Run as → Java Application; u aen la parte inferior de Eclipse se abrir´ una nueva solapa (Console) donde se amostrar´ el resultado de la ejecuci´n que debe ser: a oMuestra informaci´n accediendo directamente a los campos. oNombre: ´scar OApellidos:BelmonteTel´fono: 1234 eMuestra informaci´n llamando a los m´todos de la clase. o eNombre: ´scar OApellidos:BelmonteTel´fono: 1234 e Como has comprobado, el trabajo de edici´n en Eclipse es realmente sencillo oy te ir´s dando cuenta que este entorno de programaci´n Java es muy potente. a o
  • 31. 32 CAP´ ITULO 2. CLASES Pregunta En el ejemplo anterior estamos recuperando la informaci´n almacenada en una o instancia de la clase Persona de dos modos: accediendo directamente a sus atributos, o llamando a los m´todos de la clase. ¿Qu´ sentido tiene declarar e e m´todos de acceso a los atributos de una clase si puedo acceder directamente a e ellos?. 2.2.4. Sobrecarga de m´todos y constructores e Dos o m´s m´todos pueden tener el mismo nombre siempre que su n´mero a e u de argumentos sea distinto. En caso de que los dos m´todos tengan el mismo e n´mero de argumentos, ser´n distintos si al menos un tipo de sus argumentos u a es distinto. Por ejemplo en el siguiente Listado los dos m´todos unMetodo est´n e a sobrecargados y son distintos.1 p u b l i c v o i d unMetodo ( i n t e n t e r o ) {2 // D e f i n i c i ´ n d e l m´ todo o e3 }45 p u b l i c v o i d unMetodo ( f l o a t r e a l ) {6 // D e f i n i c i ´ n d e l m´ todo o e7 } De modo an´logo, los constructores tambi´n pueden estar sobrecargados, a e de hecho hemos sobrecargado el constructor de la clase Persona en el Listado 2.6, esta clase tiene dos constructores Persona() y Persona(String nombre, String apellidos, String telefono). Un detalle muy importante en la sobrecarga de m´todos es que el tipo de e retorno no sirve para distinguir dos m´todos. Si dos m´todos tienen el mismo e e n´mero de argumentos y sus tipos son los mismos, no los podremos sobrecargar u haciendo que el tipo de sus valores de retorno sean distintos. Definici´n o El nombre de un m´todo junto con su lista de argumentos forman la signatura e del m´todo. El tipo del valor de retorno no forma parte de la signatura de un e m´todo. e En el siguiente Listado se muestra un error al intentar sobrecargar dos m´to- e dos que se distinguen unicamente por su tipo de retorno. ´1 // ESTE LISTADO CONTIENE UN ERROR.2 ´ // LOS METODOS NO SE PUEDEN SOBRECARGAR POR EL TIPO DE RETORNO.3 p u b l i c v o i d unMetodo ( ) {4 // D e f i n i c i ´ n d e l m´ todo o e5 }67 p u b l i c i n t unMetodo ( ) {8 // D e f i n i c i ´ n d e l m´ todo o e9 } Los dos m´todos tienen el mismo nombre y ning´n argumento, el primero de e u ellos no retorna nada void, y el segundo de ellos retorna un int. El compilador es
  • 32. 2.3. TIPOS DE DATOS EN JAVA. 33 Tipo Tama˜ o(bits) n Definici´no boolean 1 true o false char 16 Car´cter Unicode a byte 8 Entero en complemento a dos con signo short 16 Entero en complemento a dos con signo int 32 Entero en complemento a dos con signo long 64 Entero en complemento a dos con signo float 32 Real en punto flotante seg´n la norma IEEE 754 u double 64 Real en punto flotante seg´n la norma IEEE 754 u Tabla 2.1: Tipos de datos primitivos en Java y sus tama˜os en memoria. nincapaz de distinguirlos y devuelve un error que indica que estamos intentandodefinir el mismo m´todo dos veces. e Pero volvamos a Java y vemos qu´ significa el t´rmino tipo de dato referencia. e e2.3. Tipos de datos en Java.En Java existen dos grandes grupos de tipos de datos, los tipos de datos primi-tivos y los tipos de datos referencia. Los tipos de datos primitivos sirven para representar tipos de datos talescomo n´meros enteros, caracteres, n´meros reales, booleanos, etc´tera. Se les u u ellama primitivos porque nos permiten manejar elementos de informaci´n b´sicos o acomo letras y n´meros. Una variable de tipo primitivo nos permite almacenar uen ella un tipo primitivo como por ejemplo un valor num´rico. e Por otro lado, los tipos de datos referencia nos permiten indicar que vamos atrabajar con instancias de clases, no con tipos primitivos. Una variable de tiporeferencia establece una conexi´n hacia un objeto, y a trav´s de esta conexi´n o e opodremos acceder a sus atributos y m´todos. e Cuando hablamos de variables, es muy importante asimilar la diferencia en-tre variables de tipo primitivo y variables de tipo referencia. En una variablede tipo primitivo podemos almacenar valores de tipo primitivo (n´meros, ca- uracteres); pero el las variables de tipo referencia no almacenamos valores sonla puerta de entrada hacia los objetos. Son los objetos, las instancias de clases,las que almacenan informaci´n y me permiten trabajar con ellos a trav´s de o ellamadas a sus m´todos. e ConceptoLas variables de tipo primitivo nos permiten almacenar valores de tipo primi-tivo como n´meros y caracteres. Las variables de tipo referencia no almacenan uvalores, sino que nos permiten acceder a los atributos y m´todos de los objetos. e En Java, el tama˜o en memoria de los tipos de datos primitivos est´ estan- n adarizado. Los tipos de datos primitivos y sus tama˜os son los que aparecen en nla Tabla 2.1.
  • 33. 34 CAP´ ITULO 2. CLASES SintaxisLas variables de tipo primitivo se declaran de este modo:tipo nombre [ = valor inicial];Ejemplo 1 : int hojas;Ejemplo 2 : float pi = 3.14f; //f´jate en la f al final del n´mero ı u Como ya hemos visto, las referencias en Java son la puerta de entrada a losobjetos, las referencias me permiten acceder a los atributos y m´todos de los eobjetos, el tipo de una referencia debe ser compatible con el tipo del objeto alque se refiere. En el cap´ıtulo 3 dedicado a la herencia veremos qu´ quiere decir e((compatible)). SintaxisLas variables de tipo referencia se declaran de este modo:tipoReferencia nombre [ = valor referencia inicial];Ejemplo 1 : Persona persona;Ejemplo 2 : Persona persona = new Persona(´scar, P´rez, 123); O e En m´ltiples ocasiones, nos interesa trabajar con m´s de un unico valor u a ´de un determinado tipo, en vez de trabajar con una unica Persona queremos ´trabajar con un grupo de personas. Veamos c´mo podemos declarar conjuntos ode elementos del mismo tipo en Java.2.3.1. Arrays de datos en Java.Hasta el momento, hemos aprendido c´mo declarar variables de tipos de datos oprimitivos y de tipos de datos referencia. Esto nos sirve para crear una unica ´variable que contendr´ bien un tipo de datos primitivo a una referencia a un aobjeto, pero a veces nos interesa poder manejar conjuntos de elementos delmismo tipo, por ejemplo, en alguna circunstancia nos puede interesar declararuna variable con la que poder acceder a un grupo de 10 enteros o 100 objetosde la clase Persona. En Java utilizaremos arrays de elementos cuando necesitemos manejar m´s ade un elemento del mismo tipo. Para declarar un array en Java utilizamos loscorchetes seg´n la siguiente sintaxis: u SintaxisLos arrays de tipos primitivos se declaran:Ejemplo 1 : int array[]; // Array declaradoEjemplo 2 : int arrayEnteros[] = new int[10]; // Array iniciadoLos arrays de tipos referencia se declaran:Ejemplo 3 : Persona grupo[]; // Array declaradoEjemplo 4 : Persona grupo = new Persona[10]; // Array iniciado Aunque la sintaxis de la declaraci´n de arrays de tipo primitivo y de tipo oreferencia es la misma, el resultado es radicalmente distinto en los dos casos.
  • 34. 2.3. TIPOS DE DATOS EN JAVA. 35 Analic´moslo. En el Ejemplo 2 del recuadro de sintaxis anterior se est´ definien- e a do un array capaz de albergar 10 enteros (con ´ ındice 0 para el primer elemento e ´ ındice 9 para el ultimo), dentro de cada una de las posiciones del array podemos ´ almacenar un entero. En el caso del Ejemplo 4, estamos definiendo un array capaz de albergar 10 ((referencias)) de tipo Persona. En este caso, lo que tenemos en cada una de las posiciones del array no es un objeto de tipo Persona, si no una referencia a un objeto de tipo Persona. Dicho de otro modo No se ha creado ning´ n objeto u de la clase Persona, s´lo referencias a objetos de ese tipo. o La diferencia entre arrays de tipo primitivo y tipo referencia es muy impor- tante. Mientras que en el caso de los arrays de tipo primitivo, una vez creados ya tenemos disponible en cada una de sus posiciones espacio para albergar un elemento del tipo correspondiente, en los arrays de tipo referencia no se ha creado ninguna instancia de la clase correspondiente, lo unico que se ha crea- ´ do es un conjunto de referencias que podremos conectar a objetos de la clase correspondiente, y estos objetos los habremos creado en otro lugar de nuestro programa. Veamos esta diferencia con el siguiente ejemplo1 package agenda ;23 public c l a s s A r r a y s {45 /∗ ∗6 ∗ @param a r g s7 ∗/8 public s t a t i c void main ( S t r i n g [ ] a r g s ) {9 // TODO Auto−g e n e r a t e d method s t u b10 i n t a r r a y E n t e r o s [ ] = new i n t [ 1 0 ] ;11 P e r s o n a g r u p o P e r s o n a s [ ] = new P e r s o n a [ 1 0 ] ;12 // La s i g u i e n t e s e n t e n c i a e s v a l i d a13 System . o u t . p r i n t l n ( V a l o r e n a r r a y E n t e r o s [ 5 ] : + a r r a y E n t e r o s [ 5 ] ) ;14 // Se p r o d u c e un e r r o r , no hay nada en l a p o s i c i ´ n [ 5 ] o15 System . o u t . p r i n t l n ( N o m b r e e n p o s i c i ´ n g r u p o P e r s o n a s [ 5 ] : + o g r u p o P e r s o n a s [ 5 ] . nombre ) ;1617 }1819 } Listado 2.8: Diferencia entre arrays de tipos primitivos y arrays de tipos referencia Si creas una nueva clase con el c´digo del Listado 2.8 y lo ejecutas (recuerda: o bot´n derecho sobre el nombre de la clase en el Package Explorer, y luego Run o as → Java Applications), obtendr´s el siguiente error: a Valor en arrayEnteros[5]: 0 Exception in thread main java.lang.NullPointerException at hola.Arrays.main(Arrays.java:15) En la posici´n 5 del array de enteros tenemos un valor por defecto, pero la o referencia que tenemos en la posici´n 5 del array de tipo Persona es el valor o por defecto null que en Java tiene el significado de Referencia no asignada.
  • 35. 36 CAP´ ITULO 2. CLASES Sintaxis A los elementos de un array se accede mediante el operador []. Dentro de este operador indicamos la posici´n del elemento a la que deseamos acceder. o ¿C´mo podemos resolver el error anterior?. Simplemente asignando a la re- o ferencia en la posici´n 5 del array grupoPersonas una referencia a un objeto o que haya sido creado:1 package agenda ;23 public c l a s s A r r a y s 2 {45 /∗ ∗6 ∗ @param a r g s7 ∗/8 public s t a t i c void main ( S t r i n g [ ] a r g s ) {9 // TODO Auto−g e n e r a t e d method s t u b10 i n t a r r a y E n t e r o s [ ] = new i n t [ 1 0 ] ;11 P e r s o n a g r u p o P e r s o n a s [ ] = new P e r s o n a [ 1 0 ] ;12 g r u p o P e r s o n a s [ 5 ] = new P e r s o n a ( J a m e s , G o s s l i n g , 5 5 5 1 2 3 4 5 6 ) ;13 // La s i g u i e n t e s e n t e n c i a e s v a l i d a14 System . o u t . p r i n t l n ( V a l o r e n a r r a y E n t e r o s [ 5 ] : + a r r a y E n t e r o s [ 5 ] ) ;15 // Se p r o d u c e un e r r o r , no hay nada en l a p o s i c i ´ n [ 5 ] o16 System . o u t . p r i n t l n ( N o m b r e e n p o s i c i ´ n g r u p o P e r s o n a s [ 5 ] : + o g r u p o P e r s o n a s [ 5 ] . nombre ) ;1718 }1920 } Si ejecutas el c´digo con la modificaci´n obtendr´s el siguiente resultado: o o a Valor en arrayEnteros[5]: 0 Nombre en posici´n grupoPersonas[5]: James o En este ultimo caso la referencia en la posici´n 5 del array grupoPersonas ´ o s´ que hace referencia a un objeto, luego no hay problema al usarla para acceder ı a su atributo nombre. Ya sabemos c´mo acceder a los elementos de un array, la pregunta que nos o surge es ¿C´mo puedo recorrer todos los elementos de un array?. La respuesta o es: ((Usando estructuras de control de repetici´n)) o 2.4. Estructuras de control. Java es un lenguaje de programaci´n estructurado, esto significa que Java pro- o porciona estructuras de control para decidir el flujo de ejecuci´n de nuestros o programas. Existen dos grandes grupos de estructuras de control: Estructuras de control de repetici´n: Nos permiten indicar si un determi- o nado bloque de c´digo se debe ejecutar mas de una vez. o Estructuras de control de selecci´n: Nos permiten especificar mas de una o direcci´n de flujo dependiendo de alguna condici´n. o o
  • 36. 2.4. ESTRUCTURAS DE CONTROL. 37 2.4.1. Estructuras de control de repetici´n. o En Java existen tres estructuras de control de repetici´n: o Bucle for. Bucle while. Bucle do...while. Las estructuras de repetici´n sirven para repetir una determinada tarea o mientras se cumpla cierta condici´n. En el caso de un array nos sirven para o recorrer los elementos almacenados en el array secuencialmente, para, por ejem- plo, mostrar sus valores. Veamos como se usa cada una de estas estructuras de repetici´n. o 2.4.1.1. El bucle for Si conocemos cual es el primer elementos y el ultimo sobre los que queremos ´ iterar el bucle for es la manera m´s c´moda de recorrerlos todos. Su sintaxis a o es la siguiente: Sintaxis La sintaxis del bucle for es: for(inicio; condici´n: incremento) o Ejemplo 1: for(int i = 0; i 10; i += 2) La variable ((i)) se declara en el bucle y s´lo tiene existencia dentro del bucle, al o salir del bucle desaparece la variable de control ((i)). Para el bucle for...each: for(Tipo variable: Colecci´n) o Ejemplo 2 : int arrayEnteros [] = new int[10]; for(int i: arrayEnteros) En el primer ejemplo del recuadro de sintaxis se utiliza una variable de control que se inicia a 0, la condici´n de parada es que el valor de la variable o sea menor que 10 y el incremento en cada paso del bucle es 2, luego la variable toma los valores 0, 2, 4, 6 y 8. En el segundo ejemplo se utiliza el bucle for...each introducido en la ver- si´n 5 de Java. En este caso utilizamos una variable que va recibiendo los valores o de los elementos que hay dentro del conjunto de manera incremental, uno con cada iteraci´n. El bucle for...each es especialmente util cuando se itera sobre o ´ los elementos de una colecci´n, tal y como veremos en el Cap´ o ıtulo 8. Veamos un ejemplo con un poco m´s de detalle: a1 package r e p e t i c i o n ;23 public c l a s s B u c l e F o r {45 public s t a t i c void main ( S t r i n g [ ] a r g s ) {6 // Declaramos e l a r r a y7 i n t a r r a y E n t e r o s [ ] = new i n t [ 5 ] ;8 // Almacenamos d a t o s en s u s e l e m e n t o s9 f o r ( i n t i = 0 ; i 5 ; i ++)
  • 37. 38 CAP´ ITULO 2. CLASES10 arrayEnteros [ i ] = i ;11 // Lo r e c o r r e m o s y e x t r a e m o s l a i n f o r m a c i ´ n almacenada o12 for ( int i : arrayEnteros )13 System . o u t . p r i n t l n ( a r r a y E n t e r o s [ + i + ] = + a r r a y E n t e r o s [ i ] ) ;14 }1516 } El resultado de la ejecuci´n de este c´digo es el siguiente: o o arrayEnteros[0] = 0 arrayEnteros[1] = 1 arrayEnteros[2] = 2 arrayEnteros[3] = 3 arrayEnteros[4] = 4 El primer bubcle for itera sobre las posiciones del array almacenando los n´meros 0 a 4 en las posiciones 0 a 4 del array. El segundo bucle itera sobre las u posiciones del array y muestra el valor almacenado en cada una de ellas. 2.4.1.2. El bucle while En el caso del bucle while, la condici´n de parada se comprueba antes de cada o iteraci´n y, si la condici´n se cumple, se ejecuta el bloque del bucle while. o o Sintaxis La sintaxis del bucle while es: while(condici´n) { o Bloque de c´digo o } 2.4.1.3. El bucle do...while En el caso del bucle do...while la condici´n se comprueba despu´s de haberse o e ejecutado al menos una vez el cuerpo del bucle. La condici´n se comprueba al o final del bucle. Sintaxis La sintaxis del bucle do...while es: do { Bloque de c´digo o } while(condici´n); o Estas tres estructuras de control de repetici´n son intercambiables, se puede o sustituir una por otra con peque˜as modificaciones. Elegir una u otra depende n de cada caso: si conocemos el intervalo sobre el que queremos iterar el bucle for es el mas comodo de utilizar; si la condici´n de parada no involucra el valor o de una posicion podemos utilizar el bucle while si necesitamos comprobar la condici´n antes, o bien el bucle do...while si queremos ejecutar al menos una o vez el bloque de c´digo que encierra el bucle. o
  • 38. 2.4. ESTRUCTURAS DE CONTROL. 39 2.4.2. Estructuras de control de selecci´n. o Las estructuras de control de selecci´n nos permiten especificar mas de un posi- o ble camino a seguir por el flujo de ejecuci´n de nuestro programa. La direcci´n o o final que seguir´ el flujo depender´ de si se cumple o no cierta condici´n. a a o 2.4.2.1. Bifurcaciones con la sentencia if...else. La sentencia if nos permite ejecutar un bloque de c´digo, o no, dependiendo o de cierta condici´n. La condici´n debe evaluarse a un valor booleano, es decir, o o a true o false, como en el siguiente ejemplo de c´digo: o1 int entero ;2 i f ( e n t e r o % 2 == 0 ) System . o u t . p r i n t l n ( E l n ´ m e r o e s p a r . ) ; u3 e l s e System . o u t . p r i n t l n ( E l n ´ m e r o e s i m p a r . ) ; u Dentro del bloque de c´digo correspondiente al else podemos a˜adir una o n nueva sentencia if, se dice entonces que las sentencias if est´n encadenadas, a como en el siguiente ejemplo:1 i f ( primeraCondicion ) {2 Bloque de c o d i g o ´3 } e l s e i f ( segundaCondicion ) {4 Bloque de c o d i g o ´5 } else {6 Bloque de c o d i g o ´7 } Esto nos permite especificar m´s de un posible camino a seguir por el flujo a de ejecuci´n de nuestro c´digo. o o 2.4.2.2. M´ ltiples caminos con la sentencia switch u Existe una construcci´n del lenguaje que nos permite especificar m´ltiples cami- o u nos a seguir por el flujo de ejecuci´n de nuestro c´digo: la sentencia switch. En o o este caso el camino a seguir se selecciona bas´ndose en el valor de una expresi´n a o que se evalua a un valor entero, como en el siguiente ejemplo:1 i n t mes = 1 ; // C o r r e s p o n d e a l mes de Enero2 s w i t h ( mes ) {3 case 1 :4 System . o u t . p r i n t l n ( E l m e s e s E n e r o . ) ;5 break ;6 case 2 :7 System . o u t . p r i n t l n ( E l m e s e s F e b r e r o . ) ;8 break ;9 case 3 :10 System . o u t . p r i n t l n ( E l m e s e s M a r z o . ) ;11 break ;12 default :13 System . o u t . p r i n t l n ( N i n g u n o d e l o s m e s e s a n t e r i o r e s . ) ;14 break ;15 } En el ejemplo anterior, se evalua el valor de la variable mes, y se prueba cada una de las opciones expresadas por un case. Cuando coinciden los valores, se ejecuta el c´digo correspondiente al case hasta que se encuentra la sentencia o break en cuyo momento se avandona el bloque de la sentencia switch. Exite una
  • 39. 40 CAP´ ITULO 2. CLASES opci´n Por defecto etiquetada como default que es opcional y cuyo c´digo se o o ejecutar´ si la expresi´n entera no coincide con ninguno de los case anteriores. a o Es importante hacer notar que una vez que se encuentra una coincidencia entre la expresi´n entera y un case, se ejecuta su c´digo correspondiente hasta o o encontrar la sentencia break. Esto nos permite obviar esta sentencia si queremos que varios case distintos ejecuten el mismo segmento de c´digo, como en el o siguiente ejemplo:1 i n t mes = 1 ; // C o r r e s p o n d e a l mes de Enero2 s w i t c h ( mes ) {3 case 1:4 case 3:5 case 5:6 case 7:7 case 8:8 case 10:9 case 12:10 System . o u t . p r i n t l n ( ” El mes t i e n e 31 d´a s . ” ) ; ı11 break ;12 case 4:13 case 6:14 case 9:15 case 11:16 System . o u t . p r i n t l n ( ” El mes t i e n e 30 d´a s . ” ) ; ı17 break :18 default :19 System . o u t . p r i n t l n ( ” El mes es Febrero . ” ) ;20 break ;21 } En el ejemplo anterior los meses cuyo ordinal es 1, 3, 5, 7, 8, 10 o 12 tienen 31 d´ todos los case correspondientes, excepto el de valor 12, no incluye la ıas, sentencia break por lo que en todos los casos, al seleccionar uno de ellos se ejecutar la sentencia de la l´ ınea 10. Lo mismo ocurrir´ si el ordinal del mes es a 4, 6, 9 u 11, en todos los casos se ejecutar´ la sentencia de la l´ a ınea 16. 2.5. Modificadores de acceso. Ahora ya estamos en situaci´n de volver a la pregunta: ¿Qu´ sentido tiene decla- o e rar m´todos de acceso a los atributos de una clase si puedo acceder directamente e a ellos? La repuesta es que, como regla general, nunca debemos hacer visibles los atributos de nuestras clases, s´lo deben ser visibles desde el interior de las o clases. Como resultado, para acceder a los valores de los atributos utilizaremos m´todos. Esta regla es una manera de expresar el concepto de Encapsulaci´n, e o una de las piezas centrales de la programaci´n orientada a objetos. o Concepto Las clases encapsulan atributos y m´todos de tal modo que s´lo se hace visible e o una parte de esos atributos y m´todos, los estrictamente necesarios para que e podamos trabajar con las instancias de esa clase. La respuesta a la pregunta anterior hace surgir una nueva: ¿C´mo restrin- o jo la visibilidad de los atributos de una clase?, la respuesta es: mediante los Modificadores de acceso.
  • 40. 2.5. MODIFICADORES DE ACCESO. 41 Definici´n o Los modificadores de acceso son palabras reservadas de Java mediante las cuales restringimos la visibilidad de los atributos y m´todos de una clase. e En Java un modificador de acceso est´ representado por una palabra reser- a vada que me permite definir la visibilidad de un atributo o un m´todo de la e clase. Los cuatro modificadores de acceso que podemos utilizar en Java son: private. protected. ((Vac´ (no escribimos nada). ıo)) public. De momento, y hasta que veamos el cap´ ıtulo de herencia, vamos a ver el significado de los dos m´s sencillos: private y public. Los modificadores de a acceso se escriben antes del tipo del atributo o antes del tipo de retorno del m´todo. Veamos c´mo quedar´ nuestra clase Persona asignando la visibilidad e o ıa adecuada a cada uno miembros:1 package agenda ;23 public c l a s s P e r s o n a {4 S t r i n g nombre ;5 String apellidos ;6 String telefono ;78 Persona ( ) { }910 P e r s o n a ( S t r i n g nombre , S t r i n g a p e l l i d o s , String telefono ) {11 t h i s . nombre = nombre ;12 this . a p e l l i d o s = a p e l l i d o s ;13 this . t e l e f o n o = t e l e f o n o ;14 }1516 S t r i n g getNombre ( ) {17 return nombre ;18 }1920 String getApellidos () {21 return a p e l l i d o s ;22 }2324 String getTelefono () {25 return t e l e f o n o ;26 }27 } En este caso estamos restringiendo la visibilidad de los atributos de la clase Persona de modo que unicamente son visibles desde el interior de la propia clase ´ donde se han definido (modificador private). Por otro lado, estamos haciendo visibles los m´todos de la clase a cualquier otra clase que los quiera utilizar e (modificador public).
  • 41. 42 CAP´ ITULO 2. CLASES Buenas pr´cticas y convenciones a En general, se considera una buena pr´ctica declarar los atributos de una clase a como privados (private) y si necesitamos acceder a ellos para leer sus valores o modificarlos utilizaremos los m´todos get o set. En caso de que el tipo del valor e devuelto sea boolean se utilizar´ is en vez de set, por ejemplo isNuevo() en a vez de getNuevo() si el valor que se retorna es un boolean (true o false). Adem´s de los modificadores que nos permiten definir la visibilidad de atri- a butos y m´todos de una clase, en Java existen otros modificadores que tambi´n e e se pueden aplicar sobre la definici´n de atributos y m´todos: static y final. o e 2.6. Modificadores static y final. Un atributo de una clase se puede modificar con la palabra reservada static, con ello indicamos que el atributo no pertenece a las instancias de la clase si no a la propia clase. ¿Qu´ quiere decir esto?, pues que no existe una copia de ese e atributo en cada uno de los objetos de la clase, si no que existe una unica copia ´ que es compartida por todos los objetos de la clase. Por ello, a los atributos static se les llama atributos de la clase. Una consecuencia de lo anterior es que para acceder a los atributos static de una clase no necesitamos crear una instancia de la clase, podemos acceder a ellos a trav´s del nombre de la clase. e De igual modo, podemos modificar los m´todos de una clase con la palabra e reserva static. A estos m´todos se les llama m´todos de la clase, y, al igual que e e con los atributos static, podemos usarlos a trav´s del nombre de la clase, sin e necesidad de crear ninguna instancia de la clase. Pero existe una restricci´n, los o m´todos est´ticos de una clase s´lo pueden acceder a atributos est´ticos u otros e a o a m´todos est´ticos de la clase, pero nunca a atributos o m´todos que no lo sean. e a e ¿Ves porqu´? ¿Qu´ ocurrir´ si desde un m´todo est´tico y usando el nombre e e ıa e a de la clase intentases acceder a un atributo de instancia de la clase? En el siguiente ejemplo de c´digo hemos a˜adido un contador para saber el o n n´mero de instancias de la clase Persona que se han creado: u1 package t i p o s ;23 public c l a s s P e r s o n a implements C o n t a c t o {4 private S t r i n g nombre ;5 private S t r i n g a p e l l i d o s ;6 private S t r i n g t e l e f o n o ;7 private s t a t i c i n t n I n s t a n c i a s ;89 public P e r s o n a ( ) {10 super ( ) ;11 iniciaAtributos () ;12 }1314 public s t a t i c i n t g e t N I n s t a n c i a s ( ) {15 return n I n s t a n c i a s ;16 } F´ ıjate que el m´todo getNInstancias() que accede al atributo nInstancias e es est´tico. En el siguiente ejemplo de c´digo se est´ utilizando este m´todo a o a e est´tico a trav´s del nombre de la clase y a trav´s de una instancia concreta: a e e
  • 42. 2.7. EL RECOLECTOR DE BASURA. 431 public f i n a l c l a s s P r i n c i p a l {2 private P r i n c i p a l ( ) {3 super ( ) ;4 }56 private void e j e c u t a ( ) {7 P e r s o n a unaPersona = new P e r s o n a ( ) ;8 // Accedemos a l m´ todo a t r a v ´ s de l a c l a s e e e9 System . o u t . p r i n t l n ( N u m e r o d e p e r s o n a s c r e a d a s : + P e r s o n a . ´ getNInstancias () ) ;10 P e r s o n a o t r a P e r s o n a = new P e r s o n a ( J a m e s , G o s s l i n g , 5 5 5 1 2 3 4 5 6 ) ;11 // Accedemos a l m´ todo a t r a v ´ s de una i n s t a n c i a c o n c r e t a e e Cuando un atributo de una clase los modificamos en su definici´n con la o palabra reservada final, estamos indicando que ese atributo no puede cambiar de valor, por ejemplo:1 private f i n a l S t r i n g a u t o r = ´ s c a r ; O Una vez definido, este atributo no puede cambiar de valor, si lo intent´semos a cambiar el compilador nos dar´ un error. ıa Muchas veces los modificadores static y final se utilizan en combinaci´n o para definir constantes, como en el siguiente ejemplo:1 public c l a s s C o n s t a n t e s {2 public s t a t i c f i n a l double PI = 3 . 1 4 1 5 9 2 ;3 ...4 } De este modo, la constante es accesible desde cualquier otra clase (al ser public) y podemos leerla a trav´s del nombre de la clase de este modo e Constantes.PI, pero si por descuido intentamos modificarla, el compilador de Java nos dar´ un error. a Regla de convenci´n o Los nombre de las constantes se escriben en may´sculas. u El modificador final tambi´n se puede usar sobre un m´todo o sobre la e e clase. Veremos con detalle lo que esto significa en el Cap´ ıtulo 3 dedicado a la herencia en Java. 2.7. El recolector de basura. Hemos visto que para crear instancias de una clase utilizamos el operador new. Cuando ya no necesitamos una instancia: ¿C´mo liberamos el espacio en memo- o ria que est´ ocupando? En Java no existe ning´n operador especial para eliminar a u de la memoria las instancias que no vamos a seguir utilizando. Para liberar la memoria existe un mecanismo mucho m´s potente, el Recolector de basura. a Como ya sabes, el modo de acceder a los objetos en Java es mediante las va- riables de tipo referencia. El recolector de basura conoce en todo momento todas las referencia que una instancia posee, y de igual modo conoce cuando una ins- tancia ha perdido todas las referencias que apuntaban a ella. Si un objeto pierde
  • 43. 44 CAP´ ITULO 2. CLASES todas la referencias que apuntan a ´l y las referencias son el unico mecanismo e ´ que tenemos de acceder a los objetos, significa que ya no podremos acceder a ese objeto, de modo que el recolector de basura puede hacer su trabajo: liberar la memoria ocupada por la instancia. ¿C´mo podemos marcar un objeto para que sea borrado de memoria? Una o t´cnica sencilla es eliminar todas las referencias que apuntan a ´l como en el e e siguiente ejemplo:1 P e r s o n a u n a R e f e r e n c i a = new P e r s o n a ( ) ; // E st a P e r s o n a t i e n e una r e f e r e n c i a hacia e l l a2 P e r s o n a o t r a R e f e r e n c i a = u n a R e f e r e n c i a ; // Ahora t i e n e dos3 u n a R e f e r e n c i a = n u l l ; // Le d e s c o n e c t a m o s l a p r i m e r a r e f e r e n c i a4 o t r a R e f e r e n c i a = n u l l ; // Le d e s c o n e c t a m o s l a segunda r e f e r e n c i a5 // El r e c o l e c t o r de b a s u r a ya puede h a c e r su t r a b a j o 2.8. Finalizaci´n. o En la secci´n anterior hemos visto cual es el mecanismo que utiliza Java para o ir liberando de la memoria los objetos que ya no son accesibles. Todos los obje- tos poseen un m´todo con la siguiente signatura protected void finalize() e throws Throwable, en los cap´ ıtulos siguientes veremos con detalle el significado de las palabras reservadas protected y throws, as´ como la clase Throwable, ı lo que nos interesa en este momento es saber que este es el ultimo m´todo de ´ e cualquier objeto que se llama antes de que el recolector de basura elimine la instancia de la memoria. Dicho de otro modo, el m´todo finalize es la ulti- e ´ ma oportunidad que tenemos como programadores para que nuestras instancias acaben limpiamente. Veamos qu´ quiere decir esto con m´s detalle. e a Sup´n que has escrito un clase que abre un fichero para lectura (veremos o acceso a ficheros en el cap´ ıtulo 7), y que por cualquier motivo una instancia de esta clase pierde todas las referencias que apuntan a ella. Cuando actuase el recolector de basura, eliminar´ esta instancia de la memoria y el resultado ıa colateral ser´ que el fichero quedar´ abierto. Para que esto no ocurra, en el ıa ıa m´todo finalize podemos escribir el c´digo que cierre los ficheros que est´n e o a abiertos, ya que sabemos que este m´todo ser´ llamado antes de eliminar la e a instancia de memoria. Pero no es tan inmediato, el problema que conlleva delegar al m´todo e finalize estas tareas de limpieza segura antes de acabar es que no sabemos cuando el recolector de basura va a hacer su trabajo, sabemos que lo har´, pero a no sabemos cuando. Y aunque podemos ((forzar)) la actuaci´n del recolector de o basura de este modo:1 Runtime r = Runtime . getRuntime ( ) ;2 r . gc ( ) ; // S o l i c i t a m o s que e l r e c o l e c t o r de b a s u r a e n t r e en a c c i o n . ´ no se garantiza que el recolector de basura vaya a ser invocado inmediatamente. Luego, como norma general:
  • 44. ´ 2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACION. 45 Buenas pr´cticas a No debemos delegar en el recolector de basura la limpieza que han de realizar nuestras clases cuando sus instancias son eliminadas de memoria. 2.9. Comentarios. Comentarios de documenta- ci´n. o Todos los programadores son conscientes de la importancia de documentar su trabajo. Una tarea de documentaci´n es incluir comentarios en el propio c´digo o o para que otros programadores puedan conocer en el momento de la lectura de c´digo los detalles de implementaci´n. o o Para realizar tareas de documentaci´n Java nos proporciona tres tipos de o comentarios: 1. Comentarios de una unica l´ ´ ınea. 2. Comentarios de m´s de una l´ a ınea. 3. Comentarios de documentaci´n. o Los comentarios de una unica l´ ´ ınea empiezan con y el texto del comentario restringe su extensi´n a una unica l´ o ´ ınea. Los comentarios de m´s de una l´ a ınea empiezan con /*, el texto del comen- tario puede ocupar cuantas l´ ıneas necesitamos, pero es necesario indicar que el comentario acaba insertando al final */. En el siguiente ejemplo puedes ver c´mo se usan ambos tipos de comentario: o1 public c l a s s P e r s o n a {2 // e s t o e s un c o m e n t a r i o de una u n i c a l´ n e a ´ ı3 private S t r i n g nombre ;4 /∗ E s t e c o m e n t a r i o ocupa5 m´s de una l´ n e a a ı6 de c o d i g o ∗/ ´7 private S t r i n g a p e l l i d o s ; Pero, sin duda, los comentarios de documentaci´n son una herramienta real- o mente potente en Java. Los comentarios de documentaci´n se incluyen en el o c´digo y nos sirven para, a partir de ellos, crear documentaci´n de nuestro c´di- o o o go en formato html. En los comentarios de documentaci´n podemos a˜adir eti- o n quetas que nos permiten enriquecer la documentaci´n generada. Veamos c´mo o o se introducen los comentarios de documentaci´n y las etiquetas que tenemos o disponibles. Un comentario de documentaci´n siempre debe empezar por /**, nota que o tras la barra se escriben dos asteriscos, y debe acabar por */, como los comen- tarios de m´s de una l´ a ınea. Dentro de los comentarios de documentaci´n podemos utilizar etiquetas para o a˜adir informaci´n que enriquezca la documentaci´n generada. Por ejemplo, n o o podemos utilizar la etiqueta para indicar quien es el autor del c´digo de una o clase, como en el siguiente ejemplo:1 /∗ ∗ I m p l e m e n t a c i o n de l a ´ c l a s e Persona
  • 45. 46 CAP´ ITULO 2. CLASES2 ∗ Es ta c l a s e d e s c r i b e a un nuevo c o n t a c t o3 ∗ en una agenda de t e l ´ f o n o s e4 ∗ ´ @author Oscar Belmonte Fern´ ndez a5 ∗ @version 1.06 ∗/78 public c l a s s P e r s o n a {9 private S t r i n g nombre ; otros comentarios de documentaci´n: o @version Indicamos la versi´n del c´digo. o o @param nombre Descripci´n del par´metro. o a @return Significado del valor de retorno. @deprecated Raz´n de por qu´ este m´todo est´ obsoleto. o e e a @see #metodo() Referencia cruzada al m´todo. e @exception Excepci´n que se puede producir en el m´todo o e @throws Excepci´n no gestionada o Adem´s, en los comentarios de documentaci´n podemos incluir c´digo HTML. a o o En el listado 2.9 tienes la clase Persona documentada.1 package p e r s o n a . c o m e n t a r i o s ;23 /∗ ∗ I m p l e m e n t a c i o n de l a c l a s e P e r s o n a ´4 ∗ Es ta c l a s e d e s c r i b e a un nuevo c o n t a c t o5 ∗ en una agenda de t e l ´ f o n o s e6 ´ ∗ @author Oscar Belmonte Fern´ ndez a7 ∗ @version 1.08 ∗/910 public c l a s s P e r s o n a {11 private S t r i n g nombre ;12 private S t r i n g a p e l l i d o s ;13 private S t r i n g t e l e f o n o ;14 private s t a t i c i n t n I n s t a n c i a s = 0 ;1516 /∗ ∗17 ∗ C ons t ru ct or por d e f e c t o18 ∗/19 public P e r s o n a ( ) {20 n I n s t a n c i a s ++;21 }2223 /∗ ∗24 ∗ C o n s t r u c t o r con p a r a m e t r o s . ´25 ∗ En n u e v a s v e r s i o n e s , t a n t o e l nombre como l o s a p e l l i d o s s e r a n ´26 ∗ i n m u t a b l e s , no e x i s t i r ´ n m´ todos p a r a c a m o b i a r l o s a e27 ∗ @param nombre Nombre d e l nuevo c o n t a c t o28 ∗ @param a p e l l i d o s A p e l l i d o s d e l nuevo c o n t a c t o29 ∗ @param t e l e f o n o T e l ´ f o n o d e l nuevo c o n t a c t o e30 ∗/31 public P e r s o n a ( S t r i n g nombre , S t r i n g a p e l l i d o s , S t r i n g t e l e f o n o ) {32 t h i s . nombre = nombre ;33 this . a p e l l i d o s = a p e l l i d o s ;34 this . t e l e f o n o = t e l e f o n o ;35 n I n s t a n c i a s ++;36 }3738 /∗ ∗39 ∗ D e v u e l v e e l n´ mero de i n s t a n c i a s c r e a d a s u40 ∗ @ re t ur n El n´ mero de i n s t a n c i a s u
  • 46. ´ 2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACION. 4741 ∗/42 public s t a t i c i n t g e t N I n s t a n c i a s ( ) {43 return n I n s t a n c i a s ;44 }4546 /∗ ∗47 ∗ D e v u e l v e e l nombre d e l c o n t a c t o48 ∗ @re t ur n Nombre d e l c o n t a c t o49 ∗/50 public S t r i n g getNombre ( ) {51 return nombre ;52 }5354 /∗ ∗55 ∗ Devuelve l o s a p e l l i d o s d e l c o n t a c o t56 ∗ @re t ur n A p e l l i d o s d e l c o n t a c t o57 ∗/58 public S t r i n g g e t A p e l l i d o s ( ) {59 return a p e l l i d o s ;60 }6162 /∗ ∗63 ∗ D e v u e l v e e l n´ mero de t e l ´ f o n o d e l c o n t a c t o u e64 ∗ @re t ur n N´ mero de t e l ´ f o n o d e l c o n t a c t o u e65 ∗/66 public S t r i n g g e t T e l e f o n o ( ) {67 return t e l e f o n o ;68 }6970 /∗ ∗71 ∗ Cambia e l nombre d e l c o n t a c t o72 ∗ @param nombre El nuevo nombre d e l c o n t a c t o73 ∗ @ d e p r e c a t e d E s t e m´ todo s e e l i m i n a r a en v e r s i o n e s f u t u r a s e ´74 ∗ @see P e r s o n a ( S t r i n g nombre , S t r i n g a p e l l i d o s , S t r i n g t e l e f o n o )75 ∗/76 public void setNombre ( S t r i n g nombre ) {77 t h i s . nombre = nombre ;78 }7980 /∗ ∗81 ∗ Cambia l o s a p e l l i d o s d e l c o n t a c t o82 ∗ @param a p e l l i d o s Los n u e v o s a p e l l i d o s d e l c o n t a c t o83 ∗ @ d e p r e c a t e d E s t e m´ todo s e e l i m i n a r a en v e r s i o n e s f u t u r a s e ´84 ∗ @see #P e r s o n a ( S t r i n g nombre , S t r i n g a p e l l i d o s , S t r i n g t e l e f o n o )85 ∗/86 public void s e t A p e l l i d o s ( S t r i n g a p e l l i d o s ) {87 this . a p e l l i d o s = a p e l l i d o s ;88 }8990 /∗ ∗91 ∗ Cambia e l n´ mero de t e l ´ f o n o d e l c o n t a c t o u e92 ∗ @param t e l e f o n o El nuevo n´ mero de t e l ´ f o n o u e del contacto93 ∗/94 public void s e t T e l e f o n o ( S t r i n g t e l e f o n o ) {95 this . t e l e f o n o = t e l e f o n o ;96 }97 } Listado 2.9: C´digo fuente de la clase Persona con comentarios de o documentaci´n. o El paquete de desarrollo Java nos proporcionan una herramienta para gene- rar la documentaci´n de nuestras clases en formato HTML a partir del c´digo. o o Esta herramienta se llama javadoc. La generaci´n de c´digo se puede realizar o o desde consola de este modo: javadoc Persona.java /ruta/a/directorio Si no se especifica la ruta la documentaci´n se generar´ en el directorio donde o a se encuentre el fichero Persona.java.
  • 47. 48 CAP´ ITULO 2. CLASESFigura 2.1: Comentarios de documentaci´n generados con la herramienta ojavadoc. Desde Eclipse tambi´n podemos generar la documentaci´n de nuestras clases e ohaciendo click con el bot´n derecho del rat´n sobre el fichero de la clase y despu´s o o eseleccionamos Export → Java Javadoc. Se generar´ toda la documentaci´n de a onuestra clase, o todo el proyecto si lo hemos seleccionado en vez de una claseindividual. En la figura 2.1 se muestra cual es el aspecto de la documentaci´n ogenerada cuando se utiliza un navegador web para su visualizaci´n. oEjercicios. 1. Escribe una clase que abstraiga la idea de Empresa. Una empresa puede tener unicamente como atributos su nombre y un tel´fono de contacto. ´ e 2. A˜ade comentarios de documentaci´n a la clase Empresa y genera la do- n o cumentaci´n de esta clase. o 3. Escribe un sencillo programa para gestionar los contactos de una agenda telef´nica (clase Agenda). Las operaciones b´sicas que la agenda telef´nica o a o es capaz de realizar son:
  • 48. ´2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACION. 49 a) Insertar nuevas Personas en la agenda. b) Listar todas las Personas de la agenda. c) Buscar una Persona a partir de su nombre.Lecturas recomendadas. El excelente libro de Arnold y otros [2] es una referencia completa a la definici´n de clases. Comenta de modo exhaustivo todos los detalles en la o definici´n de una clase, los distintos modificadores de acceso y su signifi- o cado. El modo de presentar los conceptos del lenguaje Java ne la serie de libros de la colecci´n Head first Java es muy interesante. En particular, la referencia o [3] presenta de una manera muy visual los principales conceptos en la definici´n de clases en Java. o Finalmente, una lectura siempre recomendable sobre buenas pr´cticas y a escritura de c´digo limpia es el libro de Robert C. Martin de la referencia o [10].
  • 49. 50 CAP´ ITULO 2. CLASES
  • 50. Cap´ ıtulo 3Herencia e InterfacesContenidos 3.1. Herencia. . . . . . . . . . . . . . . . . . . . . . . . . 52 3.2. Extensi´n de una clase. . . . . . . . . . . . . . o . . . 52 3.2.1. Sobrescribir atributos. . . . . . . . . . . . . . . . . . 54 3.2.2. Sobrescribir m´todos. . . . . . . . . . . . . . e . . . . 56 3.2.3. La palabra reservada super. . . . . . . . . . . . . . . 59 3.2.4. El constructor por defecto y la clase Object. . . . . 59 3.2.5. El operador instanceof. . . . . . . . . . . . . . . . . 60 3.2.6. El modificador final. . . . . . . . . . . . . . . . . . 61 3.2.7. M´todos static. . . . . . . . . . . . . . . . . e . . . . 62 3.3. Clases abstractas. . . . . . . . . . . . . . . . . . . . 63 3.4. Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . 65 3.5. Enumeraciones. . . . . . . . . . . . . . . . . . . . . . 68 3.6. Paquetes en Java. . . . . . . . . . . . . . . . . . . . 69 3.7. Clases e interface anidados . . . . . . . . . . . . . 71Introducci´n oEn el cap´ ıtulo 2 hemos visto c´mo podemos codificar la abstracci´n de una o oentidad del mundo real a c´digo Java. Hemos visto la sintaxis de Java para la ocreaci´n de clases, atributos y m´todos, adem´s de algunos de los modificadores o e aque podemos utilizar sobre ellos. Tambi´n hemos aprendido a crean instancias ea partir de una clase con el operador new. La situaci´n en la que nos encontramos ahora es que nuestras clases abstraen oentidades del mundo real pero, ¿Qu´ ocurre si quiero a˜adir m´s funcionalidad e n aa mis clases?, ¿C´mo puedo a˜adir nuevos atributos a una clase ya existente?, o n¿C´mo puedo ampliar el conjunto de m´todos que proporciona? o e En POO existe un mecanismo fundamental para a˜adir funcionalidad a una nclase el concepto es la Herencia, tambi´n conocido como Extensi´n o Derivaci´n. e o o La idea b´sica de la Herencia es crear una nueva clase a partir de la definici´n a ode otra clase ya existente. La Herencia nos permite construir una clase a˜adien- n 51
  • 51. 52 CAP´ ITULO 3. HERENCIA E INTERFACESdo unicamente la nueva funcionalidad a otra clase ya existente. Y tambi´n nos ´ epermite modificar el comportamiento de la clase original sobrescribiendo susm´todos. e3.1. Herencia. En esta secci´n vamos a ver c´mo se puede ampliar el comportamiento de o ouna clase a trav´s de la herencia. Veremos tambi´n el concepto, muy importante, e ede la vinculaci´n din´mica para encontrar qu´ m´todo se ha de invocar al uti- o a e elizar referencias a clases extendidas, as´ como el uso del operador instanceof. ıFinalmente veremos el significado de los modificadores final y abstract cuandose aplican a la definici´n de un m´todo o una clase. o e3.2. Extensi´n de una clase. oLo primero que hay que destacar es que en Java s´lo est´ permitida la herencia o asimple, una nueva clase s´lo puede extender a una unica clase base. Dicho de o ´otro modo, una clase hija no puede tener m´s de una clase padre. a Caracter´ ısticaJava s´lo admite herencia simple. Una clase no puede tener m´s de una clase o apadre. Imaginemos que necesitamos ampliar nuestra clase Persona, para a˜adirle nnueva funcionalidad. Queremos que nuestra nueva clase contenga la provincia,la poblaci´n de residencia y la edad 1 de cada uno de nuestros contactos. Para oello tenemos dos alternativas antag´nicas: o 1. Reescribir la clase desde cero. 2. Aprovechar al m´ximo el c´digo existente. a o Si optamos por la primera opci´n, no tenemos nada nuevo que aprender, con olo aprendido hasta ahora podemos resolverlo. Si optamos por la segunda opci´n estaremos haciendo uso del mecanismo de oHerencia. Veamos como se utiliza este mecanismo y cual es su sintaxis en Java. En Java se dice que una clase extiende a otra clase cuando a˜ade m´s fun- n acionalidad a una clase ya existente. A la nueva clase se le llama clase hija oextendida, a la clase original se le llama clase padre o base. Para indicar queuna clase extiende el comportamiento de otra utilizamos la palabra reservadaextends. Supongamos que queremos ampliar la definici´n de una Persona para que ocontenga datos de su lugar de residencia, como son la Provincia y la Poblaci´n oy tambi´n la Edad, y llamemos a esta nueva clase Ciudadano. en Java lo hacemos ede este modo: 1 Cuando veamos las clases para manipular fechas en el Cap´ ıtulo 8 veremos una mejorimplementaci´n para obtener la edad de una persona a partir de su fecha de nacimiento o
  • 52. ´ 3.2. EXTENSION DE UNA CLASE. 531 public c l a s s Ciudadano extends P e r s o n a {2 // D e f i n i c i ´ n de l a nueva c l a s e e x t e n d i d a o3 } En la definici´n de la nueva clase podemos incluir nuevos atributos y m´to- o e dos. En el siguiente c´digo de ejemplo, a la clase Ciudadano se le han a˜adido los o n tres nuevos atributos antes mencionados y los getters y setters para estos nuevos atributos. Nuestra nueva clase Ciudadano posee tanto los nuevos m´todos defi- e nidos en ella como los m´todos definidos en su clase padre (con las restricciones e de accesibilidad que veremos en la secci´n 3.6. o En el Listado 3.1 aparece la definici´n completa de la nueva clase Ciudadano. o1 package t i p o s ;23 public c l a s s Ciudadano extends P e r s o n a {4 private S t r i n g p o b l a c i o n ;5 private S t r i n g p r o v i n c i a ;6 private i n t edad ;78 public Ciudadano ( ) {9 super ( ) ;10 iniciaAtributos () ;11 }1213 @Override14 protected void i n i c i a A t r i b u t o s () {15 setNombre ( U n n o m b r e ) ;16 edad = 0 ;17 }1819 public S t r i n g g e t P o b l a c i o n ( ) {20 return p o b l a c i o n ;21 }2223 public void s e t P o b l a c i o n ( S t r i n g p o b l a c i o n ) {24 this . poblacion = poblacion ;25 }2627 public S t r i n g g e t P r o v i n c i a ( ) {28 return p r o v i n c i a ;29 }3031 public void s e t P r o v i n c i a ( S t r i n g p r o v i n c i a ) {32 this . provincia = provincia ;33 }3435 public i n t getEdad ( ) {36 return edad ;37 }3839 public void setEdad ( i n t edad ) {40 t h i s . edad = edad ;41 }42 } Listado 3.1: Definici´n de la clase Ciudadano o ¿C´mo hacemos uso de los m´todos de la clase, tanto de los definidos en o e la clase extendida como los definidos en la clase base?, sencillamente como lo est´bamos haciendo hasta ahora: a trav´s de las referencias, como en el siguiente a e ejemplo de c´digo: o1 Ciudadano c i u d a d a n o = new Ciudadano ( J o s e , G a r c ı a , 5 5 5 1 2 3 4 5 6 , ´ ´ Alcorc´n , Madrid , 40; o2 System . o u t . p r i n t l n ( N o m b r e : + c i u d a d a n o . getNombre ( ) ) ;
  • 53. 54 CAP´ ITULO 3. HERENCIA E INTERFACES3 System . o u t . p r i n t l n ( E d a d : + c i u d a d a n o . getEdad ( ) ) ; Como vemos en la l´ ınea 2 del Listado anterior, hacemos uso del m´todo e getNombre() definido en la clase padre, a partir de una referencia de la clase hija, mientras que en la l´ınea 3 hacemos uso del m´todo getEdad() definido en e la clase hija. ¿Podemos utilizar una referencia a la clase padre para acceder a los mismos m´todos? No, aunque es perfectamente v´lido asignar una referencia de una e a clase hija a una referencia a la clase padre, a trav´s de la referencia a la clase e padre s´lo tendremos acceso a los miembros declarados en ella. En particular, o para el ejemplo de la clase padre Persona y su clase hija Ciudadano, el siguiente c´digo ejemplo contiene un error: o1 Ciudadano c i u d a d a n o = new Ciudadano ( ) ;2 Pesona p e r s o n a = c i u d a d a n o ; // P e r f e c t a m e n t e v a l i d o . ´3 p e r s o n a . getNombre ( ) ; // No hay problema , getNombre ( ) e s t a d e f i n i d o en ´ Persona .4 p e r s o n a . getEdad ( ) ; // E r r o r ! ! ! , getEdad ( ) e s t ´ d e f i n i d o en Ciudadano . a Tambi´n es un error asignar a una referencia de una clase hija una referencia e a la clase padre, el siguiente c´digo de ejemplo contiene un error: o1 P e r s o n a p e r s o n a = new P e r s o n a ( ) ;2 Ciudadano c i u d a d a n o = p e r s o n a ; // E r r o r ! ! ! Concepto clave Una referencia de una clase padre admite una referencia a cualquiera de sus clase hijas, pero nunca al contrario. Piensa qu´ ocurrir´ si no existiese esta prohibici´n, podr´ e ıa o ıamos asignar a una referencia a Ciudadano una referencia de su clase padre Persona, y a trav´s de la e referencia a Ciudadano podr´ ıamos invocar a, por ejemplo, el m´todo getEdad(), e pero, la clase Persona no posee el atributo int edad;, ¿Qu´ se deber´ devolver e ıa en este caso? 3.2.1. Sobrescribir atributos. En algunas circunstancias, podemos vernos en la necesidad de definir un atributo en una clase hija con el mismo nombre que en su clase padre, como muestra el siguiente c´digo de ejemplo: o1 // Est a e s l a c l a s e p a d r e2 public c l a s s D i s t a n c i a {3 float distancia ;45 public D i s t a n c i a ( ) {6 distancia = 0;7 }89 public D i s t a n c i a ( f l o a t d i s t a n c i a ) {10 this . d i s t a n c i a = d i s t a n c i a ;11 }12 // S i g u e l a d e f i n i c i ´ n de e s t a c l a s e . o
  • 54. ´ 3.2. EXTENSION DE UNA CLASE. 5513 }1415 // Esta e s l a c l a s e h i j a16 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {17 // E s t e e s e l a t r i b u t o s o b r e s c r i t o18 double d i s t a n c i a ;1920 public D i s t a n c i a D o b l e P r e c i s i o n ( ) {21 distancia = 0;22 }2324 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {25 this . d i s t a n c i a = d i s t a n c i a ;26 }27 // S i g u e l a d e f i n i c i ´ n de e s t a c l a s e . o28 } En este caso se dice que el atributo distancia de la clase hija DistanciaDoblePrecision sobrescribe el atributo distancia de la clase pa- dre Distancia. Cuando una clase hija sobrescribe alg´n atributo de su clase u padre, el atributo de la clase padre queda oculto , de modo que si aparece el nombre del atributo en la clase hija se utilizar´ el atributo definido en esta clase a y no el definido en la clase padre. Esto no quiere decir que el atributo con el mismo nombre en la clase padre desaparezca, sino que para acceder a ´l ten- e dremos que hacer uso de otro mecanismo como veremos m´s adelante en esta a secci´n. o ¿C´mo accedemos al atributo distancia desde fuera de la clase? Ya lo sabe- o mos, a trav´s de referencias. De acuerdo, entonces, ¿Qu´ mostrar´ el siguiente e e a ejemplo?:1 D i s t a n c i a d i s t a n c i a = new D i s t a n c i a ( 1 0 0 ) ;2 System . o u t . p r i n t l n ( E l v a l o r d e d i s t a n c i a e s : + d i s t a n c i a . d i s t a n c i a ) ;3 D i s t a n c i a d i s t a n c i a D o b l e P r e c i s i o n = new D i s t a n c i a D o b l e P r e c i s i o n ( 2 0 0 ) ;4 System . o u t . p r i n t l n ( E l v a l o r d e d i s t a n c i a D o b l e P r e c i s i o n e s : + distanciaDoblePrecision . distancia ) ; Lo que mostrar´ este c´digo es, sorpresa: a o El valor de distancia es: 100.0 El valor de distancia2 es: 0.0 ¿Qu´ ha ocurrido? Nada extra˜o, simplemente que al acceder al atributo e n a trav´s de la referencia, se ha buscado este valor en la definici´n de la clase e o correspondiente a la referencia, que en los dos casos es Distancia y el atributo que se est´ iniciando en la l´ a ınea 3 del c´digo anterior es el de la clase hija o DistanciaDoblePrecision pues el objeto que se crea es de la clase extendida. Comparemos con el resultado de la ejecuci´n de este otro c´digo ejemplo: o o1 D i s t a n c i a d i s t a n c i a = new D i s t a n c i a ( 1 0 0 ) ;2 System . o u t . p r i n t l n ( E l v a l o r d e d i s t a n c i a e s : + d i s t a n c i a . d i s t a n c i a ) ;3 D i s t a n c i a D o b l e P r e c i s i o n d i s t a n c i a D o b l e P r e c i s i o n = new DistanciaDoblePrecision (200) ;4 System . o u t . p r i n t l n ( E l v a l o r d e d i s t a n c i a D o b l e P r e c i s i o n e s : + distanciaDoblePrecision . distancia ) ; Lo que mostrar´ este c´digo es: a o El valor de distancia es: 100.0 El valor de distanciaDoblePrecision es: 200.0
  • 55. 56 CAP´ ITULO 3. HERENCIA E INTERFACES En este ultimo ejemplo, lo unico que ha cambiado es el tipo de ´ ´ la referencia distanciaDoblePrecision, que en este caso es de tipo DistanciaDoblePrecision, es decir, la clase hija. Concepto clave Cuando una clase hija sobrescribe (oculta) un atributo de la clase padre, el atributo seleccionado se determina por el tipo de la referencia. 3.2.2. Sobrescribir m´todos. e Para introducir el modo de sobrescribir m´todos imaginemos que hemos a˜adido e n al c´digo de la clase Distancia un nuevo m´todo que nos permita incrementar o e la distancia actual:1 public c l a s s D i s t a n c i a {2 float distancia ;34 public D i s t a n c i a ( ) {5 distancia = 0;6 }78 public D i s t a n c i a ( f l o a t d i s t a n c i a ) {9 this . d i s t a n c i a = d i s t a n c i a ;10 }1112 void i n c r e m e n t a D i s t a n c i a ( f l o a t i n c r e m e n t o ) {13 d i s t a n c i a += i n c r e m e n t o ;14 }15 // S i g u e l a d e f i n i c i ´ n de e s t a c l a s e . o16 } Listado 3.2: Definici´n de la clase Distancia o Ahora queremos probar nuestra nueva funcionalidad con este ejemplo:1 D i s t a n c i a D o b l e P r e c i s i o n d i s t a n c i a = new D i s t a n c i a D o b l e P r e c i s i o n ( 1 0 0 ) ;2 distancia . incrementaDistancia (100) ;3 System . o u t . p r i n t l n ( E l v a l o r d e d i s t a n c i a e s : + d i s t a n c i a . d i s t a n c i a ) ; El resultado que obtenemos es el siguiente: El valor de distancia es: 100.0 ¿C´mo es posible? Estamos intentando incrementar la distancia inicial de o 100 en otros 100, y parece que no lo conseguimos. ¿Donde est´ el problema?. a Nuestro nuevo m´todo incrementaDistancia(float) est´ definido en la e a clase padre, y este m´todo incrementa el valor del atributo que hay definido en e ella, no en la clase hija. ¿C´mo podemos arreglarlo? La respuesta es sobrescri- o biendo el m´todo incrementaDistancia(float) en la clase hija de este modo: e1 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {2 double d i s t a n c i a ;34 public D i s t a n c i a D o b l e P r e c i s i o n ( ) {5 distancia = 0;6 }7
  • 56. ´ 3.2. EXTENSION DE UNA CLASE. 578 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {9 this . d i s t a n c i a = d i s t a n c i a ;10 }1112 @Override13 void i n c r e m e n t a D i s t a n c i a ( f l o a t i n c r e m e n t o ) {14 d i s t a n c i a += i n c r e m e n t o ;15 }16 // S i g u e l a d e f i n i c i ´ n de e s t a c l a s e . o17 } Listado 3.3: Definici´n de la clase DistanciaDoblePrecisiona o N´tese el uso de la anotaci´n @Override que indica al compilador de Java que o o se est´ intentando sobrescribir un m´todo en la clase padre. a e Ahora s´ el resultado obtenido es: ı, El valor de distancia es: 200.0 Para que una clase hija sobrescriba un m´todo de su clase padre es necesario e que ambos m´todos tengan la misma signatura y el mismo tipo de retorno, de e lo contrario no se sobrescribe el m´todo, como en el siguiente ejemplo: e1 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {2 double d i s t a n c i a ;34 public D i s t a n c i a D o b l e P r e c i s i o n ( ) {5 distancia = 0;6 }78 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {9 this . d i s t a n c i a = d i s t a n c i a ;10 }1112 // I n t e n t a m o s s o b r e s c r i b i r cambiando e l t i p o d e l argumento13 // Se p r o d u c e un e r r o r14 @Override15 void i n c r e m e n t a D i s t a n c i a ( double i n c r e m e n t o ) {16 d i s t a n c i a += i n c r e m e n t o ;17 }18 // S i g u e l a d e f i n i c i ´ n de e s t a c l a s e . o19 } Aunque es posible que la clase hija sobrescriba un m´todo de la clase pa- e dre ampliando el modificador de acceso, por ejemplo, en el caso del Lista- do 3.2.2 posemos definir el m´todo incrementaDistancia como public void e incrementaDistancia(double incremento), de modo que hemos ampliado el modificador de acceso desde acceso paquete hasta public. Lo que no est´ per- a mitido es que cuando se sobrescribe un m´todo de la clase padre, el hijo restrinja e el modificador de acceso. Siguiendo con el ejemplo, intentar sobrescribir el m´to- e do incrementaDistancia como private void incrementaDistancia(double incremento), dar´ un error. ıa Gracias al uso de la anotaci´n @Override2 obtenemos un error que nos o informa que el nuevo m´todo no est´ sobrescribiendo a ning´n m´todo en su e a u e clase padre. Si eliminamos la anotaci´n no obtenemos ning´n error y lo que o u estaremos haciendo es definiendo un nuevo m´todo que toma como argumento e una variable de tipo double. 2 Las anotaciones fueron introducidas en la versi´n 5 de Java o
  • 57. 58 CAP´ ITULO 3. HERENCIA E INTERFACES Buenas pr´cticas a Para asegurar que los m´todos en las clases hijas sobrescriben m´todos en la e e clase padre util´ ıcese la anotaci´n @Override en el m´todo sobrescrito. o e Ya sabemos que a una referencia a una clase padre le podemos asignar una referencia a cualquier clase hija, de acuerdo, modifiquemos nuestro c´digo de o prueba del siguiente modo:1 D i s t a n c i a d i s t a n c i a = new D i s t a n c i a D o b l e P r e c i s i o n ( 1 0 0 ) ;2 distancia . incrementaDistancia (100) ;3 System . o u t . p r i n t l n ( E l v a l o r d e d i s t a n c i a e s : + d i s t a n c i a . d i s t a n c i a ) ; ¿Qu´ es lo que obtenemos? Sorpresa de nuevo: e El valor de distancia es: 0.0 ¿C´mo puede ser que obtengamos 0.0 si estamos creando un objeto de tipo o DistanciaDoblePrecision con un valor inicial de 100 y despu´s lo estamos e incrementando en 100 unidades m´s?. La respuesta, esta vez est´ recogida en a a este concepto clave: Concepto clave Cuando accedemos a los m´todos de un objeto a trav´s de una referencia se e e selecciona el m´todo a partir del tipo del objeto y no de la referencia a trav´s e e de la que se accede. Este concepto es muy importante en POO y a este mecanismo se le llama Vinculaci´n din´mica . o a ¿Qu´ est´ ocurriendo entonces? Ocurre que distancia es una referen- e a cia de tipo Distancia, pero el tipo del objeto al que hace referencia, el que creamos con el operador new es DistanciaDoblePrecision. Al usar el m´todo incrementaDistancia(100) la vinculaci´n din´mica ejecuta el e o a c´digo de DistanciaDoblePrecision no el de Distancia. Mientras que o tal y como sabemos de la Secci´n 3.2.1, si utilizamos atributos no se o hace uso de la vinculaci´n din´mica y se accede al atributo correspon- o a diente al tipo que indica la referencia no el objeto que hay por deba- jo de ella, por lo tanto si escribimos distancia.distancia estamos acce- diendo al atributo en Distancia pero el atributo que se increment´ con o distancia.incrementaDistancia(100) fue el que increment´ la vinculaci´n o o din´mica, es decir, el de DistanciaDoblePrecision. a N´tese la diferencia fundamental con respecto al acceso a los atributos, donde o el atributo al que se accede se determina por el tipo de la referencia y no del objeto. Tambi´n es posible que una clase sobrescriba un m´todo ampliando el tipo e e del valor de retorno, es decir que si en la clase padre Distancia tenemos un m´todo como Distancia metodo() la clase hija puede sobrescribirlo con el m´todo e e DistanciaDoblePrecision metodo(), ya que se ha ampliado el tipo del valor de retorno
  • 58. ´ 3.2. EXTENSION DE UNA CLASE. 59 desde el original Distancia a DistanciaDoblePrecision, esta posibilidad fue introducida en la versi´n 5 de Java. o Pero cuidado, esto ultimo no funciona con los tipos de retorno primitivos, si ´ tenemos en la clase padre un m´todo definido como public float unMetodo() e que devuelve el tipo primitivo float no lo podemos sobrescribir en una clase hija con public double unMetodo() que devuelve el tipo primitivo double. 3.2.3. La palabra reservada super. Existen casos en los que, desde una clase hija, nos interesa acceder a los m´todos o atributos sobrescritos en la clase padre. Si escribimos en la clase hija e simplemente el nombre del atributo o del m´todo estaremos haciendo uso de e la definici´n dada para ellos en la clase hija. ¿C´mo accedemos a los miembros o o sobrescritos desde la clase hija?. La respuesta es haciendo uso de la palabra reservada super. Definici´n o La palabra reservada super es una referencia a la clase padre, del mismo modo que la palabra reservada this es una referencia a la propia clase. 3.2.4. El constructor por defecto y la clase Object. En el c´digo de los ejemplos de prueba, nunca hemos utilizado el construc- o tor sin par´metros de las clases Distancia (v´ase el listado 3.2) ni de la clase a e DistanciaDoblePrecision (v´ase el listado 3.3), luego, podemos intentar eli- e minar estos dos constructores e iniciar el atributo distancia de ambas clases en el momento de la definici´n, tal y como se muestra en el siguiente listado: o1 //−−−− D e f i n i c i ´ n de l a c l a s e o D i s t a n c i a −−−−//2 public c l a s s D i s t a n c i a {3 float distancia = 0;45 // E l i m i n a d o e l constructor s i n parametros ´67 public D i s t a n c i a ( f l o a t d i s t a n c i a ) {8 this . d i s t a n c i a = d i s t a n c i a ;9 }1011 void i n c r e m e n t a D i s t a n c i a ( f l o a t i n c r e m e n t o ) {12 d i s t a n c i a += i n c r e m e n t o ;13 }14 // S i g u e l a d e f i n i c i ´ n de e s t a c l a s e . o15 }16 //−−−− D e f i n i c i ´ n de l a c l a s e D i s t a n c i a D o b l e P r e c i s i o n −−−−// o17 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {18 double d i s t a n c i a = 0 ;1920 // E l i m i n a d o e l constructor s i n parametros ´2122 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {23 this . d i s t a n c i a = d i s t a n c i a ;24 }2526 @Override27 void i n c r e m e n t a D i s t a n c i a ( f l o a t i n c r e m e n t o ) {28 d i s t a n c i a += i n c r e m e n t o ;29 }30 // S i g u e l a d e f i n i c i ´ n de e s t a c l a s e . o
  • 59. 60 CAP´ ITULO 3. HERENCIA E INTERFACES Pero inmediatamente obtendremos el siguiente error en el c´digo de la clase o DistanciaDoblePrecision Implicit super constructor Distancia() is undefined. Must explicitly invoke another constructor. Este error es debido a que, el cons- tructor de la clase hija public DistanciaDoblePrecision(double distancia) est´ intentando invocar impl´ a ıcitamente al constructor de la clase padre public Distancia() que no est´ definido. Este es el mecanismo en la creaci´n de objetos a o en Java cuando existe relaci´n de herencia entre clases, desde los constructores o de las clases hijas, si no se indica lo contrario, se intenta invocar al constructor sin par´metros de la clase padre, que por este motivo es llamado Constructor por a defecto . Si no se indica lo contrario, lo primero que se hace desde el constructor de una clase hija es llamar al constructor por defecto de la clase padre. Buenas pr´cticas a Para evitar problemas en la ceraci´n de objetos, es conveniente definir siempre o el constructor por defecto en nuestras clases. El error anterior lo podemos corregir de dos modos, a˜adiendo los construc- n tores por defecto a cada una de las clases, o bien, llamando desde el constructor con par´metros de la clase hija al constructor con par´metros de la clase pa- a a dre, para que no se llame por defecto el constructor sin par´metros, que no a est´ definido: a1 public D i s t a n c i a D o b l e P r e c i s i o n ( f l o a t d i s t a n c i a ) {2 super ( 0 ) ; // Llamamos a l c o n s t r u c t o r con p a r ´ m e t r o s d e l p a d r e a3 this . d i s t a n c i a = d i s t a n c i a ;4 } Si optamos por la segunda soluci´n, la llamada al constructor del padre es o lo primero que debemos hacer en el construtor del hijo; en el ejemplo anterior si intercambiamos las l´ıneas 3 y 4 obtendremos el siguiente error Constructor call must be the first statement in a constructor La pregunta que nos surge es ¿A qu´ constructor se llama desde el construc- e tor por defecto de la clase Distancia que no est´ extendiendo a ninguna otra a clase, tal y como se muestra en el Listado 3.2? Para responder a esta pregunta necesitamos saber que la clase Object es la clase que est´ en la ra´ del ´rbol a ız a de jerarqu´ de clases en Java, y que si una clase expl´ ı ıcitamente no extiende a ninguna otra, impl´ıcitamente est´ extendiendo a la clase Object. a Concepto clave La clase Object se encuentra en la ra´ del ´rbol de jerarqu´ de clases en Java. ız a ıa Cualquier otra clase, bien directamente o bien a trav´s de herencia, es hija de e la clase Object. 3.2.5. El operador instanceof. Ya sabemos que cuando llamamos a los m´todos de un objeto a trav´s de una e e referencia, es el tipo del objeto (la clase a la que pertenece) el que determina qu´ m´todo se ha de llamar. A este mecanismo lo hemos llamado Vinculaci´n e e o
  • 60. ´ 3.2. EXTENSION DE UNA CLASE. 61 din´mica. No importa el tipo de la referencia a la que asignemos el objeto siem- a pre que, evidentemente, el tipo de la referencia sea compatible con el tipo del objeto, en tiempo de ejecuci´n el mecanismo de vinculaci´n din´mica determi- o o a nar´ cual es el m´todo que se ha de llamar si es que ese m´todo est´ sobrescrito. a e e a La pregunta que ahora nos surge es: Si el unico acceso que tenemos es a ´ trav´s de referencias y el tipo de la referencia no tiene por qu´ coincidir con e e el tipo del objeto que tiene asignado, basta con que sean compatibles, ¿C´moo podemos conocer el verdadero tipo del objeto asignado a la referencia?. Para dar contestaci´n a esta pregunta Java pone a nuestra disposici´n un o o operador binario, el operador instanceof con el que podemos preguntar si el objeto asignado a una referencia es de un determinado tipo o no; el valor de retorno de este operador es un booleano true o false. Estos son algunos casos de uso:1 P e r s o n a p e r s o n a = new P e r s o n a ( ) ;2 System . o u t . p r i n t l n ( p e r s o n a i n s t a n c e o f P e r s o n a ) ; // D e v o l v e r a emph{ t r u e } ´3 System . o u t . p r i n t l n ( p e r s o n a i n s t a n c e o f Ciudadano ) ; // D e v o l v e r a emph{´ false}4 Ciudadano c i u d a d a n o = new Ciudadano ( ) ;5 System . o u t . p r i n t l n ( c i u d a d a n o i n s t a n c e o f Ciudadano ) ; // D e v o l v e r a emph{ ´ true }6 System . o u t . p r i n t l n ( c i u d a d a n o i n s t a n c e o f P e r s o n a ) ; // D e v o l v e r a emph{ ´ true } Aunque el operador instanceof nos puede prestar ayuda en algunos casos, conviene seguir la siguiente buena pr´ctica: a Buenas pr´cticas a Intenta evitar el uso del operador instanceof en tu c´digo, utiliza polimorfismo o para no hacer uso de este operator. 3.2.6. El modificador final. En el cap´ıtulo 2 vimos el uso del modificador final aplicado a los atributos de una clase. El operador final tambi´n se puede aplicar a los m´todos de una clase, e e de tal modo que si un m´todo se declara como final estamos indicando que e ese m´todo no puede ser sobrescrito por ninguna clase hija. Con ello estamos e garantizando que el trabajo que realiza el m´todo es siempre el mismo con e independencia de si el objeto sobre el que se llama es instancia de la clase padre o instancia de alguna de sus clases hijas. En el siguiente listado se muestra c´mo o se puede violar el comportamiento al iniciar una instancia por parte de un hijo si el padre no protege el m´todo que inicia los atributos con el modificado final e :1 // C´ digo de l a c l a s e p a d r e o2 public c l a s s P e r s o n a {3 private S t r i n g nombre ;4 private S t r i n g a p e l l i d o s ;5 private S t r i n g t e l e f o n o ;6 private s t a t i c i n t n I n s t a n c i a s ;78 public P e r s o n a ( ) {
  • 61. 62 CAP´ ITULO 3. HERENCIA E INTERFACES9 super ( ) ;10 iniciaAtributos () ;11 }1213 protected void i n i c i a A t r i b u t o s ( ) {14 nombre = ;15 apellidos = ;16 telefono = ;17 nInstancias = 0;18 }19 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o2021 // C´ digo de l a c l a s e h i j a o22 public Ciudadano ( ) {23 super ( ) ;24 // Aqu´ cambiamos e l comportamiento de i n i c i o de l o s ı atributos25 iniciaAtributos () ;26 }2728 @Override29 protected void i n i c i a A t r i b u t o s ( ) {30 setNombre ( U n n o m b r e ) ;31 }32 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o Simplemente a˜adiendo el modificador final al m´todo iniciaAtributos n e de la clase padre, si intentamos sobrescribir este m´todo en la clase hija ob- e tendremos el siguiente error Cannot override the final method from Persona advirti´ndonos que no podemos sobrescribir un m´todo declarado como final e e en la clase padre. Buenas pr´cticas a Los m´todos a los que se llama desde los constructores de una clase deben e ser modificados como final para prevenir que alguna clase hija modifique el comportamiento al crear instancias de la clase. Es muy recomendable seguir la anterior buena pr´ctica, piensa que ocurrir´ a ıa si en el constructor de una clase padre que abre una conexi´n a una base de o datos, y una clase hija sobrescribiese las tareas de inicio, y la conexi´n a la base o de datos no se estableciese; toda la aplicaci´n dejar´ de funcionar. o ıa El modificador final tambi´n se puede aplicar sobre una clase de este modo: e1 public f i n a l c l a s s P e r s o n a {2 // La d e f i n i c i o n de l a c l a s e ´ En este caso lo que estamos indicando es que la clase Persona no se puede extender porque la hemos declarado como final. Un ejemplo de clase final en Java es la clase String que est´ declarada como final y por lo tanto no se a puede extender, es decir no podemos crear hijas de ella. 3.2.7. M´todos static. e En el Cap´ ıtulo 2 vimos el significado del modificador static cuando se aplica a m´todos. El modificador static indica que el m´todo pertenece a la e e clase y no a las instancias de la clase. Por otro lado hemos visto lo que significa la Vinculaci´n din´mica, determinar en tiempo de ejecuci´n el m´todo que se o a o e debe llamar al invocarse desde una instancia cuando est´ sobrescrito. F´ a ıjate
  • 62. 3.3. CLASES ABSTRACTAS. 63que el mecanismo de sobrescribir m´todos funciona en el ´mbito de los objetos, e amientras que los m´todos static pertenecen al ´mbito de las clases. Es por esto e aque un m´todo static de una clase padre no se puede sobrescribir, los m´todos e estatic de la clase padre son m´todos ocultos que no son visibles desde las clases ehija. Si en una clase hija declaramos un m´todo con la misma signatura que un em´todo static en la clase padre, lo que estamos haciendo realmente es creando eun nuevo m´todo en la clase hija sin ninguna relaci´n con el mismo m´todo e o een la clase padre. Obviamente si intentamos usar la anotaci´n @Override para oindicar que queremos sobrescribir el m´todo obtendremos un error This instance emethod cannot override the static method from ...3.3. Clases abstractas.Hasta este momento, siempre que hemos definido los m´todos de las clases que ehemos creado, siempre hemos escrito c´digo en la definici´n de los m´todos. A o o eveces es util simplemente declarar m´todos un una clase padre, sin dar ninguna ´ eimplementaci´n para ellos, y delegar la implementaci´n a las clases hijas que o ola extiendan. Esta es una t´cnica muy potente que utiliza el concepto de Poli- emorfismo de la POO. De este modo estamos garantizando que todas las claseshijas de la misma clase padre tiene un m´todo con la misma signatura, aunque, eobviamente, cada una de las clase hijas puede tener una implementaci´n distinta opara el m´todo polim´rfico. e o Si queremos indicar que no vamos a dar una implementeaci´n para alg´n o um´todo declarado en la clase, debemos modificarlo con la palabra reservada eabstract, con la restricci´n de que si una clase tiene alg´n m´todo abstract o u eella misma tambi´n debe ser declarada como abstract. e Tambi´n podemos declarar una clase como abstract sin que ninguno de sus em´todos los sea. Si una clase es declarada como abstract, sobre ella tenemos ela restricci´n recogida en el siguiente concepto clave: o Concepto claveNo se pueden crear instancias de una clase declarada como abstract De no existir esta restricci´n ¿Qu´ ocurrir´ si se llamase a un m´todo o e ıa eabstract de un objeto? ¿Qu´ c´digo se ejecutar´ Evidentemente de poder e o ıa?ser as´ tendr´ ı ıamos un grave problema, ya que puede que no existiene ning´n uc´digo para ejecutar. o Los m´todos abstract de la clase padre deben ser definidos en las clases ehijas, en cuyo caso los m´todos en las clase hijas ya no ser´n abstract y tampoco e ala propia clase hija. Ahora bien, puede existir alg´n el caso en que una clase hija utampoco defina alg´n m´todo abstract de la clase padre; en este caso la clase u ehija tambi´n deber´ ser declarada abstract y no podremos crear instancias de e aella. Un ejemplo recurrente para mostrar el uso de las clases abstract es unaaplicaci´n que dibuje distintas figuras geom´tricas tales como c´ o e ırculos, tri´ngulos ay cuadrados. Podr´ ıamos declara el comportamiento com´n de todas estas clases, upor ejemplo el m´todo dibujate() en una clase padre llamada Figuras, y ecada una de las clases hijas tuviese la implementaci´n adecuada para dibujarse o
  • 63. 64 CAP´ ITULO 3. HERENCIA E INTERFACES dependiendo de su naturaleza. De modo muy esquem´tico el c´digo de estas a o clases podr´ ser algo como lo mostrado en el Listado 3.4: ıa1 public abstract c l a s s F i g u r a {2 public abstract void d i b u j a t e ( ) ;3 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o4 }56 public c l a s s T r i a n g u l o extends F i g u r a {7 public void d i b u j a t e ( ) {8 // C´ digo p a r a d i b u j a r un t r i ´ n g u l o o a9 }10 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o11 }1213 public c l a s s Cuadrado extends F i g u r a {14 public void d i b u j a t e ( ) {15 // C´ digo p a r a d i b u j a r un c u a d r a d o o16 }17 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o18 }1920 public c l a s s C i r c u l o extends F i g u r a {21 public void d i b u j a t e ( ) {22 // C´ digo p a r a d i b u j a r un c´ r c u l o o ı23 }24 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o25 } Listado 3.4: Definici´n de una clase abstract y algunas clases hijas. o La potencia del c´digo del Listado anterior se hace evidente cuando recor- o damos que podemos asignar cualquier objeto de una clase hija (Triangulo, Cuadrado o Circulo) a una referencia de la clase padre Figura, de modo que podr´ ıamos escribir algo como:1 F i g u r a f i g u r a = new C i r c u l o ( ) ;2 f i g u r a . d i b u j a t e ( ) ; // D i b u j a r a un c´ r c u l o ´ ı3 f i g u r a = new T r i a n g u l o ( ) ;4 f i g u r a . d i b u j a t e ( ) ; // D i b u j a r a un t r i a n g u l o ´ ´5 f i g u r a = new Cuadrado ( ) ;6 f i g u r a . d i b u j a t e ( ) ; // D i b u j a r a un c u a d r a d o ´ Listado 3.5: Uso de una referencia a una clase padre abstract para recorrer instancias de las clases hijas. De nuevo la Vinculaci´n din´mica en cada una de las llamadas al m´todo o a e dibujate() determinar´ el m´todo que se debe invocar. a e Buenas pr´cticas a El dise˜o de tus aplicaciones debe estar orientado al interface no a la implemen- n taci´n. o ¿Qu´ quiere decir esta buena pr´ctica? La idea es que debes concentrarte en e a crear buenas abstracciones, es decir debes intentar encontrar el comportamiento com´n (declaraci´n de los m´todos) a tus clases para que las puedas tratar de u o e manera homog´nea, con independencia de c´mo se materializa ese comporta- e o miento com´n (implementaci´n de los m´todos en cada clase). En el caso de u o e la aplicaci´n para dibujar figuras geom´tricas, el comportamiento com´n es el o e u
  • 64. 3.4. INTERFACES. 65 hecho de que todas las figuras geom´tricas puede dibujarse. La implementaci´n e o concreta es el modo en que cada una de las clases hija se dibuja. El uso de esta buena pr´ctica en el Listado 3.5 es que las referencias deben a ser siempre del tipo m´s general posible (clase abstracta o interface como a veremos en la siguiente secci´n), y no de una clase concreta. Rescribamos el o c´digo del ultimo listado haciendo caso omiso de esta buena pr´ctica: o ´ a1 C i r c u l o f i g u r a = new C i r c u l o ( ) ;2 f i g u r a . d i b u j a t e ( ) ; // D i b u j a r a un c´ r c u l o ´ ı3 f i g u r a = new T r i a n g u l o ( ) ; // E r r o r n u e s t r a f i g u r a e s un t e x t t t { C i r c u l o } no un t e x t t t { T r i a n g u l o }4 figura . dibujate () ;5 f i g u r a = new Cuadrado ( ) ; // E r r o r n u e s t r a f i g u r a e s un t e x t t t { C i r c u l o } no un t e x t t t { Cuadrado }6 figura . dibujate () ; Como ves, no podemos aprovechar el comportamiento polim´rfico de nues- o tras figuras ya que las referencias son de un tipo concreto y no de un tipo abstracto, la clase Figura. En una clase hija tambi´n podemos declarar como abstract alg´n m´todo e u e definido en la clase padre y que por lo tanto no es abstract. ¿Cuando nos puede interesar esta estrategia? Puede ser interesante para borrar el comportamiento por defecto que ofrece la clase padre, ya que si la clase hija a su vez es extendida por otra clases, estas deber´ definir el m´todo declarado abstract. Obviamente, ıa e si una clase hija declara como abstract un m´todo de la clase padre, aunque este e no fuese abstract en la clase padre, la tendremos que declarar como abstract. 3.4. Interfaces. Los interface son una nueva construcci´n del lenguaje Java que da un paso o m´s all´ en las clases abstract. Puedes pensar que un interface es como una a a clase abstract en la que todos sus m´todos son abstract. e Siguiendo con el ejemplo de las figuras geom´tricas de la Secci´n 3.3 podemos e o definir nuestro primer interface como:1 public i n t e r f a c e D i b u j a b l e {2 public void d i b u j a ( ) ;3 } Como ves, estamos usando la palabra reservada interface para indicar que estamos definiendo un interface. Las clases no extienden a los interfaces si no que los implementan y esto se indica con el uso de la palabra reservada implements de este modo:1 public c l a s s T r i a n g u l o implements D i b u j a b l e {2 @Override3 public void d i b u j a ( ) {4 // C´ digo p a r a d i b u j a r un t r i ´ n g u l o o a5 }6 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o7 }89 public c l a s s Cuadrado implements D i b u j a b l e {10 @Override11 public void d i b u j a ( ) {12 // C´ digo p a r a d i b u j a r un c u a d r a d o o
  • 65. 66 CAP´ ITULO 3. HERENCIA E INTERFACES13 }14 // S i g u e l a d e f i n i c i ´ n de l a o clase15 }1617 public c l a s s C i r c u l o implements D i b u j a b l e {18 @Override19 public void d i b u j a ( ) {20 // C´ digo p a r a d i b u j a r un c´ r c u l o o ı21 }22 // S i g u e l a d e f i n i c i ´ n de l a c l a s e o23 } F´ ıjate que para indicar que las clases est´n implementando un m´todo de- a e clarado en un interface se anota el m´todo con @Override. e Y ahora, de nuevo, aparece la magia del polimorfismo y la vinculaci´n o din´mica: a una referencia de un tipo interface le podemos asignar cualquier a objeto de una clase que implemente ese interface, de modo que el c´digo del o siguiente listado es perfectamente v´lido: a1 D i b u j a b l e f i g u r a = new C i r c u l o ( ) ;2 f i g u r a . d i b u j a ( ) ; // D i b u j a r ´ un c´ r c u l o a ı3 f i g u r a = new T r i a n g u l o ( ) ;4 f i g u r a . d i b u j a ( ) ; // D i b u j a r ´ un t r i a n g u l o a ´5 f i g u r a = new Cuadrado ( ) ;6 f i g u r a . d i b u j a ( ) ; // D i b u j a r ´ un c u a d r a d o a F´ ıjate que hemos utilizado una referencia de un tipo lo m´s amplio posible a Dibujable y el comportamiento se materializa en la creaci´n de las instancias o de clases concretas: Circulo, Triangulo o Cuadrado. En la definici´n de un interface podemos declarar cualquier n´mero de o u m´todos y tambi´n cualquier n´mero de constantes como en el siguiente ejemplo: e e u1 public c l a s s i n t e r f a c e D i b u j a b l e {2 public s t a t i c f i n a l C o l o r BLANCO = new C o l o r ( 2 5 5 , 2 5 5 , 2 5 5 ) ;3 public s t a t i c f i n a l C o l o r NEGRO = new C o l o r ( 0 , 0 , 0 ) ;4 public void d i b u j a ( ) ;5 } El anterior interface tiene contiene la declaraci´n de dos constantes, una o define el color BLANCO y la otra el color NEGRO. Una ventaja del uso de interfaces para modelar el comportamiento de las clases es que una clase puede implementar cualquier n´mero de interfaces. u Recuerda que en el caso de la extensi´n de una clase Java s´lo permite herencia o o simple. En el siguiente Listado se muestra un ejemplo de clase que implementa m´s de un inteface: a1 // D e c l a r a c i ´ n de un nuevo t e x t t t { i n t e r f a c e } o2 public c l a s s i n t e r f a c e T r a n s f o r m a b l e {3 public void e s c a l a ( i n t sx , i n t s y ) ;4 public void d e s p l a z a ( i n t dx , i n t dy ) ;5 }67 // D e c l a r a c i ´ n de l a c l a s e o8 public c l a s s C i r c u l o implements D i b u j a b l e , T r a n s f o r m a b l e {9 @Override10 public void d i b u j a ( ) {11 // Aqu´ l a d e f i n i c i ´ n d e l m´ todo ı o e12 }1314 @Override15 public void e s c a l a ( i n t sx , i n t s y ) {
  • 66. 3.4. INTERFACES. 6716 // Aqu´ l a ı definici´n o d e l m´ todo e17 }1819 @Override20 public void d e s p l a z a ( i n t dx , i n t dy ) {21 // Aqu´ l a d e f i n i c i ´ n d e l m´ todo ı o e22 }23 } Los interface al igual que las clases se pueden extender, y adem´s un a interface puede extender a m´s de un interface, la herencia simple es una a restricci´n en el ´mbito de las clases, los interface no poseen esta restricci´n. o a o En el siguiente listado tienes un ejemplo:1 public i n t e r f a c e F i g u r a extends D i b u j a b l e , T r a n s f o r m a b l e {2 public void g i r a ( f l o a t a n g u l o ) ;3 } Listado 3.6: Un interface que extiende a otros dos Con el uso de los interface la Buena pr´ctica de programar orientado al a interface toma a´n m´s fuerza. Por cierto no confundas la palabra reservada u a interface con el concepto de interface, este concepto se refiere a los m´todos e accesibles de una clase que son los que podemos utilizar para trabajar con la clase. Hasta aqu´ hemos visto el grueso del trabajo con herencia, hemos visto cual ı es su potencia y como trabajar con ella en Java. Y todo ello para llevarnos el siguiente jarro de agua fr´ ıa: Buenas pr´cticas a En tus dise˜os software, favorece la composici´n frente a la herencia. n o ¿Qu´ significa esta buena pr´ctica? ¿No debemos utilizar nunca la herencia? e a No, todo lo contrario, la herencia es una t´cnica de POO a objetos muy potente e siempre que se la utilice bien. Un mal uso de la herencia es utilizarla para reaprovechar c´digo. La herencia significa que entre los conceptos que queremos o abstraer existe una clara relaci´n padre-hijo. No debemos utilizar la herencia o para reaprovechar c´digo entre clases que no est´n relacionadas l´gicamente a o a o traves de la herencia. Por ejemplo, tiene poco sentido que la clase Persona sea clase padre de la clase Ciudad simplemente porque una ciudad tenga un nombre. Este no es un buen uso de la herencia. La relaci´n entre esos entes en el mundo real no existe o y no debemos trasladarla de un modo artificial a nuestro c´digo. o La composici´n, significa que una clase contiene como atributos instancias o de otras clases. Este mecanismo de relaci´n entre clases es m´s flexible que la o a herencia y por lo tanto menos sensible a los cambios que tengamos que hacer en nuestro c´digo, m´s a´n si como referencias a las clases utilizamos interfaces o a u o clases abstractas. En definitiva, favorecer la composici´n frente a la herencia significa usar la o herencia s´lo cuando est´ justificado en nuestro dise˜o y no s´lo por comodidad. o e n o
  • 67. 68 CAP´ ITULO 3. HERENCIA E INTERFACES 3.5. Enumeraciones. Las enumeraciones son una construcci´n del lenguaje introducida en la ver- o si´n 5 de Java. Las enumeraciones nos sirven para definir listas enumeradas de o elementos y algo m´s, ya que en cierto modo son como clases, pueden tener a constructores, m´todos y atributos. El primer elemento de la enumeraci´n tiene e o la posici´n 0. o La unica restricci´n que tiene las enumeraciones sobre las clases es que las ´ o enumeraciones no se pueden extender ya que impl´ ıcitamente toda enumeraci´no est´ extendiendo a la clase java.lang.Enum. Adem´s, el ´mbito de los cons- a a a tructores debe ser private o de paquete. La clase Enum nos proporciona algunos m´todos utiles que podemos utili- e ´ zar en nuestra propias enumeraciones, como veremos a continuaci´n. Podemos o definir una enumeraci´n de una manera tan sencilla como la mostrada en el o siguiente Listado:1 public enum Semana {2 LUNES( P r i m e r d ı a d e l a s e m a n a . ) , ´3 MARTES( N i t e c a s e s n i t e e m b a r q u e s . ) ,4 MIERCOLES( S i n c o m e n t a r i o s . ) ,5 JUEVES( S i e m p r e e n m e d i o . ) ,6 VIERNES( ´ l t i m o d ´ a d e t r a b a j o . ) , U ı7 SABADO( E m p i e z a e l f i n d e s e m a n a . ) ,8 DOMINGO( M a ~ a n a d e n u e v o a t r a b a j a r . ) ; n910 private S t r i n g c o m e n t a r i o ;1112 // C o n s t r u c t o r a c c e s o de p a q u e t e o p r i v a t e .13 Semana ( S t r i n g c o m e n t a r i o ) {14 this . comentario = comentario ;15 }1617 public S t r i n g g e t C o m e n t a r i o ( ) {18 return c o m e n t a r i o ;19 }20 } Listado 3.7: Definici´n de una enumeraci´n para los d´ de la semana o o ıas La clase Enum nos proporciona el m´todo values() que devuelve un array e de String con todos los nombre de los elementos de la enumeraci´n. Cada o uno de los elementos de la enumeraci´n posee dos m´todos heredados de la o e clase Enum uno para conocer el nombre del elemento name(), y otro para co- nocer el ordinal del elemento dentro de la enumeraci´n ordinal(), adem´s, o a evidentemente, de los m´todos que nosotros mismo hayamos definido (el m´to- e e do getComentario(), en el ejemplo del Listado 3.8. El siguiente Listado muestra un ejemplo de uso de la anterior enumeraci´n: o1 f o r ( Semana d i a : Semana . v a l u e s ( ) ) {2 System . o u t . p r i n t l n ( d i a . name ( ) ) ;3 System . o u t . p r i n t l n ( d i a . o r d i n a l ( ) ) ;4 System . o u t . p r i n t l n ( d i a . g e t C o m e n t a r i o ( ) ) ;5 } Listado 3.8: Uso de una enumeraci´n o
  • 68. 3.6. PAQUETES EN JAVA. 69 3.6. Paquetes en Java. Los paquetes son una construcci´n del lenguaje Java que nos permite agrupar o clases que est´n l´gicamente relacionadas en el mismo grupo o paquete. Para a o denotar que una clase pertenece a un determinado paquete, se indica en la definici´n de la clase con la palabra reservada package seguida del nombre del o paquete como en el siguiente ejemplo:1 p a c k a g e agenda ; // E sta c l a s e e s t a d e n t r o d e l p a q u e t e agenda ´23 c l a s s P e r s o n a { // V i s i b i l i d a d d e n t r o d e l p a q u e t e4 // D e f i n i c i ´ n de l a c l a s e o5 } Regla de convenci´n o Los nombres de paquetes se escriben en min´sculas. Si el nombre de un paquete u est´ compuesto por m´s de una palabra, se separan mediante puntos. a a Un nombre v´lido de paquete con m´s de una palabra es: agenda.datos o a a agenda.io.teclado. El ambito o visibilidad por defecto de una clase es el paquete, por ello, para ´ denotar que la visibilidad de una clase est´ restringida al paquete en la que a est´ definida no se utiliza ning´n modificador de acceso. a u Como ya sabemos, la visibilidad tambi´n se define sobre los miembros de e una clase de tal manera que un miembro puede se p´blico, privado, protegido u o de paquete. Veamos con mas detalle como se restringe la visibilidad con cada uno de estos modificadores de acceso. El modificador de acceso private es el m´s restrictivo de los cuatro, un a miembro privado no es accesible por ninguna otra clase. Podemos utilizar este modificador de acceso para ocultar completamente miembros de una clase. Una clase nunca se puede definir como private. El modificador de acceso por defecto, o de paquete, hace visibles los miembros de una clase al resto de clases dentro del mismo paquete. Una clase puede definirse como de paquete. El modificador de acceso protected, se comporta exactamente igual que el modificador por defecto pero adem´s permite que las clases hijas de la clase a protected puedan usar sus miembros a trav´s de la herencia aunque estas clases e hijas pertenezcan a paquetes distintos. El modificador de acceso public asigna la mayor visibilidad a los miembros de una clase. Un miembro p´blico es accesible desde cualquier otra clase sin u importar el paquete en el que est´ definido, o si existe una relaci´n padre-hija e o entre ellas. La Tabla 3.1 muestra todas las posibilidades de acceso entre miembros de clases. En la Figura 3.1 su muestra gr´ficamente las posibilidades seg´n el modifi- a u cador de acceso empleado. A partir de la versi´n 5 de Java se introdujo el concepto de import static o con la intenci´n de facilitar la escritura de c´digo. La idea de los import static o o
  • 69. 70 CAP´ ITULO 3. HERENCIA E INTERFACES ¿Es accesible? private paquete protected public Misma clase SI SI SI SI Clase/subclase del paquete NO SI SI SI Subclase otro paquete NO NO SI 3 SI Clase otro paquete NO NO NO SI Tabla 3.1: Modificadores de acceso y su visibilidadFigura 3.1: Visibilidad de los miembros seg´n el modificador de acceso utilizado. u
  • 70. 3.7. CLASES E INTERFACE ANIDADOS 71 es incluir una clase o un paquete de clases y poder llamar a los miembros est´ti- a cos de las clases importadas sin necesidad de escribir el nombre de la clase, tal y como se muestra en el siguiente Listado:1 // D e f i n i c i ´ n de una c l a s e o2 package p a q u e t e . s u b p a q u e t e ;34 public c l a s s C l a s e {5 public s t a t i c void metodo ( ) {6 // D e f i n i c i ´ n d e l m´ todo o e7 }8 }910 // D e f i n i c i ´ n de o t r a c l a s e o11 import s t a t i c p a q u e t e . s u b p a q u e t e . C l a s e ;1213 public c l a s s ClaseQueUsaImports {14 public void otroMetodo ( ) {15 metodo ( ) ; Los import static son un arma de doble filo, por un lado facilitan la codifi- caci´n, pero por otro se pierde la perspectiva de la pertenencia de los miembros o static a sus clases concretas. Hay que utilizarlos con precauci´n. Un caso de o uso comunmente extendido es en las pruebas unitarias, para incluir los miembros est´ticos de los frameworks como JUnit . a 3.7. Clases e interface anidados Hasta este momento hemos definido cada una de nuestras clases en un fichero independiente con extensi´n .java. No obstante, en un mismo fichero podemos o definir m´s de una clase siempre que s´lo una de ellas sea public y su nombre a o coincida con el del fichero. El ´mbito o visibilidad del resto de clases debe ser el a paquete (recuerda, visibilidad por defecto). Aunque lo aconsejable es que, con independencia del ´mbito, cada clase est´ definida en un fichero distinto, ya que a e esto favorece el mantenimiento del c´digo. o Si embargo, dentro de la definici´n de una clase podemos definir nuevas o clases e interface como se muestra en el siguiente Listado:1 public c l a s s P e r s o n a {2 private S t r i n g nombre ;3 private S t r i n g a p e l l i d o s ;4 private D i r e c c i o n d i r e c c i o n ;5 private c l a s s D i r e c c i o n {6 private S t r i n g c a l l e ;7 private i n t numero ;8 private S t r i n g p u e r t a ;9 private S t r i n g p o b l a c i o n ;10 private S t r i n g p r o v i n c i a ;11 }1213 public i n t e r f a c e L e e r D a t o s {14 public S t r i n g getNombre ( ) ;15 } Listado 3.9: Uso de una enumeraci´n o A la clase Direccion as´ definida se le llama clase interna y, a efectos de ı programaci´n es una nueva clase como cualquier otra. De igual modo interface o LeerDatos es un interface como otro cualquiera.
  • 71. 72 CAP´ ITULO 3. HERENCIA E INTERFACES Hay un caso particular de creaci´n de clases internas en el que la nueva clase o no recibe ning´n nombre como se muestra en el siguiente Listado, continuaci´n u o del anterior:17 public L e e r D a t o s l e c t o r = new L e e r D a t o s ( ) {18 private P e r s o n a p e r s o n a ;1920 @Override21 public S t r i n g getNombre ( ) {22 return nombre ;23 } Listado 3.10: Uso de una enumeraci´n o F´ ıjate que en la l´ ınea 17 parece que se est´ intentando instanciar un a interface, cosa que como sabes no est´ permitida. Lo que est´ ocurriendo a a es que se est´ creando e instanciando una nueva clase sin nombre, y por lo tanto a an´nima, que est´ implementando el interface LeerDatos. Se est´ creando o a a e instanciando la clase interna an´nima al mismo tiempo, este es el unico mo- o ´ mento en el que se puede instanciar una clase interna an´nima, ya que por ser o an´nima no tienen nombre y por lo tanto no podemos definir sus constructores. o Las clases internas an´nimas son una construcci´n muy potente del lenguaje. o o Veremos toda su potencia en el Cap´ ıtulo 11 dedicado a la creaci´n de interfaces o gr´ficos de usuario, y en el Cap´ a ıtulo 14 dedicado a la programaci´n concurrente o con hilos. Cuestiones. 1. ¿Tiene sentido declarar los constructores de una clase como private? ¿Se podr´n crear instancias de una clase en la que todos sus constructores son a private? ¿Se podr´ extender una clase en la que todos sus constructores a son private?. Ejercicios. 1. Modifica tu implementaci´n de la clase Agenda que escribiste en el ejercicio o 3 del Cap´ıtulo 2, para que pueda trabajar, de modo transparente, tanto con instancias de tipo Persona como con instancias de tipo Empresa. 2. Amplia la clase Persona para que contenga informaci´n sobre la direcci´n o o de residencia de la persona. 3. Amplia la clase Empresa para que contenga informaci´n sobre la direcci´n o o de la sede de la empresa. 4. Modifica tu agenda para que sea capaz de trabajar con los nuevos tipos de datos definidos.
  • 72. 3.7. CLASES E INTERFACE ANIDADOS 73Lecturas recomendadas. El cap´ ıtulo 7 del libro de Sierra y Bates [3] expone gr´ficamente todos los a conceptos relacionados con la herencia en Java. Para una exposici´n m´s detallada sobre clases e interface anidados una o a excelente referecia es el cap´ ıtulo 5 del libro de James Gosling [2]. Para un fundamentado razonamiento de porqu´ favorecer la composici´n e o sobre la herencia v´ase el item 14 de la referencia [4]. e
  • 73. 74 CAP´ ITULO 3. HERENCIA E INTERFACES
  • 74. Cap´ ıtulo 4Control de versiones conSubversionContenidos 4.1. ¿Qu´ es un sistema de control de versiones? e . . . 76 4.2. Principales caracter´ ısticas de Subversion . . . . . 76 4.3. Creaci´n de un repositorio . . . . . . . . . . . o . . . 77 4.4. Trabajo con repositorios . . . . . . . . . . . . . . . 78 4.4.1. Obteniendo informaci´n del repositorio . . . o . . . . 82 4.5. Integraci´n con Eclipse . . . . . . . . . . . . . o . . . 84Introducci´n oEste es el primer cap´ ıtulo dedicado a una herramienta utilizada en el desarrollode proyectos inform´ticos y no directamente al lenguaje de programaci´n Java. a o Como ya se coment´ en la introducci´n, el objetivo de este libro es mostrar o oc´mo desarrollar proyectos inform´ticos con tecnolog´ Java en un contexto o a ıade desarrollo lo m´s cercano posible al que el programador va a encontrar en acualquier organizaci´n de desarrollo de software. Lo que hace realmente valioso oa un programador no es s´lo que conozca lenguajes de programaci´n, si no que o oconozca las herramientas de desarrollo de software m´s utilizadas y que, desde un aprincipio, se integre suavemente en un equipo de desarrollo. Y esta integraci´notiene mucho que ver con toda la experiencia que posea con el trabajo en grupoy las herramientas que lo favorecen. En este cap´ıtulo se presenta la herramienta de control de versiones Subver-si´n, que, incluso trabajando individualmente, se hace imprescindible en todo oproyecto para la gesti´n de las diferentes versiones del proyecto a medida que oeste evoluciona. 75
  • 75. 76 CAP´ ITULO 4. SUBVERSION4.1. ¿Qu´ es un sistema de control de versiones? eEl escenario m´s usual en el desarrollo de software es un equipo formado por avarios programadores, probablemente coordinado por un jefe de proyecto. Seacual sea la metodolog´ que se utilice en el desarrollo del proyecto, el c´digo va ıa oa estar sujeto a continuas modificaciones por parte de todos los programadoresdel equipo. En este escenario, no es raro encontrar que dos programadores hanmodificado el mismo fragmento de c´digo de modo que se llegue a conflictos ocuando se quiere unificar el c´digo del proyecto. Otra necesidad del equipo es ogarantizar que todos los programadores pueden disponer de la ultima versi´n ´ odel c´digo del proyecto. o Un Sistema de Control de versiones es una herramienta software que, demanera autom´tica, se encarga de facilitar la gesti´n de las versiones del c´digo a o ode un proyecto de manera centralizada.4.2. Principales caracter´ ısticas de SubversionSubversion es una herramienta centralizada de ayuda al control de versiones.Su uso no es exclusivo en el desarrollo de proyectos inform´ticos, si no que apuede utilizarse en cualquier proyecto que requiera de un sistema autom´tico ade control de versiones. El concepto central en Subversion es el Repsositorio . Por repositorio seentiende la ultima versi´n del proyecto que existe en el sistema de control de ´ oversiones. El paradigma que Suversion utiliza es Copia-Modificaci´n-Fusi´n (Copy- o oModify-Merge en ingl´s). En este paradigma, cada uno de los miembros del eequipo, cuando empieza a trabajar en el proyecto, hace una copia local delcontenido del repositorio; modifica su copia local y finalmente fusiona sus modi-ficaciones locales con el c´digo del repositorio, resolviendo los posibles conflicto oque hayan aparecido. Al finalizar esta fase, se dice que se ha creado una nuevaversi´n del proyecto en el repositorio. o Una de las caracter´ıstica principales de Subversion es que las actualizacionesen el repositorio son incrementales, s´lo se actualizan los ficheros que se han omodificado con respecto a la versi´n anterior. Otra caracter´ o ıstica es relativa a lanumeraci´n de la versi´n del repositorio, cada vez que se realiza una modificaci´n o o oen el repositorio, se actualiza la versi´n de todos los ficheros existentes en el orepositorio y no unicamente de los ficheros que se han modificado. ´ Por otro lado, se puede trabajar con Subversion de manera local sobre elpropio sistema de ficheros donde se realiza el desarrollo, o sobre un servidor enred. Y en este ultimo caso, el servidor utilizado puede ser el propio servidor ad- ´hoc que viene incluido con la distribuci´n de Subversion (svnserve), o como un om´dulo de Apache. La elecci´n del modo de trabajo con Subversion se ver´ re- o o aflejada en la URL que utilizaremos para acceder al repositorio. Dependiendo delprotocolo utilizado, las opciones son las que aparecen en la Tabla 4.1. En la primera de las opciones de la Tabla 4.1 se accede directamente alrepositorio en el sistema de ficheros. En la segunda de las opciones se accedeutilizando el servidor ad-hoc que viene incluido en la propia distribuci´n de oSubversion. En la tercera opci´n se utiliza Subversion a trav´s de un t´nel ssh. o e uLa cuarta opci´n permite el acceso a un repositorio a trav´s de Apache y el o e
  • 76. ´4.3. CREACION DE UN REPOSITORIO 77 file:/// El repositorio se encuentra en el disco local. El acceso al repositorio se realiza a trav´s del servidor e svn:// svnserve. El acceso al repositorio se realiza a trav´s del servidor e svn+ssh:// svnserve utilizando un t´nel SSH u El acceso al repositorio se realiza a trav´s de Apache con el e http:// m´dulo WebDAV. o El acceso al repositorio ser realiza con encriptaci´n SSL a o https:// trav´s de Apache con el m´dulo WebDAV. e o Tabla 4.1: Tipos de acceso a los repositorios Subversion.m´dulo WebDAV (del ingl´s Web-based Distributed Authoring and Versioning). o eFinalmente, en la ultima opci´n se accede al respositorio a trav´s de un servidor ´ o eApache con encriptaci´n ssl (del ingl´s Secure Socket Layer ). o e Cada una de estas opciones tiene sus ventajas y desventajas. En las pr´ximas osecciones utilizaremos el protocolo svn:// para acceder a un repositorio a trav´s edel servidor svnserve. El trabajo con Subversion es independiente del protocoloutilizado.4.3. Creaci´n de un repositorio oLa creaci´n de un nuevo repositorio se hace utilizando la herramienta svnadmin oincluida en la distribuci´n de Subversion. Supongamos que hemos creado el di- orectorio ./Repositorio (directorio Repositorio en la ra´ de nuestro directo- ızrio de usuario), en nuestro disco duro local. Para crear un repositorio Subversionen este directorio, en una consola escribir´ ıamos:~$ svnadmin create ~./Repositorio Si examinamos el contenido del directorio veremos que se han creado lossiguiente subdirectorios y ficheros dentro del directorio ./Repositorio:drwxr-xr-x 8 oscar staff 272 23 may 18:48 .drwxr-xr-x 32 oscar staff 1088 23 may 18:48 ..-rw-r--r-- 1 oscar staff 229 23 may 18:48 README.txtdrwxr-xr-x 5 oscar staff 170 23 may 18:48 confdrwxr-sr-x 16 oscar staff 544 23 may 18:48 db-r--r--r-- 1 oscar staff 2 23 may 18:48 formatdrwxr-xr-x 11 oscar staff 374 23 may 18:48 hooksdrwxr-xr-x 4 oscar staff 136 23 may 18:48 locks El fichero README.txt contiene un aviso sobre c´mo debe ser usado este odirectorio, s´lo a trav´s de las herramientas que proporciona Subversion. El o edirectorio hooks contiene scripts b´sicos para el trabajo con Subversion. El di- arectorio locks es utilizado por Subversion para los bloqueos del repositorio. Eldirectorio db es el que emplea Subversion para registrar todos los cambios reali-zados en el contenido del repositorio, es el coraz´n del repositorio. Finalmente, oel directorio conf es donde se encuentran los ficheros de configuraci´n para el oacceso al servidor de Subversion.
  • 77. 78 CAP´ ITULO 4. SUBVERSION A efectos pr´cticos, el directorio donde vamos a realizar tareas de configu- araci´n es conf. Su contenido es el siguiente: odrwxr-xr-x 5 oscar staff 170 23 may 18:48 .drwxr-xr-x 8 oscar staff 272 23 may 18:48 ..-rw-r--r-- 1 oscar staff 1080 23 may 18:48 authz-rw-r--r-- 1 oscar staff 309 23 may 18:48 passwd-rw-r--r-- 1 oscar staff 2279 23 may 18:48 svnserve.conf En el fichero svnserve.conf indicamos las opciones de acceso al repositorio,en particular podemos restringir los permisos de lectura y escritura para cadauno de los usuarios a cada uno de los directorios que contiene nuestro repositorioa trav´s de lo especificado en el fichero authz, y los usuarios y claves de acceso eal repositorio en el fichero passwd. Como ejemplo unicamente vamos a especificar los usuarios y sus claves en ´el fichero passwd sin modificar el fichero authz, lo que implica que todos losusuarios dados de alta en el fichero passwd tendr´n acceso total a todos los adirectorios y ficheros del repositorio. Para activar la opci´n de acceso a trav´s de usuario autorizado hay que o edescomentar la l´ınea:password-db = passwdesta l´ ınea indica el nombre del fichero de pares usuario/clave para nuestro re-positorio. Despu´s de descomentar esta l´ e ınea debemos editar el fichero passwdy a˜adir los usuario y sus claves siguiendo el ejemplo que encontraremos en ´l: n e# harry = harryssecret# sally = sallyssecretoscar = clave_secreta Con esto ya tenemos activa la configuraci´n m´s b´sica para nuestro repo- o a asitorio, al que s´lo puede acceder el usuario oscar con permisos de lectura y oescritura para todos los directorios y ficheros del repositorio. El siguiente paso antes de empezar a trabajar con nuestro repositorio esiniciar el servidor de Subversion del siguiente modo:~$ sudo svnserve --daemon Es necesario tener permisos de administrador para iniciar el servidor deSubversion como un proceso daemon. A partir de ahora ya podemos empezar a trabajar con nuestro repositorio.4.4. Trabajo con repositoriosSupongamos que, por claridad, elegimos nombrar al directorio que va a mante-ner nuestra copia de trabajo como . /CopiaTrabajo (en Eclipse nuestra copiade trabajo ser´ algo como . /workspace/NombreProyecto, no es necesario que alos nombre del proyecto y del repositorio coincidan). El primer paso que de-bemos dar es importar el contenido del directorio CopiaTrabajo al repositorioSubversion, suponiendo que nos encontramos en el directorio CopiaTrabajo, deeste modo:
  • 78. 4.4. TRABAJO CON REPOSITORIOS 79~/CopiaTrabajo$ svn import . svn://localhost/home/oscar/Repositorio/trunk -m Import inicial del proyecto El . corresponde al directorio actual, y la direcci´n que aparece a continua- oci´n svn://localhost/home/oscar/Repositorio/trunk corresponde a la di- orecci´n donde se encuentra el repositorio. Finalmente -m ¨mport inicial del o Iproyecto es un mensaje descriptivo de lo que estamos haciendo para que, aposteriori, resulte c´modo encontrar una determinada versi´n del proyecto. En o oeste momento se solicitar´ la clave del usuario que hemos activado en el fichero ade configuraci´n passwd. o Te habr´s dado cuenta de que estamos a˜adiendo el subdirectorio trunk al a nrepositorio, esto forma parte de las buenas pr´cticas de trabajo con Subversion. a Buenas pr´cticas aEn nuestro directorios Subversion es recomendable crear los siguiente subdirec-torios:trunk: Directorio donde se encuentra la versi´n en desarrollo del proyecto. obranches: Directorio donde se encuentran las posibles ramificaciones del pro- yecto.tags: Directorio donde se encuentran las versiones finales (releases). Para que los ficheros y directorios en CopiaTrabajo se conviertan en unacopia de trabajo real, necesitamos hacer el checkout del repositorio hacia nues-tra copia de trabajo de este modo (f´ıjate en el punto final ((.)) para indicar queel checkout lo queremos hacer sobre el directorio actual):~/CopiaTrabajo$ svn checkout svn://localhost/home/oscar/Repositorio/trunk . En este momento tenemos sincronizada la copia en el repositorio con nuestracopia de trabajo. Para seguir la estructura t´ ıpica de un proyecto Eclipse creemosel directorio src para contener los fuentes del proyecto. Cualquier modificaci´noen el directorio de trabajo la tenemos que hacer utilizando Subversion, luego, enla consola debemos escribir lo siguiente para crear un directorio que Subversionpueda sincronizar con el repositorio:~/CopiaTrabajo$ svn mkdir srcA src Subversion nos indicar´ que se ha a˜adido (A) el directorio src a la copia a nlocal. Ahora, dentro del directorio creado en el paso anterior creamos un ficherode definici´n de clase llamado Saludo.java, y le indicamos a Subverion que lo oa˜adimos a nuestra copia local: n~/CopiaTrabajo$ touch src/Saludo.java~/CopiaTrabajo$ svn add Saludo.javaA src/Saludo.java
  • 79. 80 CAP´ ITULO 4. SUBVERSION En este momento el fichero Saludo.java no se ha enviado al repositorio,s´lo se ha marcado para que la pr´xima vez que se haga un commit este fichero o ose a˜ada efectivamente al Repositorio. n Lo siguiente que debemos hacer es subir al repositorio todos los cambios quehemos hecho en nuestra copia local, de este modo:~/CopiaTrabajo$ svn commit -m A~adiendo la clase Saludo al nrepositorio Observa que es necesario a˜adir un mensaje descriptivo de lo que estamos nhaciendo con la opci´n -m ”Texto del mensaje”, si lo olvidas, Subversion te lo opedir´. La respuesta que ver´s en consola por parte de Subversion ser´ parecida a a aa esta:A~adiendo n srcA~adiendo n src/Saludo.javaTransmitiendo contenido de archivos .Commit de la revisi´n 2. o Donde se nos informa que la ultima versi´n disponible en el repositorio es la 2. ´ oA la ultima versi´n se le llama HEAD. Ahora realiza alg´n peque˜o cambio en el ´ o u nfichero Saludo.java, como a˜adir un simple comentario y graba tu fichero. Para nconocer el estado de tu copia local con respecto a la ultima versi´n existente en ´ oel Repositorio puedes utilizar la instrucci´n status con la opci´n -v tecleando o oen consola:~/CopiaTrabajo$ svn status -v 1 1 oscar . 2 2 oscar srcM 2 2 oscar src/Saludo.java Este texto nos informa que el fichero Saludo.java se ha modificado (letrainicial M), y en el pr´ximo commit se subir´ la versi´n local al repositorio, la o a oopci´n -v significa verbose, es decir queremos una descripci´n detallada del o oestado de la copia local. Veamos el resultado de un commit escribiendo:~/CopiaTrabajo$ svn commit -m Una modificaci´n en el fichero oSaludo.javaEnviando src/Saludo.javaTransmitiendo contenido de archivos .Commit de la revisi´n 3. o Este texto nos informa, de nuevo, que no ha habido ning´n problema al subir ula nueva copia local al Repsositorio. En un equipo de desarrollo cualquier otro programador puede realizar cam-bios sobre alg´n fichero en el Repositorio, o puede a˜adir nuevos ficheros y u ndirectorios al repositorio, para conocer en todo momento cual es el estado denuestra copia local con respecto a la ultima versi´n existente en el Repositorio ´ opodemos escribir:~/CopiaTrabajo$ svn status -u* 3 src/Saludo.javaEstado respecto a la revisi´n: 5 o
  • 80. 4.4. TRABAJO CON REPOSITORIOS 81 La opci´n -u indica que se compare nuestra versi´n local con respecto a o ola ultima versi´n en el Repositorio, si no la incluimos la comparaci´n se rea- ´ o olizar´ entre la copia local y la ultima versi´n que nos descargamos desde el a ´ oRepositorio que puede no ser la versi´n HEAD del Repositorio. El * indica que el ofichero Saludo.java en mi copia de trabajo est´ en la versi´n 3 mientras que a oen el repositorio la ultima versi´n es la 5. Si queremos conocer cual es el estado ´ olocal de nuestros fichero con respecto de la ultima actualizaci´n escribimos: ´ o~/CopiaTrabajo$ svn diffIndex: src/Saludo.java==============================================--- src/Saludo.java (revisi´n: 3) o+++ src/Saludo.java (copia de trabajo)@@ -1,2 +1,3 @@ public class Saludo {+ // Un comentario } No newline at end of file Si vemos un signo ((+)) al inicio de la l´ ınea significa que esa l´ ınea se ha a˜adido ncon respecto de la ultima actualizaci´n que hicimos (que no tiene porqu´ coin- ´ o ecidir con la ultima versi´n que existe en el repositorio). Si la l´ ´ o ınea empieza conun signo ((-)) indica que esa l´ınea sea ha eliminado. Si ahora intentamos hacerun commit obtendremos el siguiente error:~/CopiaTrabajo$ svn commit -m Intentando un commit que fallar´ aEnviando src/Saludo.javaTransmitiendo contenido de archivos .svn: Fall´ el commit o(detalles a continuaci´n): osvn: El archivo ’/trunk/src/Saludo.java’ est´ desactualizado a Este error se debe a que nuestra copia local se encuentra en la versi´n 3 y ola ultima versi´n en el repositorio es la 5, luego es necesario que primero actua- ´ olicemos nuestra copia local a la ultima versi´n en el repositorio, y en segundo ´ olugar que enviemos los cambios. Para actualizar nuestra copia local a la ultima ´versi´n del Repositorio (HEAD) escribimos: o~/CopiaTrabajo$ svn updateG svn/Saludo.javaActualizado a la revisi´n 5. o Esta vez, la letra G al inicio de la l´ ınea indica que Subversion ha sido capazde mezclar (merge en ingles) la ultima revisi´n existente en el Repositorio con ´ olos cambios locales en nuestro fichero y no ha encontrado conflictos. Si dos pro-gramadores no modifican las mismas l´ ıneas de c´digo, si no que las discrepancias oaparecen el l´ ıneas de c´digo distintas, no aparecer´ ning´n conflicto cuando Sub- o a uversion intente mezclar el c´digo de nuestra copia local con el de la copia en el oRepositorio. En este caso tenemos, en nuestra copia local, la ultima versi´n en ´ oel Repositorio m´s nuestros cambios que se han a˜adido sin conflicto, ahora ya a npodemos hacer de nuevo un commit:
  • 81. 82 CAP´ ITULO 4. SUBVERSION~/CopiaTrabajo$ svn commit -m Ya actualizado subo mis cambiosEnviando src/Saludo.javaTransmitiendo contenido de archivos .Commit de la revisi´n 6. o Ahora s´ que ha tenido ´xito el commit puesto que la versi´n de nuestra copia ı e olocal coincide con la ultima versi´n en el repositorio. ´ o En otras ocasiones, cuando una l´ ınea ha sido modificada por dos o m´s aprogramadores, Subversion no sabr´ c´mo resolver el conflicto por s´ solo, y en a o ıel momento de intentar hacer un update seremos informados de que existe unconflicto, como en el caso siguiente:~/CopiaTrabajo$ svn diffIndex: src/Saludo.java================================================--- src/Saludo.java (revisi´n: 7) o+++ src/Saludo.java (copia de trabajo)@@ -1,4 +1,7 @@ @author Oscar public class Saludo {+ .mine+======= // Un comentario, un poco largo+ .r7 } No newline at end of file En este mensaje se nos informa que hay un conflicto ya que nuestra copialocal contiene la l´ ınea del comentario que ha sido eliminada en el repositorio, dehecho, las l´ ıneas extra que aparecen en el c´digo se han a˜adido realmente al o nfichero Saludo.java. En este caso debemos resolver el conflicto a mano, y unavez resuelto (por ejemplo, eliminando todas las l´ıneas insertadas y manteniendoel comentario) se lo indicamos a Subversion del siguiente modo:~/CopiaTrabajo$ svn resolved Saludo.javaSe resolvi´ el conflicto de ’src/Saludo.java’ o De nuevo, podemos seguir trabajando con nuestro repositorio como hasta elmomento o hasta que aparezca un nuevo conflicto.4.4.1. Obteniendo informaci´n del repositorio oSin ´nimo de ser exhaustivos con respecto a las posibilidad para obtener infor- amaci´n sobre un repositorio Subversi´n, aqu´ mostramos algunas de las opciones o o ıpara conocer el estado del repositorio y de nuestra copia local. Subversion nosproporciona instrucciones para conocer en cualquier momento informaci´n sobre oel repositorio. Con svn log obtenemos informaci´n sobre los mensajes que fueron adjun- otados con cada nuevo commit, tal y como se muestra a continuaci´n: o
  • 82. 4.4. TRABAJO CON REPOSITORIOS 83--------------------------------------------------------------r2 | oscar | 2010-05-17 09:44:03 +0200 (lun, 17 may 2010) | 1 lineC´digo del cap´tulo Clases. o ı--------------------------------------------------------------r1 | oscar | 2010-05-17 09:43:33 +0200 (lun, 17 may 2010) | 1 lineInitial import.-------------------------------------------------------------- Si estamos interesados en alguna revisi´n en particular, podemos indicarlo ocon la opci´n -r como en el siguiente ejemplo: ocaterva:LibroJava oscar$ svn log -r 10--------------------------------------------------------------r10 | oscar | 2010-06-25 10:31:51 +0200 (vie, 25 jun 2010) | 1 linePara el Cap´tulo de Entrada/Salida. ı-------------------------------------------------------------- Para conocer el estado del repositorio podemos usar svn list. De nuevo, siestamos interesados en el estado del repositorio para una determinada revisi´n opodemos utilizar la opci´n -r tal y como se muestra en el siguiente ejemplo: ocaterva:LibroJava oscar$ svn list -r 3clases/herencia/ Si lo que nos interesa es conocer el estado de las ultimas modificaciones de ´nuestra copia local con respecto al repositorio, podemos utilizar la instrucci´n osvn status, con lo que obtendremos informaci´n del modo siguiente: o? Punto.java! Nuevo.javaA Punto3D.javaC Principal.javaD PruebaPunto.javaM Ejemplo.javaL Hola.java La letra may´scula de la primera columna, antes del nombre de cada fichero, ues un c´digo que nos indica el estado del fichero o directorio con el siguiente osignificado: En la tabla 4.2, el ultimo c´digo indica que el fichero ha quedado bloqueado. ´ oEsta situaci´n puede ocurrir cuando, por ejemplo, al intentar hacer un commit ola conexi´n con el repositorio remoto se pierde y no se puede acabar el env´ Al o ıo.realizar cambios en la copia local, Subversion va acumulando en una lista todaslas tareas pendientes. En el momento en que se desea sincronizar la copia localcon la remota, se bloquean los ficheros que se van a sincronizar. Si por algunaraz´n alguna de las tareas pendientes no se puede llevar a cabo, el resultado oser´ que alg´n fichero puede quedar bloqueado. a u
  • 83. 84 CAP´ ITULO 4. SUBVERSION ? El fichero no se est´ a˜adido al repositorio a n ! No existe una copia local de este fichero A El fichero se ha marcado para a˜adir al repositorio n C Existen conflictos entre la copia local y el repositorio D El fichero se ha marcado para ser borrado del repositorio M Hay modificaciones en la copia local del fichero L El fichero est´ bloqueado aTabla 4.2: C´digos con informaci´n sobre el estado de la copia local del fichero o osobre la copia remota. Para eliminar los bloqueos podemos utilizar la instrucci´n svn cleanup, oesta instrucci´n comprueba el listado de tareas a realizar, y si queda alguna opendiente, intenta realizarla, al final de lo cual, se eliminar´ el bloqueo sobre alos ficheros. Finalmente, si lo que nos interesas conocer con detalle son los cambios produ-cidos en los ficheros entre la copia local y la existente en el repositorio podemosutilizar svn diff.4.5. Integraci´n con Eclipse oAunque el trabajo con Subversion desde l´ ınea de instrucciones es bastante sen-cillo, resulta interesante no tener que abandonar Eclipse para trabajar con elRepositorio de nuestro proyecto. Como se coment´ en la Secci´n 1.4.1, se puede o oa˜adir nueva funcionalidad a Eclipse instalando nuevos plug-ins, y precisamente nexisten excelentes plug-ins para trabajar con Subversion desde Eclipse. Uno deellos es Subclipse, (http://subclipse.tigris.org/), aunque existe otros ex-celentes plug-ins como Subversive (http://www.eclipse.org/subversive/).Elegir entre uno u otro acaba siendo cuesti´n de gustos ya que todos ellos son oexcelentes, la mejor idea es probar algunos de ellos y quedarnos con el que m´s ase adapte a nuestra forma de trabajo. La ultima versi´n del plug-in Subclipse puedes encontrarla en la direcci´n ´ o ohttp://subclipse.tigris.org. En la secci´n Dowload and Install encontrar´s o ala ultima release y la URL desde donde instalar el plug-in. ´ Para instalar un plug-in 1 selecciona la opci´n Help → Install new Software ose abrir´ una ventana con una secci´n Work with donde debes introducir la a oURL del plug-in Subclipse 2 . Al pulsar Enter te aparecer´n todos los plug-ins adisponibles en esa direcci´n, aunque algunos de ellos son opcionales, conviene oinstalarlos todos, as´ que marca todos ellos y pulsa el bot´n Next. En la siguien- ı ote ventana se te mostrar´n a modo de informaci´n todos los paquetes que se a oinstalar´n. Pulsa de nuevo Next. En la siguiente ventana se te pide que aceptes alos t´rminos de la licencia, para ello selecciona la opci´n I accept the terms and e oof the license agreements y pulsa el bot´n Finish, ver´s como todos los paquetes o ade clases necesarios para el nuevo plug-in se descargan hacia tu m´quina. Hacia ael final de la instalaci´n se abrir´ una ventana de advertencia indic´ndote que o a a 1 Estas instrucciones son v´lidas para la versi´n 3.5 (Galileo) y la 3.6 (Helios) de Eclipse a o 2 En el momento de escribir este libro la ultima versi´n de este plug-in es la 1.6 y su URL ´ oes http://subclipse.tigris.org/update_1.6.x
  • 84. ´4.5. INTEGRACION CON ECLIPSE 85parte del software que est´s instalando no est´ firmado, por esta vez pulsa la a atecla OK para seguir con la instalaci´n. Finalmente se te pedir´ que para aca- o abar con la instalaci´n est´ recomendado reiniciar Eclipse, pulsa el bot´n Yes y o a oEclipse se reiniciar´ y ya podr´s usar el plug-in reci´n instalado. a a e Ahora, dispondr´s de una nueva Perspectiva que te permitir´ visualizar tus a arepositorios Subversion. Para abrir esta nueva Perspectiva, selecciona la opci´n ode men´ de Eclipse Window → Show perspective → Other.... Al hacerlo se uabrir´ una ventana, en ellaselecciona SVN Repository Exploring. Dentro de esa anueva Perspectiva puedes abrir una nueva vista con la opci´n de men´ Window o u→ Show View → SVN Repositories. Finalmente, se abrir´ una Vista con el at´ ıtulo SVN Repositories que inicialmente estar´ vac´ a ıa. En esta nueva vista es donde puedes a˜adir conexiones a los repositorios nSubversion que quieras. Como ejemplo, vamos a crear una conexi´n al reposito- orio local que tenemos en svn://localhost/home/oscar/Repositorio/trunk(esta direcci´n no tiene por qu´ coincidir con la que hayas elegido t´), para o e uello, sobre la vista SVN Repositories pulsa el bot´n derecho de tu rat´n y en o oel men´ emergente que aparecer´ selecciona New → Repository Location, se u aabrir´ una ventana solicit´ndote la URL del repositorio al que te quieres co- a anectar, introduce svn://localhost/home/oscar/Repositorio, f´ ıjate que nohemos introducido trunk al final de la URL, pulsa Finish, ver´s que la nueva aURL te aparece en la vista SVN Repositories, despliega la URL y ver´s bajo ella ael directorio trunk y bajo ´l, el fichero Saludo.java. Para hacer el chekout del erepositorio selecciona trunk con el bot´n derecho y en el men´ contextual que o ute aparecer´ selecciona Checkout... se abrir´ una nueva ventana con la opci´n a a oseleccionada por defecto Check out as a project configured using New ProjectWizard, pulsa Finish, y en la nueva ventana despliega la opci´n Java y selec- ociona la opci´n Java Project, a partir de aqu´ el Wizard es el que ya conoces o ı,y utilizas cada vez que quieres crear un nuevo proyecto, as´ que simplemente ıintroduce un nombre para el nuevo proyecto y pulsa Finish. Al final de este proceso tendr´s una copia local del proyecto en Eclipse so- abre la que puedes trabajar tal y como trabajas sobre cualquier otro proyectoEclipse, con la enorme ventaja de que todo el trabajo con el repositorio puedeshacerlo desde el propio Eclipse. Cada vez que queramos ver las discrepanciasentre nuestra copia local y la existente en el repositorio, es decir la informaci´n oque en consola obten´ ıamos con svn diff, ahora la podemos obtener, de ma-nera gr´fica, pulsado el bot´n derecho del rat´n sobre el proyecto en Eclipse y a o oeligiendo del men´ contextual Team → Synchronize with repository, pasaremos ua la perspectiva Team Synchronization donde nos aparecer´ toda la informaci´n a ode sincronizaci´n con el repositorio. La interpretaci´n de los iconos es la que se o omuestra en la Tabla 4.3. Si existe conflicto en alguno de nuestros ficheros de c´digo podemos abrir una oVista donde se nos muestra, al mismo tiempo, el estado de nuestra copia localy el estado del fichero en el repositorio, para que podamos resolver los conflictosc´modamente. Una vez resueltos los conflictos pulsamos el bot´n derecho del o orat´n y elegimos Mark as Merged para indicar que hemos resuelto los conflictos oy que nuestra copia local est´ lista para ser subida al repositorio (commit), asolo nos restar´ hacer bot´n derecho sobre el nombre del fichero y seleccionar a oCommit..., se abrir´ una ventana para introducir un comentario para el commit ay pulsamos OK.
  • 85. 86 CAP´ ITULO 4. SUBVERSION La versi´n del repositorio es m´s actual o a Flecha azul hacia la izquierda que la copia local. La versi´n local es m´s actual que la o a Flecha gris hacia la derecha existente en el repositorio. Existe un conflicto que Subversion no Doble flecha roja sabe resolver entre la copia local y la existente en el repositorio. Tabla 4.3: Significado de los iconos en la perspectiva Team SynchronizationLecturas recomendadas. La referencia obligada para conocer cualquier aspecto de Subversion es [7]. Existe una versi´n gratuita que se puede descargar desde http:// o subversion.apache.org/. Otra referencia m´s compacta es el cap´ a ıtulo 4 del la referencia [13], donde se detallan los puntos principales para empezar a trabajar con Subversion.
  • 86. Cap´ ıtulo 5ExcepcionesContenidos 5.1. ¿Qu´ es una excepci´n? . . . . . . . . . . . . . e o . . . 87 5.1.1. Tipos de excepciones . . . . . . . . . . . . . . . . . . 88 5.2. C´mo se gestiona una excepci´n . . . . . . . o o . . . 88 5.3. Creaci´n de excepciones propias . . . . . . . o . . . 91Introducci´n oEs evidente que lo deseable es que no se produzcan errores durante la ejecu-ci´n de un programa. A todos nos provoca rechazo utilizar herramientas que ofallan, ya sean herramientas software o cualquier otro tipo de herramientas.En el cap´ıtulo 6 veremos la t´cnica de Test unitarios para comprobar nuestro esoftware con objeto de eliminar el mayor n´mero posible de errores durante la ufase de desarrollo de nuestras aplicaciones. No obstante, durante la ejecuci´n de onuestras aplicaciones existir´n situaciones an´malas susceptibles de provocar un a omal funcionamiento de nuestro software. Piensa por ejemplo en el caso en que unusuario intenta guardar un fichero en un directorio protegido contra escritura. Existen lenguajes de programaci´n, como C++, que proporcionan un meca- onismo opcional de reacci´n frente a estas situaciones an´malas, pero el progra- o omador no est´ obligado a utilizarlo, es como se ha dicho, una opci´n. a o En Java existe un mecanismo de reacci´n ante situaciones an´malas muy pa- o orecido al de C++ con la gran diferencia de que el programador s´ que est´ obli- ı agado a usarlo en aquellas situaciones susceptibles de provocar un error, lo queconduce a la producci´n de c´digo m´s robusto frente a este tipo de fallos en o o atiempo de ejecuci´n. o5.1. ¿Qu´ es una excepci´n? e oEn Java, una excepci´n es una situaci´n an´mala en tiempo de ejecuci´n. Piensa o o o oen el ejemplo de la introducci´n en el que un usuario intenta guarda un fichero oen un directorio protegido contra escritura. Piensa tambi´n en el acceso a una e 87
  • 87. 88 CAP´ ITULO 5. EXCEPCIONES posici´n fuera de un array est´tico. Todo este tipo de situaciones se producen o a en tiempo de ejecuci´n, y aunque podemos estar prevenidos contra ellas, no o podemos evitar completamente que vayan a ocurrir. 5.1.1. Tipos de excepciones En Java existen tres grandes grupos de excepciones dependiendo de su natura- leza: 1. Excepciones de la propia m´quina virtual. Estas excepciones causadas a por un mal funcionamiento de la propia m´quina virtual (S´ la m´quina a ı, a virtual Java tambi´n es una pieza de software y como tal est´ sujeta a e a fallos). Este tipo de errores, por su naturaleza, son ajenos al programador y por lo tanto no estamos obligados a gestionarlos. Si este tipo de errores se produce durante la ejecuci´n de nuestras aplicaciones puede ocurrir o que nuestra aplicaci´n se cierre y veamos un mensaje de error de la propia o m´quina virtual. Pero quede el lector tranquilo, es extremadamente dif´ a ıcil encontrarse con un error de esta naturaleza. En la Figura 5.1 la clase Error es la clase padre de todo este grupo de excepciones. 2. El siguiente grupo de situaciones excepcionales son aquellas tan comunes c´mo intentar acceder a una posici´n inexistente de un array est´tico; o o o a intentar hacer un casting incompatible sobre una variable de tipo referen- cia. El c´digo donde se puede dar este tipo de situaciones es tan com´n o u que a˜adir m´s c´digo para gestionarlas sobrecargar´ terriblemente la es- n a o ıa critura de nuestros programas, por lo que no es necesario gestionar este tipo de excepciones, aunque si queremos siempre lo podemos hacer. En la Figura 5.1 la clase RunTimeException es la clase padre de este grupo de excepciones. 3. El tercer y ultimo tipo de excepciones est´ formado por el resto de situacio- ´ a nes que no son las anteriores, como por ejemplo, de nuevo, intentar escribir en un directorio protegido contra escritura. Este es el tipo de excepciones que s´ estamos obligados a gestionar y para los que Java proporciona un ı potente mecanismo de gesti´n. En la Figura 5.1 la clase Exception es la o clase padre de este grupo de excepciones. 5.2. C´mo se gestiona una excepci´n o o Java proporciona un mecanismo de gesti´n de errores a trav´s de los bloques o e try...catch...finally, tal y como se muestra en el siguiente Listado:1 try {2 F i c h e r o f = a b r e F i c h e r o ( nombre ) ; // Q u i z a s e l f i c h e r o no e x i s t a ´3 S t r i n g l i n e a = f . l e e L i n e a ( ) ; // Q u i z ´ s s e p r o d u z c a un e r r o r d u r a n t e l a a lectura4 } catch ( FileNotFoundException e ) {5 // C´ digo de r e c u p e r a c i o n d e l e r r o r o ´6 System . o u t . p r i n t l n ( e . g e t M e s s a g e ( ) ) ; // Muestra una d e s c r i p c i ´ n d e l o error7 } catch ( IOException e ) {8 // C´ digo de r e c u p e r a c i o n d e l e r r o r o ´9 } finally {
  • 88. ´ ´ 5.2. COMO SE GESTIONA UNA EXCEPCION 89 Figura 5.1: Parte del ´rbol de jerarqu´ de las excepciones en Java. a ıa10 // C´ digo com´ n o u11 }12 // O t r a s l´ n e a s de c ´ d i g o ı o13 System . o u t . p r i n t l n ( ” Aqu´ s i g u e l a ı e j e c u c i ´ n ”) ; o Listado 5.1: Ejemplo bloque try{...} catch{...} finally{...} En el ejemplo anterior, el m´todo de la l´ e ınea 2 est´ intentando abrir a un fichero, y una posible situaci´n an´mala es que el fichero no exista o o (FileNotFoundException) lo que puede provocar un error. En la l´ ınea 3 se est´ intentando leer una l´ a ınea desde el fichero que s´ est´ abierto, lo que tam- ı a bi´n puede provocar alg´n tipo de error de entrada/salida. e u Antes de pasar a ver la t´cnica para gestionar estas situaciones an´malas, e o f´ ıjate que cuando se produce un error, lo que recibe el bloque catch{...} es una referencia del tipo del error correspondiente, que, entre otras cosas, lleva una descripci´n sobre el error que se produjo. Recuerda, todo en Java es un objeto o y en particular los errores en tiempo de ejecuci´n son instancias de clases que o representan errores. La t´cnica para gestionar esta situaci´n es: e o 1. Encerrar en un bloque try{...} el o los m´todos susceptibles de provocar e un error. 2. Atrapar en bloques catch{...} separados, cada uno de los posibles errores que se pueden producir en el bloque try{...}. 3. Opcionalmente podemos definir un bloque finally{...} que se ejecu- tar´ con independencia de que se genere alguna de las excepciones gestio- a nadas o no. Es decir si existe el bloque finally{...} su c´digo siempre o se ejecutara se produzca o no una excepci´n. o En el listado 5.1, si en la l´ ınea 2 se produce un error porque no se encontrase el fichero que se desea abrir, la siguiente l´ ınea de c´digo 3 no se ejecutar´ la o ıa,
  • 89. 90 CAP´ ITULO 5. EXCEPCIONES ejecuci´n pasar´ directamente al bloque catch(FileNotFoundException e), o ıa y tras su ejecuci´n al bloque finally de la l´ o ınea 10, ya que tenemos definido uno. Despu´s de la gesti´n del error, la ejecuci´n seguir´ en la l´ e o o a ınea 13. Si tanto el c´digo en el bloque finally{...} como el c´digo posterior a la gesti´n o o o de la excepci´n se ejecuta (c´digo en la l´ o o ınea 13), ¿Qu´ sentido tiene incluir e el bloque finally{...}?. Antes de contestar a esta pregunta veamos c´mo o podemos obviar la gesti´n de una excepci´n en el momento en que esta se o o produce y delegar su gesti´n en el m´todo que invoc´ al actual en la pila de o e o llamadas. Existen casos es los que nos interesa delegar la gesti´n de la excepci´n, la o o excepci´n ocurre en la definici´n de un determinado m´todo pero no queremos o o e a˜adir el c´digo de gesti´n de la excepci´n en la definici´n de ese m´todo, ¿C´mo n o o o o e o indicamos que un m´todo no va a gestionar una determinada excepci´n?, con e o el uso de la palabra reservada throws como en el siguiente listado:1 public void metodo ( ) throws F i l e N o t F o u n d E x c e p t i o n {2 // Aqu´ l a d e f i n i c i ´ n d e l m´ todo ı o e3 } En este caso estamos delegando la gesti´n de la excepci´n al m´todo que o o e llam´ a este otro, que es quien delega la gesti´n de la excepci´n. o o o La palabra reservada throws tambi´n se utiliza para lanzar excepciones pro- e pias tal y como vamos a ver en la Secci´n 5.3. o Ahora ya podemos contestar a la pregunta sobre la utilidad del bloque finally{...} observando el ejemplo del Listado 5.2. Si se produjese la excep- ci´n IOException en la l´ o ınea 4 durante el proceso de lectura, se abandonar´ ıa la ejecuci´n del m´todo delegando la gesti´n de estas excepciones al m´todo o e o e que invoc´ a este (ejecuta()). La l´ o ınea 6 nunca se ejecutar´ y el fichero que- ıa dar´ abierto, su referencia perdida al salir del m´todo y no podr´ ıa e ıamos cerrar el fichero.1 // E s t e m´ todo d e l e g a l a g e s t i o n de l a s e x c e p c i ´ n F i l e N o t F o u n d E x c e p t i o n e ´ o y IOException2 private void e j e c u t a ( ) throws F i l e N o t F o u n d E x c e p t i o n , I O E x c e p t i o n {3 F i l e R e a d e r f r = new F i l e R e a d e r ( f i c h e r o . t x t ) ;4 int c a r a c t e r = f r . read ( ) ;5 System . o u t . p r i n t l n ( c a r a c t e r : + c a r a c t e r ) ;6 fr . close () ;7 } Listado 5.2: Tanto la excepci´n FileNotFoundException como IOException se o delegan En el ejemplo del Listado 5.3, tanto si se produce una excepci´n, como si o no se produce, el bloque finally{...} siempre se ejecutar´ y el fichero se a cerrar´ en cualquier caso, se produzca o no una excepci´n. a o1 // E s t e m´ todo d e l e g a l a g e s t i o n de l a s e x c e p c i ´ n F i l e N o t F o u n d E x c e p t i o n e ´ o y IOException2 private void e j e c u t a ( ) throws F i l e N o t F o u n d E x c e p t i o n , I O E x c e p t i o n {3 FileReader f r = null ;4 int c a r a c t e r = 0 ;5 try {6 f r = new F i l e R e a d e r ( f i c h e r o . t x t ) ;7 c a r a c t e r = f r . read ( ) ;8 } finally {
  • 90. ´ 5.3. CREACION DE EXCEPCIONES PROPIAS 919 System . o u t . p r i n t l n ( c a r a c t e r : + c a r a c t e r ) ;10 i f ( f r != n u l l ) f r . c l o s e ( ) ;11 }12 } Listado 5.3: En este caso el fichero siempre se gracias al uso del bloque finally{...} Otro detalle importante es el orden de los errores que se atrapan en los bloques catch{...}. Compara el orden del ejemplo del Listado 5.1. F´ ıjate que el orden en los bloques try{...} catch{...} finally{...} va desde el m´s espec´ a ıfico (FileNotFoundException) al m´s general (IOException). Pien- a sa qu´ ocurrir´ si se intercambiase el orden, la clase padre aparecer´ en el e ıa ıa primer bloque catch{...} de modo que tanto si se produjera una excepci´n deo tipo IOException como si fuera de tipo FileNotFoundException ambas pro- vocar´ la ejecuci´n del bloque catch{IOException} ya que este bloque atrapa ıa o referencias de la clase padre y cualquier clase hija. Recuerda, las excepciones tambi´n son instancias de objetos, y por lo tanto sobre ellas es v´lido todo lo e a que aprendimos sobre herencia en el cap´ıtulo 3. 5.3. Creaci´n de excepciones propias o En el desarrollo de nuestras propias aplicaciones resulta interesante poder lan- zar excepciones propias ante situaciones inesperadas. Java proporciona un me- canismo para definir nuevas excepciones y lanzarlas en los casos en los que se produzcan situaciones an´malas. o El mecanismo para definir y lazar excepciones propias es el siguiente: 1. Definir la nueva clase que representa a nuestra excepci´n. o 2. Lanzar la excepci´n en las situaciones an´malas. o o 3. Gestionar la excepci´n como cualquier otra. o Al utilizar este mecanismo estamos creando excepciones que son tratadas del mismo modo que las excepciones predefinidas en Java. Veamos cada uno de estos pasos con un ejemplo. Supongamos que queremos generar una excepci´n o si se solicita una posici´n no v´lida dentro de nuestra aplicaci´n de la Agenda. o a o Lo primero que debemos hacer es definir la clase que representa a nuestra nueva excepci´n. El detalle que debemos tener en cuenta es que nuestra excepci´n o o debe ser hija de la clase Exception, tal y como se muestra en el Listado 5.4:1 public c l a s s T e m p e r a t u r a N o V a l i d a E x c e p t i o n extends E x c e p t i o n {2 public T e m p e r a t u r a N o V a l i d a E x c e p t i o n ( ) {3 super ( L a t e m p e r a t u r a n o p u e d e s e m e n o r q u e - 2 7 3 o C ) ;4 }5 } Listado 5.4: Definici´n de una excepci´n propia o o F´ıjate que el constructor por defecto llama a super del padre con un String que es una descripci´n del error que ha ocurrido. Este String se podr´ recuperar o a en el bloque catch{...} correspondiente como veremos m´s adelante. a El siguiente paso es lanzar la excepci´n en caso de producirse una situaci´n o o an´mala, como en el Listado 5.5 : o
  • 91. 92 CAP´ ITULO 5. EXCEPCIONES1 public c l a s s C o n v e r s o r T e m p e r a t u r a s {2 private f i n a l double CERO ABSOLUTO = − 2 7 3 . 1 5 ;34 public C o n v e r s o r T e m p e r a t u r a s ( ) {5 super ( ) ;6 }78 public double c e l s i u s A F h a r e n h e i t ( double c e l s i u s ) throws TemperaturaNoValidaException {9 i f ( c e l s i u s CERO ABSOLUTO) throw new T e m p e r a t u r a N o V a l i d a E x c e p t i o n ( ) ;10 return 9 . 0 / 5 . 0 ∗ c e l s i u s + 3 2 . 0 ;11 }1213 public double c e l s i u s A R e a m u r ( double c e l s i u s ) throws TemperaturaNoValidaException {14 i f ( c e l s i u s CERO ABSOLUTO) throw new T e m p e r a t u r a N o V a l i d a E x c e p t i o n ( ) ;15 return 4 . 0 / 5 . 0 ∗ c e l s i u s ;16 }17 } Listado 5.5: Definici´n de una excepci´n propia o o Cabe resaltar un par de cosas del Listado 5.5, la primera es que el m´todoe desde el que se lanza la excepci´n indica que va a hacerlo con el uso de la palabra o reservada throws seguida del nombre de la excepci´n. La segunda es que para o lanzar la excepci´n utilizamos la palabra reservada throw y creamos una nueva o instancia de la excepci´n con new, recuerda que una excepci´n al fin y al cabo o o no es m´s que una instancia de una clase. a Nuestra excepci´n se gestiona como cualquier otra ya definida en el paquete o est´ndar de Java, mediante el bloque try{...} catch{...} finally{...}, tal a y como se muestra en el Listado 5.6. En la l´ ınea 7 de este listado se muestra c´mo recuperar el texto descriptivo de la excepci´n que proporcionamos en la o o definici´n de la clase TemperaturaNoValidaException. Un m´todo util para o e ´ recuperar toda la traza de ejecuci´n de nuestro programa hasta el momento o en el que se produzco la excepci´n es printStackTrace() definido en la clase o Throwable que es la clase de la que heredan todas las excepciones en Java.1 f o r ( i n t c e l s i u s = 0 ; c e l s i u s 1 0 1 ; c e l s i u s += 5 ) {2 try {3 fharenheit = conversor . celsiusAFharenheit ( c e l s i u s ) ;4 reamur = c o n v e r s o r . c e l s i u s A R e a m u r ( c e l s i u s ) ;5 System . o u t . p r i n t l n ( c e l s i u s + t + f h a r e n h e i t + t + reamur ) ;6 } catch ( T e m p e r a t u r a N o V a l i d a E x c e p t i o n e ) {7 System . o u t . p r i n t l n ( e . g e t M e s s a g e ( ) ) ;8 }9 } Listado 5.6: Definici´n de una excepci´n propia o o Lecturas recomendadas El cap´ ıtulo 8 de la referencia [2] presenta todos los detalles de la gesti´n o de excepciones y c´mo crear excepciones propias. o
  • 92. Cap´ ıtulo 6Pruebas unitarias con JUnitContenidos 6.1. ¿Qu´ son las pruebas unitarias? . . . . . . . . . . . e 94 6.1.1. Principios FIRST para el dise˜o de pruebas unitarias 94 n 6.2. Pruebas unitarias con JUnit . . . . . . . . . . . . . 95 6.2.1. Creaci´n de clases de prueba . . . . . . . . . . . . . 95 o 6.2.2. La anotaci´n @Test . . . . . . . . . . . . . . . . . . 96 o 6.2.3. Las anotaciones @Before y @After . . . . . . . . . . 98 6.2.4. Las anotaciones @BeforeClass y @AfterClass . . . . 99 6.2.5. Pruebas con bater´ de datos de entrada . . . . . . . 100 ıa 6.2.6. Ejecutar varias clases de prueba. Test Suites . . . . 101 6.3. Cobertura de las pruebas . . . . . . . . . . . . . . . 102 6.3.1. EclEmma y su plug-in para Eclipse . . . . . . . . . . 103Introducci´n oLlegados a este punto ya somos capaces de escribir aplicaciones Java utilizandolos principios de la POO. Sabemos c´mo definir clases, como utilizar la herencia oo la composici´n para ampliar el comportamiento de nuestras clases. Incluso osomos capaces de controlar las situaciones an´malas que pueden darse durante ola ejecuci´n de nuestras aplicaciones a trav´s de la gesti´n de excepciones. o e o El siguiente paso que usualmente se suele dar es comprobar la validez delc´digo realizando pruebas. A veces, estas pruebas son caseras, probamos unos ocuantos valores de entrada en nuestra aplicaci´n, valores de los que conocemos ocual es la salida esperada, y confiamos que en el resto de casos nuestro c´digo oest´ libre de errores. Y en este momento es cuando la confianza se convierte en eenga˜o, nuestro c´digo est´ plagado de errores que no hemos sido capaces de n o adetectar y que tarde o temprano saldr´n a la luz sumi´ndonos en la oscuridad, a eparadojas de la vida. La primera idea que debemos fijar es que hacer pruebas de c´digo no debe oser una opci´n, es un requerimiento, por defecto, en todo desarrollo de proyectos oinform´ticos. a 93
  • 93. 94 CAP´ ITULO 6. PRUEBAS UNITARIAS CON JUNIT La segunda idea que debemos fijar es que las pruebas de c´digo no deben oser manuales, si no automatizadas. Si son manuales, por aburrimiento o falta detiempo acabaremos por no hacerlas. Las pruebas automatizadas forman partedel c´digo del proyecto, son tan importantes como el c´digo que se est´ probando o o ay por lo tanto debemos dedicarles el mismo empe˜o que al desarrollo del c´digo n ode nuestra aplicaci´n. o En este cap´ ıtulo no se va a mostrar c´mo dise˜ar buenas pruebas de c´digo, o n oy lo que es m´s importante, no se va a mostrar c´mo escribir c´digo que se pueda a o oprobar f´cilmente, en la secci´n de referencias de este cap´ a o ıtulo se dan algunost´ ıtulos que son de lectura obligada a todo desarrollador que quiera poner enpr´ctica la prueba de c´digo en sus proyectos. a o En este cap´ıtulo se va a mostrar c´mo utilizar una herramienta para com- oprobar c´digo. Esta excelente herramienta es JUnit. o6.1. ¿Qu´ son las pruebas unitarias? eLas pruebas unitarias se realizan sobre una clase, para probar su comportamien-to de modo aislado, independientemente del resto de clases de la aplicaci´n. Este orequisito a veces no se cumple, piensa en el c´digo de una clase que accede a ouna base de datos, y que la prueba de la clase se base en el resultado que serecupera de la base de datos, resulta imposible comprobar esta clase de modoaislado, aunque existen t´cnicas (como los Mock Objects) que minimizan estas edependencias.6.1.1. Principios FIRST para el dise˜ o de pruebas unita- n riasCuando se dise˜an pruebas unitarias es importante seguir los principios FIRST. nCada una de las letras de esta palabra inglesa est´ relacionada con un concepto. aVe´moslos: aFast : La ejecuci´n del c´digo de pruebas debe ser r´pida. Si las pruebas con- o o a sumen demasiado tiempo acabaremos por no hacerlas.Independent : Una prueba no puede depender de otras. Cada prueba debe ser unitaria, debe poder realizarse de modo aislado.Repetable : Las pruebas se deben poder repetir en cualquier momento y la cantidad de veces que sea necesario. El resultado de una prueba debe ser siempre el mismo.Self-validating : S´lo hay dos posibles resultados de una prueba: ((La prueba o pas´ con ´xito)) o ((La prueba fall´)). o e oTimely : Las pruebas han de escribirse en el momento de escribir el c´digo, y o no al final de toda la fase de desarrollo 1 1 La metodolog´ de desarrollo Test Driven Development (TDD) lleva este principio al ıainicio del proceso de desarrollo de tal modo que las pruebas de c´digo se escriben antes que oel propio c´digo que se intenta probar. o
  • 94. 6.2. PRUEBAS UNITARIAS CON JUNIT 95 6.2. Pruebas unitarias con JUnit JUnit es una herramienta para realizar pruebas unitarias automatizadas. JUnit est´ integrada en Eclipse, no es necesario descargarse ning´n paquete ni instalar a u un nuevo plug-in para utilizarla. Eclipse facilita la creaci´n de pruebas unitarias. o Para mostrar con un ejemplo c´mo se escriben pruebas unitarias de o c´digo con JUnit vamos a utilizar las clases ConversorTemperaturas y o TemperaturaNoValidaException que vimos en el cap´ ıtulo anterior. 6.2.1. Creaci´n de clases de prueba o Para crear una clase de prueba en Eclipse seleccionamos File → New → Ot- her..., en la ventana de di´logo que se abrir´ seleccionamos Java → JUnit → a a JUnit Test Case, se abrir´ una nueva ventana de di´logo, en la parte supe- a a rior seleccionamos New JUnit 4 test, introducimos el nombre para la clase de prueba y su paquete, y por comodidad, en la parte inferior de esta ventana pulsamos Browse y seleccionamos la clase que deseamos probar, que en nuestro caso es ConversorTemperaturas, y pulsamos Next, en la nueva ventana vere- mos que se nos ofrece la posibilidad de seleccionar los m´todos que queremos e probar, en nuestro caso vamos a seleccionar celsiusAFharenheit(double) y celsiusAReamur(double) y pulsamos Finish. La clase de prueba que se crear´ au- a tom´ticamente ser´ la que se muestra en el Listado 6.1. a a Cabe destacar varios puntos de este Listado: 1. F´ ıjate en el uso del import static de la l´ ınea 3, es util para no tener ´ que incluir el nombre de la clase Assert cuando utilizamos alguno de sus m´todos est´ticos, como fail. Un import static me permite utilizar e a todos los m´todos static de una clase sin necesidad de anteponer al e m´todo el nombre de la clase 2 . e 2. Observa que se han creado dos m´todos de prueba, una para cada m´todo e e que seleccionamos sobre la clase a probar, y que la signatura de ambos es public void nombreDelMetodo(). Los m´todos de prueba deben ser p´ blicos no e u retornar ning´n valor y su lista de argumentos debe estar vac´ u ıa. 3. F´ ıjate que sobre cada uno de los m´todos de prueba aparece la anotaci´n e o @Test que indica al compilador que es un m´todo de prueba. e 4. Por defecto, cada uno de los m´todos de prueba tiene una llamada a e fail(Mesaje con descripci´n.. o1 package t e s t ;23 import s t a t i c o r g . j u n i t . A s s e r t . ∗ ;45 import o r g . j u n i t . T e s t ;67 public c l a s s T e s t C o n v e r s o r T e m p e r a t u r a s { 2 Los static import se introdujeron en la versi´n 5 de Java, y aunque son c´modos de o o utilizar, el uso de JUnit es un caso, pueden provocar confusi´n en el programador, ya que o al no aparecer el nombre de la clase tendremos la duda de si el m´todo pertenece a la clase e actual o a un clase de la que se ha hecho un static import. En general, el uso de los static import est´ desaconsejado. a
  • 95. 96 CAP´ ITULO 6. PRUEBAS UNITARIAS CON JUNIT89 @Test10 public f i n a l void t e s t C e l s i u s A F h e r e n h e i t ( ) {11 f a i l ( Not yet implemented ) ;12 }1314 @Test15 public f i n a l void t e s t C e l s i u s A R e a m u r ( ) {16 f a i l ( Not yet implemented ) ;17 }1819 } Listado 6.1: C´digo generado autom´ticamente por Eclipse para una clase de o a prueba. 6.2.2. La anotaci´n @Test o Como ya se ha dicho, la anotaci´n @Test sirve para indicar que un determinado o m´todo es un m´todo de prueba. Vamos a escribir el primer c´digo de prueba e e o tal y como se muestra en el Listado 6.2. F´ ıjate que tambi´n hemos a˜adido e n throws TemperaturaNoValidaException para indicar que no queremos gestionar esta posible excepci´n en el c´digo del m´todo de prueba. o o e1 @Test2 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throws TemperaturaNoValidaException {3 C o n v e r s o r T e m p e r a t u r a s c o n v e r s o r = new C o n v e r s o r T e m p e r a t u r a s ( ) ;4 as ser tEq ual s (32 , conversor . celsiusAFharenheit (0) , 0) ;5 } Listado 6.2: Un m´todo de prueba. e Lo primero que hacemos en el m´todo de prueba es crear una instancia e de la clase ConversorTemperaturas, y despu´s utilizar el m´todo assertEquals( e e valorEsperado, valorObtenido, error). Este m´todo comprueba que la diferencia entre el e valorEsperado y el valorObtenido es menor que error. Si es as´ se ha pasado ı, la prueba, de lo contrario la prueba falla. En nuestro caso, se est´ aseverando a que la diferencia entre el valor que devuelve el m´todo celsiusAFharenheit(0) e y el valor 32 es cero. Escribamos el c´digo para la segunda de las pruebas tal y o como se muestra en el Listado 6.3.1 @Test2 public void t e s t C e l s i u s A R e a m u r ( ) throws T e m p e r a t u r a N o V a l i d a E x c e p t i o n {3 C o n v e r s o r T e m p e r a t u r a s c o n v e r s o r = new C o n v e r s o r T e m p e r a t u r a s ( ) ;4 a s s e r t E q u a l s ( 0 , c o n v e r s o r . celsiusAReamur ( 0 ) , 0) ;5 } Listado 6.3: Un segundo m´todo de prueba. e De modo an´logo al primer m´todo de prueba, en este caso estamos ase- a e verando que la diferencia entre el valor que devuelve la llamada al m´todo e celsiusAReamur(0) y el valor 0 es cero. Para ejecutar las pruebas desde Eclipse pulsa el bot´n derecho del rat´n o o sobre la clase de prueba y en el men´ emergente selecciona la opci´n Run As → u o JUnit Test, ver´s que se abre una nueva vista con el resultado de la ejecuci´n a o de las pruebas, que en nuestro caso es Runs: 2/2 Errors: 0 Failures: 0 que nos
  • 96. 6.2. PRUEBAS UNITARIAS CON JUNIT 97 indica que se han realizado 2 pruebas, ninguna de ellas a provocado un error y ninguna de ellas a provocado un fallo. ¿Cual es la diferencia entre un fallo y un error en el contexto de las prue- bas unitarias con JUnit?. Un fallo es una aseveraci´n que no se cumple, un o error es una excepci´n durante la ejecuci´n del c´digo. Generemos un fallo de o o o modo artificial para ver el resultado, cambiemos la l´ ınea assertEquals(32, conversor. celsiusAFharenheit(0), 0); por esta otra assertEquals(0, conversor.celsiusAFharenheit(0), 0); , y ejecutemos de nuevo la prueba, en este caso obtendremos un fallo que nos in- formar´ que el valor esperado de la prueba era 0 mientras que el valor obtenido a es 32.0. A˜adamos otro m´todo de prueba que genere un error, para ver la diferencia n e con un fallo, tal y como se muestra en el Listado 6.4.1 public void t e s t T e m p e r a t u r a N o V a l i d a F h a r e n h e i t ( ) throws TemperaturaNoValidaException {2 C o n v e r s o r T e m p e r a t u r a s c o n v e r s o r = new C o n v e r s o r T e m p e r a t u r a s ( ) ;3 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;4 } Listado 6.4: Un m´todo de prueba que genera un error. e Al ejecutar de nuevo las pruebas esta vez obtendremos la excepci´n La tem- o peratura no puede ser menor que -273o C. ¿Y si lo que queremos es precisa- mente comprobar que se lanza la excepci´n?, es decir, ¿Y si nuestra prueba o pasa precisamente si se genera la excepci´n? Para ello basta con a˜adir el atri- o n buto expected=TemperaturaNoValidaException.class a la anotaci´n @Test o quedando de este modo @Test(expected=TemperaturaNoValidaException.class). Si ejecu- tamos de nuevo las pruebas veremos que todas ellas pasan. Otra t´cnica que no utiliza el atributo expected de la anotaci´n @Test pa- e o ra comprobar que se produce una excepci´n es la mostrada en el Listado 6.5. o Esta vez el m´todo est´ etiquetado unicamente con @Test, y detr´s de la l´ e a ´ a ınea de c´digo que esperamos que produzca la excepci´n escribimos fail(Para o o temperaturas por encima de -273 la prueba debe pasar.). Si la excep- ci´n se produce al ejecutarse la l´ o ınea 5, la ejecuci´n de la prueba continuar´ en o a el bloque catch (TemperaturaNoValidaException e) y la prueba pasar´, que a es lo que esperamos. Si no se produce la excepci´n en la l´ o ınea 5, se ejecutar´ la sentencia fail(...) a y la prueba no pasar´, cosa que ser´ indicativa de que algo ha ido mal ya lo que a a intentamos probar es que la excepci´n s´ que se produce. o ı1 @Test2 public void t e s t T e m p e r a t u r a N o V a l i d a d F a h r e n h e i t ( ) {3 C o n v e r s o r T e m p e r a t u r a s c o n v e r s o r = new C o n v e r s o r T e m p e r a t u r a s ( ) ;4 try {5 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;6 f a i l ( Para t e m p e r a t u r a s por encima de -273 la prueba debe pasar . ) ;7 } catch ( T e m p e r a t u r a N o V a l i d a E x c e p t i o n e ) {8 }9 } Listado 6.5: Un m´todo de prueba que genera un error. e Este segundo m´todo de prueba de excepciones es el recomendado, ya que e es m´s f´cil interpretar qu´ es lo que se est´ intentando probar. a a e a
  • 97. 98 CAP´ ITULO 6. PRUEBAS UNITARIAS CON JUNIT 6.2.3. Las anotaciones @Before y @After Si revisas el c´digo de los tres m´todos de prueba anteriores ver´s que lo primero o e a que hacemos es crear una instancia de la clase ConversorTemperaturas. JUnit nos proporciona un mecanismo para extraer el c´digo que se repite en todos o los m´todos, y que debe ejecutarse antes de cualquiera de ellos, a trav´s de las e e anotaciones @Before y @After. Si anotamos un m´todo con @Before su c´digo e o ser´ ejecutado antes de cada uno de los m´todos de prueba, si tenemos tres a e m´todos de prueba ser´ ejecutado antes de cada uno de los tres m´todos. Por su e a e lado, si anotamos un m´todo con @After ser´ ejecutado despu´s de la ejecuci´n e a e o de cada uno de los m´todos de prueba. Por lo tanto, podemos usar la anotaci´n e o @Before para iniciar todas las infraestructuras necesarias a la ejecuci´n de las o pruebas y la anotaci´n @After para limpiar estas infraestructuras. o En nuestro caso, la clase de prueba quedar´ tal y como se muestra en el ıa Listado 6.6.1 import s t a t i c o r g . j u n i t . A s s e r t . ∗ ;23 import o r g . j u n i t . A f t e r ;4 import o r g . j u n i t . B e f o r e ;5 import o r g . j u n i t . T e s t ;67 import c o n v e r s o r . C o n v e r s o r T e m p e r a t u r a s ;8 import c o n v e r s o r . T e m p e r a t u r a N o V a l i d a E x c e p t i o n ;910 public c l a s s T e s t C o n v e r s o r T e m p e r a t u r a s 2 {11 private C o n v e r s o r T e m p e r a t u r a s c o n v e r s o r ;1213 @Before14 public void c r e a C o n v e r s o r T e m p e r a t u r a s ( ) {15 c o n v e r s o r = new C o n v e r s o r T e m p e r a t u r a s ( ) ;16 }1718 @After19 public void d e s t r u y e C n v e r s o r T e m p e r a r t u r a s ( ) {20 conversor = null ;21 }2223 @Test24 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throws TemperaturaNoValidaException {25 as ser tEq ual s (32 , conversor . celsiusAFharenheit (0) , 0) ;26 }2728 @Test29 public void t e s t C e l s i u s A R e a m u r ( ) throws T e m p e r a t u r a N o V a l i d a E x c e p t i o n {30 a s s e r t E q u a l s ( 0 , c o n v e r s o r . celsiusAReamur ( 0 ) , 0) ;31 }3233 @Test ( e x p e c t e d=T e m p e r a t u r a N o V a l i d a E x c e p t i o n . c l a s s )34 public void t e s t T e m p e r a t u r a N o V a l i d a F h a r e n h e i t ( ) throws TemperaturaNoValidaException {35 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;36 }37 } Listado 6.6: Uso de las anotaciones @Before y @After. Las anotaciones @Before y @After las puedes utilizar tantas veces como te sea necesario, puede haber m´s de un m´todo anotado con alguna de estas a e anotaciones. Todos los m´todos que est´n anotados con @Before se ejecutar´n e e a antes de cada uno de los m´todos de prueba y todos los m´todos que est´n e e e anotados con @After se ejecutar´n despu´s de cada uno de los m´todos de a e e
  • 98. 6.2. PRUEBAS UNITARIAS CON JUNIT 99 prueba. 6.2.4. Las anotaciones @BeforeClass y @AfterClass Podemos mejorar un poco m´s nuestra clase de prueba con el uso de dos nuevas a etiquetas @BeforeClass y @AfterClass. F´ ıjate que la clase que estamos pro- bando ConversorTemperaturas no tiene estado, y por lo tanto el resultado de las llamadas a sus m´todos es independiente del orden en el que se hagan, por lo e que no es necesario crear una instancia nueva antes de cada una de las pruebas, si no que la misma instancia nos sirve para las tres pruebas. Si anotamos un m´todo de una clase de prueba con @BeforeClass ese m´to- e e do se ejecutar´ una unica vez antes de la ejecuci´n de cualquier m´todo de a ´ o e prueba. Por otro lado, si anotamos un m´todo de una clase de prueba con e @AfterClass el m´todo ser´ ejecutado una unica vez despu´s de haberse ejecu- e a ´ e tado todos los m´todos de prueba, tal y como se muestra en el Listado 6.7. e1 import s t a t i c o r g . j u n i t . A s s e r t . a s s e r t E q u a l s ;23 import o r g . j u n i t . A f t e r C l a s s ;4 import o r g . j u n i t . B e f o r e C l a s s ;5 import o r g . j u n i t . T e s t ;67 import c o n v e r s o r . C o n v e r s o r T e m p e r a t u r a s ;8 import c o n v e r s o r . T e m p e r a t u r a N o V a l i d a E x c e p t i o n ;910 public c l a s s T e s t C o n v e r s o r T e m p e r a t u r a s 3 {11 private s t a t i c C o n v e r s o r T e m p e r a t u r a s c o n v e r s o r ;1213 @BeforeClass14 public s t a t i c void c r e a C o n v e r s o r T e m p e r a t u r a s ( ) {15 c o n v e r s o r = new C o n v e r s o r T e m p e r a t u r a s ( ) ;16 }1718 @AfterClass19 public s t a t i c void d e s t r u y e C n v e r s o r T e m p e r a r t u r a s ( ) {20 conversor = null ;21 }2223 @Test24 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throws TemperaturaNoValidaException {25 as ser tEq uals (32 , conversor . celsiusAFharenheit (0) , 0) ;26 }2728 @Test29 public void t e s t C e l s i u s A R e a m u r ( ) throws T e m p e r a t u r a N o V a l i d a E x c e p t i o n {30 a s s e r t E q u a l s ( 0 , c o n v e r s o r . celsiusAReamur ( 0 ) , 0) ;31 }3233 @Test ( e x p e c t e d=T e m p e r a t u r a N o V a l i d a E x c e p t i o n . c l a s s )34 public void t e s t T e m p e r a t u r a N o V a l i d a F h a r e n h e i t ( ) throws TemperaturaNoValidaException {35 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;36 }37 } Listado 6.7: Uso de las anotaciones @BeforeClass y @AfterClass. F´ıjate en el importante detalle que aparece en el Listado 6.7, los m´todos e anotados con @BeforeClass y @AfterClass deben ser ambos static y por lo tanto, los atributos a los que acceden tambi´n deben ser static, tal y como e vimos en 2.6.
  • 99. 100 CAP´ ITULO 6. PRUEBAS UNITARIAS CON JUNIT 6.2.5. Pruebas con bater´ de datos de entrada ıa Cada uno de los m´todos de prueba de los ejemplos anteriores utiliza un tr´ de e ıo datos, valor esperado, valor real y error para comprobar cada uno de los casos de prueba. Si queremos escribir una nueva prueba para otro tr´ de valores es ıo tedioso crear un m´todo s´lo para ´l. JUnit proporciona un mecanismo para e o e probar bater´ de valores en vez de unicamente tr´ aislados. ıas ´ ıos Lo primero que debemos hacer es anotar la clase de prueba con @RunWith(Parameterized.class) indicando que va a ser utilizada para rea- lizar bater´ de pruebas. La clase de prueba ha de declarar un atributo por ıas cada uno de los par´metros de la prueba, y un constructor con tantos argumen- a tos como par´metros en cada prueba. Finalmente necesitamos definir un m´todo a e que devuelva la colecci´n de datos a probar anotado con @Parameters . De este o modo, cada uno de los m´todos de prueba ser´ llamado para cada una de las e a tuplas de valores de prueba. En el Listado 6.8 se muestra un ejemplo de clase de prueba para una bater´ ıa de pruebas sobre la clase ConversorTemperaturas.1 import s t a t i c o r g . j u n i t . A s s e r t . ∗ ;23 import j a v a . u t i l . A r r a y s ;4 import j a v a . u t i l . C o l l e c t i o n ;56 import org . junit . AfterClass ;7 import org . junit . BeforeClass ;8 import org . junit . Test ;9 import org . junit . r u n n e r . RunWith ;10 import org . junit . runners . Parameterized ;11 import org . junit . runners . Parameterized . Parameters ;1213 import c o n v e r s o r . C o n v e r s o r T e m p e r a t u r a s ;14 import c o n v e r s o r . T e m p e r a t u r a N o V a l i d a E x c e p t i o n ;1516 @RunWith ( P a r a m e t e r i z e d . c l a s s )17 public c l a s s T e s t C o n v e r s o r T e m p e r a t u r a s 4 {18 private double c e l s i u s ;19 private double f h a r e n h e i t ;20 private double reamur ;21 private double e r r o r ;22 private s t a t i c C o n v e r s o r T e m p e r a t u r a s c o n v e r s o r ;2324 public T e s t C o n v e r s o r T e m p e r a t u r a s 4 ( double c e l s i u s , double f h a r e n h e i t , double reamur , double e r r o r ) {25 this . c e l s i u s = c e l s i u s ;26 this . fharenheit = fharenheit ;27 t h i s . reamur = reamur ;28 this . e r r o r = e r r o r ;29 }3031 @Parameters32 public s t a t i c C o l l e c t i o n O b j e c t [ ] d a t o s ( ) {33 return A r r a y s . a s L i s t (new O b j e c t [ ] [ ] {34 { 0 . 0 , 3 2 . 0 , 0 . 0 , 0 . 0 } , // { c e l s i u s , f h a r e n h e i t , reamur , error}35 {15 , 5 9 . 0 , 1 2 . 0 , 0 . 0 } ,36 {30 , 8 6 . 0 , 2 4 . 0 , 0 . 0 } ,37 {50 , 122.0 , 4 0 . 0 , 0 . 0 } ,38 {90 , 194.0 , 7 2 . 0 , 0.0}39 }) ;40 }4142 @BeforeClass43 public s t a t i c void i n i c i a C o n v e r s o r ( ) {44 c o n v e r s o r = new C o n v e r s o r T e m p e r a t u r a s ( ) ;45 }4647 @AfterClass
  • 100. 6.2. PRUEBAS UNITARIAS CON JUNIT 10148 public s t a t i c void e l i m i n a C o n v e r s o r ( ) {49 conversor = null ;50 }5152 @Test53 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throws TemperaturaNoValidaException {54 assertEquals ( fharenheit , conversor . celsiusAFharenheit ( c e l s i u s ) , error ) ;55 }5657 @Test58 public void t e s t C e l s i u s A R e a m u r ( ) throws T e m p e r a t u r a N o V a l i d a E x c e p t i o n {59 a s s e r t E q u a l s ( reamur , c o n v e r s o r . c e l s i u s A R e a m u r ( c e l s i u s ) , e r r o r ) ;60 }61 } Listado 6.8: Ejemplo de definici´n de una clase que realiza una bater´ de o ıa pruebas. De modo resumido, estos son los pasos para definir una clase que ejecuta bater´ de pruebas: ıas 1. Anotar la clase de prueba con @RunWith(Parameterized.class). 2. Declarar un atributo en la clase por cada par´metro de prueba. a 3. Definir un constructor con tantos argumentos como par´metros de prueba. a 4. Definir un m´todo que devuelva una colecci´n con todas las tuplas de e o prueba, y anotarlo con Parameters. Internamente y de modo esquem´tico, lo que JUnit hace en el caso de las a bater´ de pruebas es crear una instancia de la clase de prueba a partir del ıas constructor con tantos argumentos como par´metros en cada una de las tuplas a de prueba, y seguidamente llama a cada uno de los m´todos de prueba. e 6.2.6. Ejecutar varias clases de prueba. Test Suites Lo com´n, como hemos visto, es tener varias clases de prueba ya que a veces u no tiene sentido una unica clase donde se realicen todas las pruebas. Piensa ´ por ejemplo en las pruebas parametrizadas, quiz´s tengas alg´n caso de prueba a u sobre el que no tenga sentido realizar pruebas parametrizadas, como por ejemplo comprobar que se produce una excepci´n. o Por lo tanto, si tenemos varias clases de prueba, ¿C´mo podemos ejecutar o todas las pruebas, o al menos algunas de ellas sin tener que ejecutarla cada clase de modo independiente? Para ello existe un mecanismo en JUnit llamado Test Suites, que no son m´s que agrupaciones de clases de prueba que se ejecutan a una tras otra. B´sicamente lo que hacemos es anotar una clase para que JUnit la reconozca a como una suite de pruebas y con otra anotaci´n a˜adimos a esta clase todas las o n clases de prueba que nos interese ejecutar, tal y como se muestra en el Listado 6.9.1 import o r g . j u n i t . r u n n e r . RunWith ;2 import o r g . j u n i t . r u n n e r s . S u i t e ;3 import o r g . j u n i t . r u n n e r s . S u i t e . S u i t e C l a s s e s ;4
  • 101. 102 CAP´ ITULO 6. PRUEBAS UNITARIAS CON JUNIT5 @RunWith ( S u i t e . c l a s s )6 @SuiteClasses ({7 TestConversorTemperaturas . class ,8 TestConversorTemperaturas2 . class ,9 TestConversorTemperaturas3 . class ,10 TestConversorTemperaturas4 . class11 })12 public c l a s s A l l T e s t s {13 } Listado 6.9: Ejemplo de una suite de pruebas Test Suite. De este modo bien podemos ejecutar una unica clase de prueba para probar ´ el funcionamiento correcto de una clase en particular de todas las que forman nuestro proyecto, o bien podemos ejecutar toda la suite de clases de prueba para probar todo nuestro proyecto. 6.3. Cobertura de las pruebas Una duda que nos surge al realizar pruebas unitarias es la cantidad de l´ ıneas de c´digo que han cubierto las pruebas, ¿Ha quedado alg´n fragmento de c´digo o u o que no se ha ejecutado ni una sola vez para todas las pruebas? ¿C´mo podemos o saberlo?. Lo ideal es que nuestras pruebas cubran el 100 % del c´digo de la clase o que estamos probando. Pero no caigas en el enga˜o de pensar que por cubrir n con pruebas el 100 % del c´digo est´s cubriendo todos los casos posibles de la o a ejecuci´n de ese c´digo, ni mucho menos. Puedes cubrir el 100 % de la ejecuci´n o o o del c´digo con casos triviales que nunca fallar´n y no sacar´n a la luz los posibles o a a errores de tu c´digo. o Para que veas con claridad la diferencia entre cobertura de c´digo y pruebas o exhaustivas el Listado 6.10 te muestra un m´todo a probar y un par de m´todos e e de prueba. Los m´todos de prueba cubren el 100 % del c´digo del m´todo que e o e se est´ probando pero, ¿Qu´ pasa si alguna de las referencias que se pasan al a e m´todo de prueba es null? Evidentemente el m´todo que se est´ probando e e a contiene un error que no ser´ descubierto por las pruebas aunque estas est´n a e cubriendo el 100 % del c´digo. o1 // M´todo que s e va a p r o b a r e2 p u b l i c i n t quienEsMayor ( P e r s o n a p r i m e r a , P e r s o n a segunda ) {3 i f ( p r i m e r a . edad segunda . edad ) r e t u r n −1;4 i f ( p r i m e r a . edad segunda . edad ) r e t u r n 1 ;5 e l s e return 0;6 }78 // T r e s m´ todos de prueba e9 @Test10 p u b l i c void masViejoElPrimero ( ) {11 P e r s o n a p r i m e r a = new P e r s o n a ( ) ;12 p r i m e r a . setEdad ( 1 0 0 ) ;13 P e r s o n a segunda = new P e r s o n a ( ) ;14 segunda . setEdad ( 5 0 ) ;15 a s s e r t E q u a l s ( −1 , quienEsMayor ( p r i m e r a , segunda ) , 0 ) ;16 }1718 @Test19 p u b l i c v o i d m as V i e jo E l Se g u nd o ( ) {20 P e r s o n a p r i m e r a = new P e r s o n a ( ) ;21 p r i m e r a . setEdad ( 5 0 ) ;22 P e r s o n a segunda = new P e r s o n a ( ) ;23 segunda . setEdad ( 1 0 0 ) ;
  • 102. 6.3. COBERTURA DE LAS PRUEBAS 10324 a s s e r t E q u a l s ( 1 , quienEsMayor ( p r i m e r a , segunda ) , 0 ) ;25 }2627 @Test28 p u b l i c v o i d mismaEdad ( ) {29 P e r s o n a p r i m e r a = new P e r s o n a ( ) ;30 p r i m e r a . setEdad ( 5 0 ) ;31 P e r s o n a segunda = new P e r s o n a ( ) ;32 segunda . setEdad ( 5 0 ) ;33 a s s e r t E q u a l s ( 0 , quienEsMayor ( p r i m e r a , segunda ) , 0 ) ;34 } Listado 6.10: Los tres m´todos cubren el 100 % del c´digo del m´todo que se e o e est´ probando, pero este m´todo contiene un error ya que no se comprueba que a e las referencias sean distintas de null Existe modelos te´ricos que dan las pautas a seguir para garantizar que las o pruebas son exhaustivas, de modo que se contemplan todos los posibles casos de fallo de nuestro c´digo. La referencia [5] presenta de modo exhaustivo las o pruebas de c´digo que se han de realizar para garantizar que se cubre el 100 % o de los posibles caminos de ejecuci´n. o 6.3.1. EclEmma y su plug-in para Eclipse Afortunadamente existen excelentes herramientas que miden, de modo autom´ti- a co, la cobertura de nuestras pruebas unitarias. Una de esas herramientas, aunque no la unica es Ecl-Emma de la que existe un plug-in para Eclipse y cuya p´gina ´ a web es http://www.eclemma.org/. Para instalar este plug-in basta seguir los mismo pasos que se mostraron en la Secci´n 4.5, pero siendo la direcci´n del o o plug-in la que se indica en la p´gina web de la herramienta. a Una vez instalado este plugin te aparecer´ un nuevo bot´n en Eclipse a la a o izquierda del bot´n ejecutar. Si pulsas este bot´n cuando est´ seleccionada una o o a clase de prueba, se abrir´ una nueva vista de nombre Coverage en la que se te a mostrar´ todos los resultados de la cobertura de la prueba, y lo que es de gran a ayuda, de cada una de las l´ ıneas de c´digo de la clase que se est´ probando o a se colorear´ su fondo en verde, si la l´ a ınea ha sido cubierta completamente por la prueba; amarilla, si ha sido cubierta s´lo parcialmente; o roja, si no ha sido o cubierta. En la vista Coverage se muestra para cada clase probada, una tabla con el porcentaje de l´ıneas de c´digo cubiertas por la prueba. o Sin duda, la herramienta Ecl-Emma y su plugin para Eclipse son excelentes herramientas que contribuyen a aumentar la calidad de nuestro c´digo. o Lecturas recomendadas. Un excelente libro, de autores espa˜oles, donde se trata de un modo com- n pleto las pruebas de software es la referencia [5]. De lectura obligada si se quiere poner en pr´ctica las pruebas de software. a Otra excelente referencia de autores espa˜oles es [6]. En la primera parte n de este libro se expone con claridad los principios del Dise˜o de Software n Dirigido por Pruebas. En la segunda parte del libro se aplica lo expuesto en la primera parte al desarrollo de una aplicaci´n de ejemplo. Aunque los o
  • 103. 104 CAP´ ITULO 6. PRUEBAS UNITARIAS CON JUNIT lenguajes de programaci´n que muestran los ejemplos con .Net y Python, o la aplicaci´n a Java con JUnit es directa. o El cap´ ıtulo 10 de [13] presenta las c´mo realizar pruebas unitarias con o JUnit, y en el cap´ıtulo 12 muestra como trabajar con Cobertura para ana- lizar el grado de cobertura de nuestras pruebas. Cobertura es otra excelente herramienta para el an´lisis de cobertura de las pruebas unitarias. a Otro excelente t´ ıtulo que deber´ figurar en todos las estanter´ de un ıa ıas buen desarrollador es Clean code de Robert C. Martin, tambi´n conocido e como uncle Bob. En el cap´ ıtulo 9 de esta referencia [10] est´ dedicado a a las buenas pr´cticas en el dise˜o de test unitarios de prueba. a n
  • 104. Cap´ ıtulo 7Entrada y SalidaContenidos 7.1. Flujos (Streams) . . . . . . . . . . . . . . . . . . . . 106 7.2. Flujos de bytes . . . . . . . . . . . . . . . . . . . . . 107 7.3. Flujos de caracteres . . . . . . . . . . . . . . . . . . 108 7.4. Conexi´n entre flujos de bytes y de caracteres o . . 109 7.5. El sistema de ficheros y flujos a ficheros . . . . . . 110 7.5.1. El sistema de ficheros . . . . . . . . . . . . . . . . . 110 7.5.2. Flujos a ficheros . . . . . . . . . . . . . . . . . . . . 110 7.5.2.1. Flujos de bytes a ficheros . . . . . . . . . . 110 7.5.2.2. Flujos de caracteres a ficheros . . . . . . . 111 7.6. Serializaci´n . . . . . . . . . . . . . . . . . . . . . o . . 112Introducci´n oYa conocemos como definir clases que contienen datos y m´todos que nos per- emiten trabajar con las instancias de estas clases. En muchos casos, una vez quehemos trabajado con nuestros datos nos interesa almacenarlos de manera per-manente, de tal modo que sea posible recuperar nuestros datos m´s tarde para aseguir trabajando sobre ellos. Todo lenguaje de programaci´n proporciona una serie de mecanismos para orealizar operaciones de entrada y salida de datos. Decimos que los datos son deentrada cuando llegan a nuestra aplicaci´n desde una fuente de datos, y que son ode salida cuando nuestra aplicaci´n env´ datos a alg´n sumidero de datos. o ıa u El lenguaje de programaci´n Java nos proporciona un paquete, con una gran ocantidad de clases, para poder realizar entrada/salida en nuestras aplicaciones.Ver´s, que las operaciones de entrada/salida son susceptibles de lanzar gran acantidad de excepciones que vamos a tener que gestionar tal y como vimos enel cap´ ıtulo 5. La potencia de la aproximaci´n de Java a las operaciones de entrada/salida oes que Java utiliza un concepto transversal con independencia del dispositivosobre el que se trabaja. Independientemente de si la salida es hacia un fichero, 105
  • 105. 106 CAP´ ITULO 7. ENTRADA Y SALIDA Figura 7.1: El concepto de flujo representado gr´ficamente. aun Socket o una conexi´n de Internet, el mecanismo de entrada/salida es el omismo: el uso de Flujos (Streams).7.1. Flujos (Streams)Los flujos en Java son los canales por donde transita la informaci´n. Los flujos opueden ser de entrada, de salida, o tener ambas direcciones. Utilizaremos flujosde entrada cuando a nuestras aplicaciones lleguen datos, es decir, cuando que-ramos leer datos que nos llegan desde alguna fuente de datos. Por el contrario,utilizaremos flujos de salida cuando nuestras aplicaciones quieran enviar datosa alg´n sumidero de datos. u La potencia de los flujos est´ relacionada con su independencia de los dispo- asitivos de entrada/salida a los que se est´n conectando. Desde el punto de vista ede nuestro c´digo, no importa que el dispositivo de salida sea una consola en opantalla, un Socket, o un fichero en nuestro disco duro, el mecanismo de salidasiempre es el mismo. Por otro lado, no importa que el dispositivo de entrada seael teclado, una conexi´n a una URL, o un fichero en nuestro disco duro, el me- ocanismo de entrada siempre es el mismo. Las operaciones de entrada/salida enJava siempre se realizan a trav´s de flujos que son independientes de las fuentes eo sumideros de datos. En la Figura 7.1 se muestra gr´ficamente el concepto de aflujo. En Java existen dos grandes categor´ de flujos, cada una de ellas con sus ıaspropias clases para realizar operaciones de entrada salida: los flujos de bytes ylos flujos de caracteres. Utilizaremos unos u otros para realizar operaciones deentrada/salida dependiendo de la naturaleza de los datos que recibamos desdeuna fuente de datos o enviemos a un sumidero de datos. En las siguientes secciones se va a presentar una gran cantidad de nuevasclases, lo que implica que vas a ver muchos nombres de clase nuevos. Al principiopuede parecer abrumador, pero presta atenci´n al nombre de las clases y ver´s o aque es muy significativo, veremos los detalles en la nominaci´n de las clases en olas siguientes secciones. Por otro lado, tambi´n ver´s que existe simetr´ entre e a ıalos nombres de las clases que realizan operaciones de lectura y las que realizan
  • 106. 7.2. FLUJOS DE BYTES 107 Figura 7.2: Parte de la jerarqu´ de clases para flujos de bytes. ıaoperaciones de escritura. Y finalmente, tambi´n ver´s que existe simetr´ en el e a ıanombre de las clases que correspondes a flujos de bytes y los nombres de lasclases que corresponden a flujos de caracteres. Esta doble simetr´ y el criterio ıapara nominar a las clases te resultar´ de gran ayuda para reconocer cual es el acometido de una clase simplemente a trav´s de su nombre. e7.2. Flujos de bytesLos flujos de bytes nos permiten leer bytes desde una fuente de datos o escribirbytes hacia un sumidero de datos, es decir nos permiten la lectura/escritura dedatos binarios. Las clases que nos permiten leer/escribir sobre flujos de bytesexisten en Java desde las primeras versiones del lenguaje, y por ello, dispositivosde entrada como el teclado, o dispositivos de salida como una consola en pantallason ambos flujos de bytes, aunque lo que finalmente se lee o escriba a ellos seancaracteres. Existe simetr´ en el modo de nombrar a las clases que realizan operaciones ıade lectura sobre flujos de bytes y las que realizan operaciones de escritura. Si laoperaci´n es de lectura, el nombre de la clase contendr´ la palabra Input, si el o aflujo es de escritura, la clase contendr´ la palabra Output. a Todas las clases que realizan operaciones de lectura de bytes extienden a laclase abstract InputStrem, por su lado, todas las clases que realizan opera-ciones de escritura de bytes extienden a la clase abstract OutputStrem. F´ ıjateque ambas clases son abstract y por lo tanto no se pueden instanciar directa-mente. En la Figura 7.2 se muestra algunas clases de la jerarqu´ de flujos de ıabytes. La clase DataInputStream permite abrir un fichero para leer tipos de datosprimitivos, as´ por ejemplo, esta clase proporciona el m´todo float readFloat() ı e
  • 107. 108 CAP´ ITULO 7. ENTRADA Y SALIDA Figura 7.3: Parte de la jerarqu´ de clases para flujos de caracteres. ıaque devuelve un n´mero real de precisi´n simple le´ desde un flujo, y boolean u o ıdoreadBoolean() que devuelve un booleano le´ desde un flujo de bytes. ıdo De modo an´logo, la clase DataOutputStream permite abrir un flujo para es- acribir en ´l tipos de datos primitivos, y en este caso contamos con m´todos como e evoid writeFloat(float f) para escribir en un flujo de byte un n´mero real de uprecisi´n sencilla y final void writeBoolean(boolean b) para escribir datos obooleanos sobre un flujo de bytes. Las clases BufferedInputStream y BufferedOutputStream efect´an lectura uy escritura de bytes desde un flujo utilizando una memoria intermedia (buffer )con el objeto de acelerar el proceso.7.3. Flujos de caracteres La Figura 7.3 muestra una peque˜a parte de la jerarqu´ de clases existente n ıaen Java para los flujos de caracteres. F´ ıjate en la analog´ con la Figura 7.2. ıaDe nuevo, tenemos un par de clases abstractas, abstract Reader en el caso delectura de flujos de caracteres y abstract Writer en el caso de escritura. De nuevo, y tambi´n de modo an´logo a los flujos de bytes, disponemos e ade dos subclases que proporcionan una memoria intermedia para mejorar elrendimiento del proceso de lectura/escritura con flujos de caracteres. Estas clasesson BufferedReader que nos proporciona un flujo de lectura de caracteres conbuffer, y BufferedWriter que nos proporciona un flujo de escritura de caracterescon buffer. En el caso de la clase BufferedReader, esta clase cuenta con el m´todo eString readLine() que nos permite leer cadenas de caracteres.
  • 108. ´ 7.4. CONEXION ENTRE FLUJOS DE BYTES Y DE CARACTERES 109 7.4. Conexi´n entre flujos de bytes y de carac- o teres Como acabas de ver en las secciones anteriores, para un determinado tipo de flujo existe varias clases definidas en el ´rbol de jerarqu´ de modo que las clases a ıa, hijas van a˜adiendo nueva funcionalidad sobre la que proporciona la clase padre. n Tomando como ejemplo los flujos de entrada de caracteres, la clase Reader proporciona m´todos para leer caracter a caracter desde alguna fuente de datos, e y su clase hija BufferedReader a˜ade un buffer intermedio en la lectura de n modo que podemos leer l´ ıneas de caracteres (String) desde la fuente de datos. Por otro lado los constructores de BufferedReader son: BufferedReader(Reader in) BufferedReader(Reader in, int sz) ambos necesitan una referencia a Reader para invocarse. La nueva instancia de BufferedReader se construye envolviendo a la instancia de Reader. Y esta es la idea clave del trabajo con flujos en Java, obtener de alg´n modo u flujos de tipo b´sico e ir construyendo sobre ellos nuevas instancias hasta llegar a a un flujo que nos proporcione la funcionalidad que necesitamos. Por otro lado, el primer tipo de flujos que se introdujo en Java desde la versi´n 1.0 fueron los flujos de bytes, y desde la versi´n 1.1 aparecieron los o o flujos de caracteres. No es por lo tanto de extra˜ar que tanto el teclado, como n la pantalla/consola sean flujos de bytes. Con todo esto, la pregunta que nos surge es ¿C´mo se conectan los flujos de o caracteres con los flujos de bytes para que, por ejemplo, podamos leer cadenas de caracteres directamente desde el teclado? La respuesta a esta pregunta es que Java proporciona clases de conexi´n entre ambos tipos de flujos: o InputStreamReader toma una instancia de InputStream, flujo de entrada de bytes, y sobre ´l que crear un flujo de lectura de caracteres. e OutputStreamWriter toma una instancia de OutputStream, flujo de salida de bytes, y sobre ´l que crea un flujo de escritura de caracteres. e El Listado 7.1 muestra un ejemplo de uso de esta t´cnica para leer cadenas e de caracteres desde teclado:1 I n p u t S t r e a m i s = System . i n ; // El t e c l a d o e s Java e s System . i n2 I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r ( i s ) ; // Lo decoramos como un f l u j o de c a r a c t e r e s3 B u f f e r e d R e a d e r br = new B u f f e r e d R e a d e r ( i s r ) ; // Lo decoramos con un f l u j o con memoria i n t e r m e d i a4 S t r i n g l i n e a = br . r e a d L i n e ( ) ; // Ya podemos l e e r c a d e n a s de t e x t o d e s d e el teclado . Listado 7.1: T´cnica para leer caracteres desde teclado e En la l´ ınea 1 del Listado 7.1 simplemente definimos la referencia is hacia el teclado, que es el flujo de entrada de byte desde el que queremos leer. En la l´ ınea 2, convertimos el flujo de entrada bytes a un flujo de entrada de caracteres con la ayuda de la clase InputStreamReader, en este momento ya podr´ ıamos leer ca- racteres desde el teclado, pero es m´s eficiente utilizar una memoria intermedia. a
  • 109. 110 CAP´ ITULO 7. ENTRADA Y SALIDAEn la l´ ınea 3 estamos creando una instancia de la clase BufferedReader sobreel flujo de entrada de caracteres (InputStreamReader), para poder finalmenteleer cadenas de caracteres con la ayuda del m´todo String readLine(), tal y e ınea 4 1 .como se muestra en la l´7.5. El sistema de ficheros y flujos a ficherosUn caso particular de fuente y sumidero de datos son los ficheros. Desde nuestrosprogramas podemos leer los datos contenidos en un fichero, sean estos datos detipo binarios o caracteres, y podemos escribir datos a un fichero, sea estos datosde tipo binarios o caracteres. Como el acceso para realizar entrada/salida es independiente del dispositivo,lo que necesitaremos en este caso es alg´n medio para acceder al sistema de uficheros. Para ello Java nos proporciona la clase File. Esta clase nos da acceso alsistema de ficheros y sobre esta clase podemos construir flujos de entrada/salidapar tipos de datos tanto binarios como caracteres.7.5.1. El sistema de ficherosLa clase File nos da acceso al sistema de ficheros, con independencia del sis-tema operativo sobre el que se ejecute. Gracias a esta clase, podemos obtenerinformaci´n tal como la lista de ficheros o directorios bajo un directorio dado, oo comprobar si el camino con el que se construye la instancia de File hacereferencia a un fichero o a un directorio.7.5.2. Flujos a ficherosLos flujos a ficheros nos permiten acceder a la informaci´n contenida en los fiche- oros de nuestro sistema con el fin de leer desde ellos o escribir informaci´n hacia oellos. De nuevo, podemos distinguir dos tipos de flujos a ficheros dependiendode si la informaci´n contenida en ellos es de tipo binario o caracteres. o7.5.2.1. Flujos de bytes a ficherosLa clase FileInputStream nos permite crear un flujo de lectura hacia un ficheropara leer desde ´l datos de tipo binario. Podemos instanciar esta clase a partir ede una referencia a File o bien a partir de un String que represente el caminohasta el fichero. De modo an´logo, la clase FileOutputStream nos permite crear un flujo de aescritura hacia un fichero para escribir en ´l datos de tipo binario. Podemos einstanciar esta clase tambi´n a partir de una referencia a File o bien a partir ede un String que represente el camino hasta el fichero. En el momento de lacreaci´n del flujo podemos indicar si queremos conservar el posible contenido odel fichero en el momento de la creaci´n del flujo a trav´s de un argumento de o etipo booleano en el constructor. 1 En el Cap´ ıtulo 8 se mostrar´ un clase de utilidad Scanner que facilita enormemente la alectura de datos desde teclado, y en general desde cualquier flujo de entrada. No obstante loque aqu´ se ha mostrado es un ejemplo del mecanismo general de conversi´n entre flujos de ı ocaracteres y flujos de bytes
  • 110. 7.5. EL SISTEMA DE FICHEROS Y FLUJOS A FICHEROS 111 7.5.2.2. Flujos de caracteres a ficheros La clase FileReader nos permite crear un flujo de lectura hacia un fiche- ro, para leer desde ´l datos de tipo caracter. De modo an´logo al caso de e a FileInputStream, podemos instanciar esta clase s partir de una referencia a File o bien a partir de un String que represente el camino hasta el fichero. Finalmente, la clase FileWriter nos permite crear un flujo de escritura de caracteres hacia un fichero para escribir en ´l datos de tipo caracter. De modo e an´logo a la clase FileOutputStream, podemos instanciar esta clase a partir de a una referencia File, de un String que indique el camino al fichero, e indicar en el momento de la creaci´n si el fichero conserva o no el posible contenido. o Como ejemplo del manejo de ficheros, el Listado 7.2 muestra c´mo abrir un o flujo a un fichero de caracteres para leer desde ´l l´ e ınea a l´ınea su contenido y mostrarlo por consola.1 import java . io . BufferedReader ;2 import java . io . File ;3 import java . io . FileNotFoundException ;4 import java . io . FileReader ;5 import java . io . IOException ;67 public c l a s s L e c t u r a F l u j o T e x t o {8 public L e c t u r a F l u j o T e x t o ( ) {9 super ( ) ;10 }1112 private void e j e c u t a ( S t r i n g camino ) {13 F i l e f i c h e r o = new F i l e ( camino ) ;14 FileReader flujoLectura ;15 BufferedReader f l u j o B u f f e r = null ;16 try {17 try {18 f l u j o L e c t u r a = new F i l e R e a d e r ( f i c h e r o ) ;19 f l u j o B u f f e r = new B u f f e r e d R e a d e r ( f l u j o L e c t u r a ) ;20 String linea ;21 while ( ( l i n e a = f l u j o B u f f e r . r e a d L i n e ( ) ) != n u l l ) {22 System . o u t . p r i n t l n ( l i n e a ) ;23 }24 } finally {25 i f ( f l u j o B u f f e r != n u l l )26 flujoBuffer . close () ;27 }28 } catch ( F i l e N o t F o u n d E x c e p t i o n e ) {29 e . printStackTrace () ;30 } catch ( I O E x c e p t i o n e ) {31 e . printStackTrace () ;32 }33 }3435 public s t a t i c void main ( S t r i n g [ ] a r g s ) {36 new L e c t u r a F l u j o T e x t o ( ) . e j e c u t a ( a r g s [ 0 ] ) ;37 }38 } Listado 7.2: Lectura desde un flujo de texto hacia un fichero Como curiosidad del Listado 7.2, f´ ıjate que hay un bloque try{...} finally{...} en las l´ ıneas 17-27 que est´ incluido dentro de otro bloque a try{...}. El uso de estos bucles anidados facilita la lectura de c´digo, y su o ejecuci´n es la siguiente: si se produce alguna excepci´n durante el trabajo con o o el fichero (l´ ıneas de c´digo 18-23), se ejecutar´ el bloque finally{...} con lo o a que se cerrar´ el fichero, y la excepci´n se propagar´ al bloque try{...} ex- a o a terno, que es quien tiene los bloques catch{...} para atrapar a cada una de
  • 111. 112 CAP´ ITULO 7. ENTRADA Y SALIDAlas excepciones posibles. Este modo de codificaci´n es ampliamente utilizado y oconviene que lo incorpores como t´cnica de escritura de tu propio c´digo. e o7.6. Serializaci´n oLa Serializaci´n es el mecanismo por el cual Java es capaz de convertir un objeto oen una secuencia de bytes. De este modo, podemos crear un flujo a partir dela secuencia de bytes que representa al objeto para escribirlo en un fichero oenviarlo a trav´s de un Socket por ejemplo. e El concepto de serializaci´n es extremadamente potente, si somos capaces ode obtener una secuencia de bytes del objeto y sobre ella crear un flujo, pode-mos enviar el objeto a cualquier dispositivo que lo acepte. Y de modo an´logo, apodr´ıamos conectarnos a una fuente de datos a trav´s de un flujo de entrada ey obtener objetos desde ella. De hecho, esta t´cnica es tan potente que es la epieza sobre la que descansan otras tecnolog´ Java como la invocaci´n remota ıas ode m´todo (Remote Method Innvocation - RMI ), y gran parte de las tecnolog´ e ıasJava en su edici´n Enterprise 2 . o En esta secci´n vamos a presentar la serializaci´n y c´mo utilizarla para o o oalmacenar un objeto en un fichero, y en el Cap´ ıtulo 15 veremos c´mo utilizar la oserializaci´n para enviar y recibir objetos a trav´s de un Socket. o e Para indicar que un objeto es Serializable debe implementar la interfaceSerializable. Esta interface no declara ning´n m´todo, es unicamente una u e ´marca sem´ntica para que Java sepa que en alg´n momento podemos querer a userializar el objeto. Cuando el objeto serializable se convierta en una secuenciade bytes, esta secuencia, adem´s de incluir el valor de los atributos de la ins- atancia incluye m´s informaci´n, como la clase de la instancia y un n´mero de a o userie de la clase, para poder hacer un control de versiones de la clase. Por con-venci´n, este n´mero de control se declara como private static final long o userialVersionUID. El valor de este atributo lo podemos fijar nosotros mismos,pero eso no garantiza que otro programador pueda utilizar el mismo n´mero de userie para una clase completamente distinta. Java nos proporciona una herra-mienta, integrada en el JDK, que genera n´meros de versi´n a partir de la clase u ocompilada, esta herramienta es serialver, su uso es serialver [-classpath]nombreClase y un ejemplo de uso es:$ serialver -classpath . serializacion.Personaserializacion.Persona: static final long serialVersionUID =7360368493593648647L; En este caso la herramientaserialver ha generado el n´mero de serie u7360368493593648647L para la clase Persona. Y es muy improbable queserialver genere el mismo n´mero de versi´n para cualquier otra clase, lo u oque nos garantiza que este n´mero de versi´n es unico e identifica a la clase u o ´Persona. 2 Java 2 Enterprise Edition est´ enfocada al desarrollo de aplicaciones de servidor. Son aaplicaciones que se no se ejecutan en las m´quinas de un cliente, si no en un servidor remoto. aUsualmente, el ciente accede al servidor a trav´s de un navegador web, aunque no es la unica e ´opci´n posible o
  • 112. ´ 7.6. SERIALIZACION 113 Eclipse es capaz de invocar a serialver de manera transparente. Si nuestra nueva clase implementa la interface Serializable y olvidamos incluir el atri- buto serialVersionUID, Eclipse nos mostrar´ un aviso. Si corregimos el aviso a que nos da Eclipse seleccionando la opci´n Add generated serial verion ID, o se a˜adir´ el n´mero de serie obtenido con serialver. n a u El Listado 7.3 muestra la clase Persona que implementa la interface Serializable y a la que se le ha a˜adido el n´mero de versi´n generado con n u o serialver.1 package s e r i a l i z a c i o n ;23 import j a v a . i o . S e r i a l i z a b l e ;45 public c l a s s P e r s o n a implements S e r i a l i z a b l e {6 private s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 7 3 6 0 3 6 8 4 9 3 5 9 3 6 4 8 6 4 7L ;7 S t r i n g nombre ;8 String apellidos ;9 String telefono ;1011 Persona ( ) { }1213 P e r s o n a ( S t r i n g nombre , S t r i n g a p e l l i d o s , String telefono ) {14 t h i s . nombre = nombre ;15 this . a p e l l i d o s = a p e l l i d o s ;16 this . t e l e f o n o = t e l e f o n o ;17 }1819 S t r i n g getNombre ( ) {20 return nombre ;21 }2223 String getApellidos () {24 return a p e l l i d o s ;25 }2627 String getTelefono () {28 return t e l e f o n o ;29 }3031 @Override32 public S t r i n g t o S t r i n g ( ) {33 return P e r s o n a [ a p e l l i d o s = + a p e l l i d o s + , n o m b r e = + nombre34 + , telefono = + telefono + ] ;35 }36 } Listado 7.3: La clase Persona lista para ser serializada El Listado 7.4 muestra un ejemplo de como serializar esta clase y la secuencia de bytes, y c´mo almacenar la secuencia de bytes obtenida en un fichero para o su recuperaci´n posterior. o1 package s e r i a l i z a c i o n ;23 import java . io . FileNotFoundException ;4 import java . io . FileOutputStream ;5 import java . io . IOException ;6 import java . io . ObjectOutput ;7 import java . io . ObjectOutputStream ;8 import java . io . OutputStream ;910 public c l a s s E s c r i t o r P e r s o n a {11 public E s c r i t o r P e r s o n a ( ) {12 super ( ) ;13 }1415 private void e j e c u t a ( S t r i n g fichero ) {
  • 113. 114 CAP´ ITULO 7. ENTRADA Y SALIDA16 OutputStream s t r e a m E s c r i t u r a ;17 ObjectOutput s t r e a m E s c r i t u r a P e r s o n a = n u l l ;18 try {19 try {20 s t r e a m E s c r i t u r a = new F i l e O u t p u t S t r e a m ( f i c h e r o ) ;21 s t r e a m E s c r i t u r a P e r s o n a = new ObjectOutputStream ( s t r e a m E s c r i t u r a ) ;22 s t r e a m E s c r i t u r a P e r s o n a . w r i t e O b j e c t (new P e r s o n a ( J a m e s , G o s l i n g , 555 123 456 ) ) ;23 } finally {24 i f ( s t r e a m E s c r i t u r a P e r s o n a != n u l l ) s t r e a m E s c r i t u r a P e r s o n a . c l o s e ( ) ;25 }26 } catch ( F i l e N o t F o u n d E x c e p t i o n e ) {27 e . printStackTrace () ;28 } catch ( I O E x c e p t i o n e ) {29 e . printStackTrace () ;30 }31 }3233 public s t a t i c void main ( S t r i n g [ ] a r g s ) {34 new E s c r i t o r P e r s o n a ( ) . e j e c u t a ( a r g s [ 0 ] ) ;35 }3637 } Listado 7.4: Serializaci´n de la clase Persona hacia un fichero de bytes o Si ejecutamos el programa Java anterior, introduciendo por l´ ınea de ins- trucciones como nombre de fichero persona.ser 3 se crear´ un fichero de tipo a binario cuyo contenido es una instancia de la clase Persona cuyos atributos tienen los valores asignados. El Listado 7.5 muestra un ejemplo completo de c´mo leer un objeto seriali- o zado almacenado en un fichero de tipo binario.1 package s e r i a l i z a c i o n ;23 import java . io . FileInputStream ;4 import java . io . FileNotFoundException ;5 import java . io . IOException ;6 import java . io . InputStream ;7 import java . io . ObjectInput ;8 import java . io . ObjectInputStream ;910 public c l a s s L e c t o r P e r s o n a {11 public L e c t o r P e r s o n a ( ) {12 super ( ) ;13 }1415 private void e j e c u t a ( S t r i n g f i c h e r o ) {16 InputStream streamLectura ;17 ObjectInput streamLecturaPersona = null ;18 try {19 try {20 s t r e a m L e c t u r a = new F i l e I n p u t S t r e a m ( f i c h e r o ) ;21 s t r e a m L e c t u r a P e r s o n a = new O b j e c t I n p u t S t r e a m ( s t r e a m L e c t u r a ) ;22 Persona persona = ( Persona ) streamLecturaPersona . readObject ( ) ;23 System . o u t . p r i n t l n ( p e r s o n a ) ;24 } finally {25 i f ( s t r e a m L e c t u r a P e r s o n a != n u l l ) s t r e a m L e c t u r a P e r s o n a . c l o s e ( ) ;26 }27 } catch ( F i l e N o t F o u n d E x c e p t i o n e ) {28 e . printStackTrace () ;29 } catch ( I O E x c e p t i o n e ) {30 e . printStackTrace () ;31 } catch ( C l a s s N o t F o u n d E x c e p t i o n e ) {32 e . printStackTrace () ;33 } 3 Por convenci´n, para los ficheros que contienen datos de objetos serializados se utiliza la o extensi´n .ser o
  • 114. ´ 7.6. SERIALIZACION 11534 }3536 public s t a t i c void main ( S t r i n g [ ] a r g s ) {37 new L e c t o r P e r s o n a ( ) . e j e c u t a ( a r g s [ 0 ] ) ;38 }3940 } Listado 7.5: Des-serializaci´n de la clase Persona desde un fichero de bytes o F´ıjate c´mo, de nuevo, se han utilizado bloques try{...} anidados para o facilitar la gesti´n de excepciones. o En el cap´ ıtulo 15 se mostrar´ otro ejemplo de serializaci´n de objetos pero a o esta vez se enviar´ la secuencia de bytes que representa al objeto a trav´s de a e los flujos que proporciona un Socket, con lo que podremos escribir un servidor capaz de enviarnos, a trav´s de una red de comunicaci´n de datos, objetos e o serializados y podremos recuperarlos en un cliente. Ejercicios 1. Amplia la funcionalidad de la aplicaci´n de la agenda, para que sea po- o sible almacenar los datos de los contactos en un fichero de texto para su posterior recuperaci´n. Escribe tanto el c´digo de escritura como de o o lectura. 2. Sigue ampliando la funcionalidad de tu aplicaci´n de la agenda para que o sea posible serializarla a un fichero de tipo binario para su posterior recu- peraci´n. Escribe tanto el c´digo de escritura como el de lectura. o o Lecturas recomendadas La referencia [2] dedica todo el Cap´ ıtulo 15 al estudio del mecanismo de entrada/salida en Java. La referencia [3] dedica tambi´n todo el Cap´ e ıtulo 14 al estudio del meca- nismo de entrada/salida en Java. En particular las secciones dedicadas a la serializaci´n de objetos son muy interesantes. o
  • 115. 116 CAP´ ITULO 7. ENTRADA Y SALIDA
  • 116. Cap´ ıtulo 8Algunas clases de utilidaddel paquete est´ndar aContenidos 8.1. La clase Scanner . . . . . . . . . . . . . . . . . . . . 118 8.2. Trabajo con cadenas de caracteres . . . . . . . . . 120 8.2.1. La clase String . . . . . . . . . . . . . . . . . . . . . 120 8.2.2. Las clases StringBuffer y StringBuilder . . . . . . 121 8.3. Clases recubridoras . . . . . . . . . . . . . . . . . . 122 8.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . . 124 8.5. Trabajo con fechas . . . . . . . . . . . . . . . . . . . 128 8.5.1. La clase Date . . . . . . . . . . . . . . . . . . . . . . 128 8.5.2. Las clases Calendar y GregorianCalendar . . . . . . 129 8.6. Matem´ticas . . . . . . . . . . . . . . . . . . . a . . . 129 8.6.1. La clase Math . . . . . . . . . . . . . . . . . . . . . . 129 8.6.2. La clase Random . . . . . . . . . . . . . . . . . . . . . 130Introducci´n oLa edici´n est´ndar de Java proporciona una ampl´ o a ısima colecci´n de clases ya odefinidas. Estas clases ya definidas son de gran utilidad, a medida que comoprogramadores vamos conociendo nuevas clases de esta colecci´n nuestra pro- oductividad aumenta. En este Cap´ıtulo presentamos un peque˜o grupo de clases de gran utilidad, naunque existen otras muchas tan utiles como las aqu´ presentadas. Obviamen- ´ ıte, por cuesti´n de espacio, s´lo presentamos las que creemos m´s interesantes o o allegados a este punto. 117
  • 117. 118CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR 8.1. La clase Scanner En el Cap´ ıtulo 7 comentamos que en Java tanto la consola como el teclado son flujos de bytes. Esto se debe a que en la versi´n 1.0 de Java no exist´ flujos de o ıan caracteres. Para guardar la compatibilidad hacia atr´s, tanto el teclado como la a consola siguen siendo flujos de caracteres en las versiones actuales de Java. Como finalmente lo que nos interesa leer desde el teclado son caracteres, tenemos que aplicar la t´cnica del recubrimiento que vimos en el Cap´ e ıtulo 7 para conseguir leer cadenas de caracteres. El uso de la clase de utilidad Scanner nos oculta los flujos y nos permite leer directamente de flujos, en particular desde el teclado. La clase Scanner est´ presente en Java desde la versi´n 5. Esta clase per- a o mite analizar una cadena de texto utilizando para ello expresiones regulares 1 . Las cadenas se pueden proporcionar directamente en el momento de crear la instancia de clase Scanner o a trav´s de un flujo. e Supongamos que tenemos un fichero de texto que almacena datos de una agenda de tel´fonos con el formato Persona: Nombre: Apellidos: Telefono, e un ejemplo de contenido del fichero es2 : Persona: LUISA: GARCIA MORENO: 313372295 Persona: ROSARIO: GONZALEZ ESTEBAN: 560248322 Persona: MANUEL: SANZ GARCIA: 571365702 Persona: FRANCISCO: VAZQUEZ FERRER: 690109651 Persona: VICTOR: MU~OZ LOPEZ: 500661266 N El Listado 8.1 muestra un ejemplo de uso de la clase Scanner para leer l´ ınea a l´ ınea de este fichero. El m´todo hasNext() nos sirve para comprobar si hay e m´s elementos a devolver. a1 package u t i l i d a d ;23 import j a v a . i o . F i l e ;4 import j a v a . i o . F i l e N o t F o u n d E x c e p t i o n ;5 import j a v a . u t i l . S c a n n e r ;67 public f i n a l c l a s s U t i l i d a d S c a n n e r {8 private s t a t i c f i n a l S t r i n g FICHERO = a g e n d a . t x t ;9 private U t i l i d a d S c a n n e r ( ) {10 super ( ) ;11 }1213 private void e j e c u t a ( ) {14 try {15 S c a n n e r s c a n n e r = new S c a n n e r (new F i l e (FICHERO) ) ;16 while ( s c a n n e r . hasNext ( ) )17 System . o u t . p r i n t l n ( s c a n n e r . n e x t L i n e ( ) ) ;18 } catch ( F i l e N o t F o u n d E x c e p t i o n e ) {19 e . printStackTrace () ;20 }21 }2223 public s t a t i c void main ( S t r i n g [ ] a r g s ) {24 new U t i l i d a d S c a n n e r ( ) . e j e c u t a ( ) ;25 } 1 Param´s informaci´n sobre qu´ son expresiones regulares el lector puede consultar http: a o e //en.wikipedia.org/wiki/Regular_expression 2 Estos datos se han generado aleatoriamente tomando como base los datos estad´ ısticos del Instituto Nacional de Estad´ıstica. Estos datos estad´ ısticos se pueden consultar en la direcci´n o http://www.ine.es
  • 118. 8.1. LA CLASE SCANNER 1192627 } Listado 8.1: Lectura de l´ ıneas de texto desde un fichero con el uso de la clase Scanner La clase Scanner tiene m´todos para poder leer tipos de datos primitivos e tal y como se muestra en el Listado 8.2.1 package u t i l i d a d ;23 import j a v a . i o . F i l e ;4 import j a v a . i o . F i l e N o t F o u n d E x c e p t i o n ;5 import j a v a . u t i l . S c a n n e r ;67 public f i n a l c l a s s U t i l i d a d S c a n n e r 2 {8 private s t a t i c f i n a l S t r i n g FICHERO = a g e n d a . t x t ;910 private U t i l i d a d S c a n n e r 2 ( ) {11 super ( ) ;12 }1314 private void e j e c u t a ( ) {15 try {16 S c a n n e r s c a n n e r = new S c a n n e r (new F i l e (FICHERO) ) ;17 while ( s c a n n e r . hasNext ( ) ) {18 analizaLinea ( scanner . nextLine () ) ;19 }20 } catch ( F i l e N o t F o u n d E x c e p t i o n e ) {21 e . printStackTrace () ;22 }23 }2425 private void a n a l i z a L i n e a ( S t r i n g l i n e a ) {26 S c a n n e r s c a n n e r = new S c a n n e r ( l i n e a ) ;27 scanner . useDelimiter ( : ) ;28 S t r i n g p e r s o n a , nombre , a p e l l i d o s ;29 int t e l e f o n o ;30 persona = scanner . next ( ) ;31 nombre = s c a n n e r . n e x t ( ) ;32 a p e l l i d o s = scanner . next ( ) ;33 t e l e f o n o = scanner . nextInt () ;34 System . o u t . p r i n t l n ( nombre + , + a p e l l i d o s + , + t e l e f o n o ) ;35 }3637 public s t a t i c void main ( S t r i n g a r g s [ ] ) {38 new U t i l i d a d S c a n n e r 2 ( ) . e j e c u t a ( ) ;39 }40 } Listado 8.2: Lectura de l´ ıneas de texto desde un fichero con el uso de la clase Scanner El resultado de la ejecuci´n de este c´digo es el siguiente: o o FRANCISCO JOSE,ALVAREZ MARTIN,90727037 ROBERTO,CASTRO RUIZ,945953372 MANUEL,PEREZ HERRERA,520908284 JULIA,ALVAREZ ORTEGA,482596843 TOMAS,VARGAS MARTINEZ,691825532 Si construimos la instancia de Scanner sobre el flujo de bytes que repre- senta al teclado System.in, con la clase Scanner podremos leer tipos de datos primitivos, tal y como muestra el Listado 8.3.
  • 119. 120CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR1 S c a n n e r l e c t o r T e c l a d o = new S c a n n e r ( System . i n ) ;2 System . o u t . p r i n t ( I n t r o d u c e u n e n t e r o : ) ;3 int entero = l e c t o r T e c l a d o . nextInt ( ) ;4 System . o u t . p r i n t l n ( I n t r o d u c e u n r e a l : ) ;5 float r e a l = lectorTeclado . nextFloat () ;6 System . o u t . p r i n t l n ( E n e r o = + e n t e r o + ; r e a l = + r e a l ) ; Listado 8.3: La clase Scanner nos permite leer tipos primitivos desde teclado. La clase Scanner nos permite leer desde cualquier flujo de entrada, no s´lo o desde el teclado. 8.2. Trabajo con cadenas de caracteres Ya conoces una clase que representa y manipula cadenas de caracteres, la clase String. No obstante, Java proporciona clases m´s eficientes para trabajar con a cadenas, ya que la clase String es inmutable y al manipular las cadenas de texto que representa, se crean nuevas instancias de esta clase, con el coste que supone la creaci´n de objetos. A efectos pr´cticos la inmutabilidad de la clase o a String significa que cuando concatenamos dos cadenas el resultado es una nueva cadena, no se amplia ninguna de las cadenas originales para albergar la nueva cadena.1 S t r i n g cadenaConcatenada = H o l a + , c o m o e s t a s ; ´ Listado 8.4: ”La concatenaci´n de dos cadenas crea una nueva cadena.” o En el Listado 8.4 se crean tres objetos de tipo String, el primero de ellos contiene la cadena de caracteres Hola, el segundo contiene , como est´s y el a tercero contiene Hola, como est´s.a 8.2.1. La clase String La clase String representa una cadena de caracteres inmutable, es decir, una vez creada no se puede modificar la secuencia de caracteres. Por la tanto es util ´ utilizar esta clase cuando no vamos a hacer manipulaciones continuadas sobre la cadena que representa. El unico operador sobrecargado en Java es el operador + cuando se aplica ´ sobre cadenas con el significado de concatenarlas, tal y como muestra el Listado 8.5. Al concatenar dos cadenas se crea una nueva cadena para almacenar el resultado, de ah´ la ineficiencia al utilizar el operador + sobre String. ı1 S t r i n g primera = Hola ;2 S t r i n g segunda = m u n d o . 3 S t r i n g r e s u l t a d o = p r i m e r a + segunda ; Listado 8.5: Uso del operador + para concatenar dos cadenas de caracteres. Para comparar dos cadenas de caracteres, caracter a caracter, no debemos cometer el error de utilizar el operador == ya que este operador compara la igualdad de dos referencias. Para comparar dos cadena de caracteres utilizamos el m´todo public boolean equals(Object o), que compara el String actual e con la representaci´n como String del objeto que se le pasa como argumento. El o
  • 120. 8.2. TRABAJO CON CADENAS DE CARACTERES 121 m´todo equals(Object o) distingue entre may´sculas y min´sculas, si quere- e u u mos comparar dos cadenas con independencia del uso de may´sculas/min´sculas u u utilizaremos el m´todo public boolean equalsIgnoreCase(String s). e Para averiguar el n´mero de caracteres de una cadena utilizamos el m´todo u e public int length(). Si queremos convertir todas las letras de una cadena a min´sculas utilizamos el m´todo public String toLowerCase(), y el m´todo u e e public String toUpperCase en caso de que la queramos en may´sculas.u La clase String tambi´n posee el m´todo sobrecargado static String e e valueOf(boolean/char/int/long/float/double) para convertir tipos de da- tos primitivos a su representaci´n como cadenas de caracteres. o Un m´todo interesante que nos permite trocear una cadena de caracteres a e partir de una subcadena contenida en ellas es String split(String s), donde el argumento es una expresi´n regular. El Listado 8.6 muestra un ejemplo de uso o del m´todo split, f´ e ıjate que estamos dividiendo la cadena original buscando el patr´n representado por otra cadena, , . o1 S t r i n g i n i c i a l = Esta cadena , c o n t i e n e comas , por la que quiero t r o c e a r . ;2 String trozos [ ] = i n i c i a l . s p l i t (, ) ;3 for ( String trozo : t r o z o s )4 System . o u t . p r i n t l n ( t r o z o ) ; Listado 8.6: Uso del m´todo split para trocear una cadena. e El resultado de la ejecuci´n de este c´digo es: o o Esta cadena contiene comas por la que quiero trocear. Para poder dar formato a cadenas al estilo de C, la clase String nos propor- ciona el m´todo public static String format(String cadena, Object... e argumentos. El Listado 8.7 muestra un sencillo caso de uso.1 System . o u t . p r i n t l n ( S t r i n g . f o r m a t ( E l v a l o r d e P I e s : % 2 . 2 f , 3 . 1 4 1 5 ) ) ; Listado 8.7: Ejemplo de formato usando el m´todo format de la clase String e El resultado de la ejecuci´n del Listado 8.7: o El valor de PI es: 3,14 Si se necesitan formatos m´s sofisticados, la clase Formatter es de gran a ayuda. 8.2.2. Las clases StringBuffer y StringBuilder La clase StringBuffer tambi´n representa una cadena de caracteres co- e mo la clase String pero esta vez la cadena que representa puede cam- biar. Esta clase es la recomendada si, por ejemplo, queremos concatenar dos cadenas, ya que el resultado no crea una nueva cadena, si no que se modifica la original para representar la cadena concatenada final. Pa- ra ello la clase StringBuffer posee el m´todo sobrecargado StringBuffer e
  • 121. 122CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR append(boolean/int/long/float/double/String/StringBuffer) que a˜ade n la representaci´n como String del argumento a la cadena actual. o La clase StringBuffer posee otros m´todos interesantes de manipu- e laci´n. Por ejemplo, el m´todo int indexOf(String s) devuelve la po- o e sici´n de la primera ocurrencia de la cadena s dentro de la cade- o na original. El m´todo sobrecargado StringBuffer insert(int offset, e boolean/char/int/long/float/double/String) inserta la representaci´n del o segundo argumento en la cadena original a partir del offset indicado en el primer argumento. El Listado 8.8 muestra un ejemplo de uso de estos m´todos. e1 S t r i n g B u f f e r sb = new S t r i n g B u f f e r ( H o l a . ) ;2 sb . i n s e r t ( sb . i n d e x O f ( . ) , J a v a ) ;3 System . o u t . p r i n t l n ( sb ) ; Listado 8.8: Uso de los m´todos indexOf y insert de la clase StringBuffer e Los m´todos que manipulan la representaci´n de la cadena dentro de la e o clase StringBuffer est´n sincronizados, luego se pueden utilizar en aplicacio- a nes en las que varios hilos est´n accediendo a la misma referencia de la clase a StringBuffer. Veremos el uso de hilos y lo que significa que un m´todo est´ sin- e e cronizado en el Cap´ ıtulo 14. Por su parte, la clase StringBuilder funciona exactamente igual que la clase StringBuffer, de hecho los m´todos que proporciona la clase StringBuilder e son exactamente los mismo que la clase StringBuffer, pero esta vez ninguno de ellos est´ sincronizado por razones de eficiencia3 . a 8.3. Clases recubridoras Como ya sabes, en Java existen dos grandes grupos de tipos de datos, los tipos de datos primitivos y los tipos de datos referencia. Sin embargo, Java propor- ciona clases que recubren los tipos de datos primitivos para poder trabajar con ellos a trav´s de referencias, es decir, como con cualquier otro objeto. Esto es e especialmente util al trabajar con colecciones, tal y como veremos en la Secci´n ´ o 8.4. Tal y como muestra la Tabla 8.1, para cada tipo primitivo existe una clase recubridora. Crear una clase recubridora a partir de un tipo primitivo es muy sencillo, tal y como muestra el Listado 8.9, donde se crean clases recubridoras tanto a partir de datos primitivos como a partir de la representaci´n como o cadena de texto de los datos primitivos.1 Integer e n t e r o = new I n t e g e r ( 1 5 ) ;2 Integer e n t e r o S t r i n g = new I n t e g e r ( 1 0 ) ;3 Boolean b o o l e a n o V e r d a d e r o = new B o o l e a n ( true ) ;4 Boolean b o o l e a n o F a l s o = new B o o l e a n ( f a l s e ) ; Listado 8.9: Ejemplo de creaci´n de clases recubridoras. o Para recuperar, como tipos primitivos, los valores que almacena una clase recubridora, estas proporcionan m´todos tal y como muestra el Listado 8.10 e 3 Como veremos en el Cap´ ıtulo 14, el acceso a m´todos sincronizados tiene un sobrecoste e temporal debido al uso de cerrojos.
  • 122. 8.3. CLASES RECUBRIDORAS 123 Tipo primitivo Clase recubridora void java.lang.Void boolean java.lang.Boolean char java.lang.Character byte java.lang.Byte short java.lang.Short int java.lang.Integer long java.lang.Long float java.lang.Float double java.lang.Double Tabla 8.1: Para cada uno de los tipos primitivos de la columna de la izquierda, Java proporciona una clase recubridora, en la columna de la derecha.1 int ente roPrimit ivo = entero . intValue ( ) ;2 boolean b o o l e a n o P r i m i t i v o = b o o l e a n o V e r d a d e r o . b o o l e a n V a l u e ( ) ; Listado 8.10: Recuperaci´n de los datos como tipos primitivos a partir de las o clases recubridoras. Sin embargo, en la mayor´ de los casos es de gran utilidad hacer uso del ıa mecanismo de Autoboxing introducido en la versi´n 5 de Java. Este mecanismo o convierte, de modo autom´tico y transparente para el usuario, tipos primiti- a vos a recubridores en una asignaci´n siempre que el tipo primitivo y la clase o recubridora sean compatibles, tal y como muestra el Listado 8.11.1 Integer entero = 15;2 int ente roPrimit ivo = entero ;3 B o o l e a n b o o l e a n o V e r d a d e r o = true ;4 boolean b o o l e a n o P r i m i t i v o = b o o l e a n o V e r d a d e r o ; Listado 8.11: Ejemplos de autoboxing para la conversi´n entre tipos de datos o primitivos y sus correspondientes clases recubridoras. El mecanismo de Autoboxing es especialmente util cuando se utilizan las ´ clases colecci´n, tal y como veremos en la Secci´n 8.4. o o1 int e n t e r o = I n t e g e r . p a r s e I n t ( 10 ) ;2 double r e a l = Double . p a r s e D o u b l e ( 3 . 1 4 1 5 9 2 ) ; Listado 8.12: M´todos para obtener tipos primitivos a partir de cadenas de e caracteres. Un grupo de m´todos especialmente util cuando se procesan datos de en- e ´ trada de tipo texto, como por ejemplo los par´metros de llamada a nuestra a aplicaci´n, y los queremos convertir a tipos primitivos son los que muestra el o Listado 8.12, de modo an´logo cada clase recubridora, tiene un m´todo para a e realizar la correspondiente conversi´n. o
  • 123. 124CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR Figura 8.1: Interfaces b´sicos presentes en el Java Collections Framework. a Interface Implementaci´n o Set E HashSet E SortedSet E TreeSet E List E ArrayList E , LinkendList E , Vector E Queue E LinkendList E Map E HasMap E , HashTable E SortedMap E TreeMap E Tabla 8.2: Varias clases colecci´n y los interfaces que implementan. o8.4. ColeccionesLas clases que forman parte del grupo de clases colecci´n son aquellas que nos osirven para almacenar referencias a objetos, e implementan estructuras de datostales como listas, tablas de dispersi´n, conjuntos, etc´tera. El conjunto de inter- o efaces, clases y algoritmos que nos permiten trabajar con estructuras de datos seagrupan bajo el Java Collections Framework. La Figura 8.4 muestra los interfaces b´sicos definidos en el Java Collections aFramework. La parte de la derecha de esta figura muestra los interfaces querepresentan la funcionalidad de las clase que implementan colecciones de re-ferencias como ((conjuntos)); mientras que en la parte derecha de la figura semuestran los interfaces que representan la funcionalidad de las clases que imple-menten colecciones donde cada elemento de la colecci´n representa una pareja oclave/valor. Las clases que representan colecciones de referencias pueden implementaruno o m´s de estos interfaces. La Tabla 8.2 muestra algunas clases colecci´n y a olos interfaces que implementan. F´ıjate que en la Tabla 8.2 hay una novedad, los s´ ımbolos E a continua-ci´n de las interfaces o las clases, esta sintaxis significa que la interface o la clase oalmacena un tipo Gen´rico. En el momento de creaci´n de la clase debemos es- e opecificar cual es el tipo de los elementos que va a contener la clase. Veremos condetalle c´mo utilizar gen´ricos en nuestra propias clases en el Cap´ o e ıtulo 9. En
  • 124. 8.4. COLECCIONES 125 este cap´ıtulo s´lo veremos como trabajar con ellos en el momento de creaci´n de o o las clases y las ventajas que supone el uso de gen´ricos, que fueron introducidos e en la versi´n 5 de Java. Aunque, es posible instanciar las clases colecci´n sin es- o o pecificar el tipo de los elementos que contendr´. En este caso, el compilador s´lo a o mostrar´ un aviso con el siguiente texto ArrayList is a raw type. References to a generic type ArrayList E should be parameterized no un error. Sin embargo, es muy aconsejable declarar el tipo de los elementos al crear la colecci´n.o En particular, la clase ArrayList E representa una secuencia indexada de elementos, cada uno de ellos ocupa una posici´n dentro de la estructura, y o se puede acceder a un elementos dentro de la estructura a trav´s de su ´ e ındice. Un ejemplo de c´mo usar las clases colecci´n se muestra en los siguientes o o listados: el Listado 8.13 muestra la interface Figura que declara un m´todo para e que las clases que la implementen definan la funcionalidad del c´lculo del ´rea a a de una figura. El Listado 8.14 define la clase Circulo de cuyas instancias pode- mos calcular el ´rea. An´logamente, el Listado 8.15 define la clase Rectangulo a a de cuyas instancias tambi´n podemos calcular el ´rea. Finalmente, el Listado e a 8.16 define la clase TrianguloRectangulo de cuyas instancias tambi´n podemos e calcular el ´rea. El Listado 8.17 muestra un ejemplo de c´mo utilizar la clase a o ArrayList F igura para contener referencias a clases que implementen la interface Figura, a ella podemos a˜adir c´ n ırculos, cuadrados y tri´ngulos, y los a podemos recuperar utilizando un bucle for...each.1 package c o l e c c i o n e s . f i g u r a s ;23 public i n t e r f a c e F i g u r a {4 public s t a t i c f i n a l double PI = 3 . 1 4 1 5 9 2 ;5 public double g e t A r e a ( ) ;6 } Listado 8.13: Interface que declara la funcionalidad del c´lculo del ´rea de una a a figura geom´trica. e1 package c o l e c c i o n e s . f i g u r a s ;23 public c l a s s C i r c u l o implements F i g u r a {4 private double r a d i o ;56 public C i r c u l o ( ) {7 super ( ) ;8 }910 public C i r c u l o ( double r a d i o ) {11 this . radio = radio ;12 }1314 @Override15 public double g e t A r e a ( ) {16 return PI ∗ r a d i o ∗ r a d i o ;17 }1819 @Override20 public S t r i n g t o S t r i n g ( ) {21 S t r i n g B u i l d e r b u i l d e r = new S t r i n g B u i l d e r ( ) ;22 b u i l d e r . append ( C i r c u l o [ r a d i o = ) ;23 b u i l d e r . append ( r a d i o ) ;24 b u i l d e r . append ( ] ) ;25 b u i l d e r . append ( A r e a = ) ;26 b u i l d e r . append ( g e t A r e a ( ) ) ;27 return b u i l d e r . t o S t r i n g ( ) ;28 }
  • 125. 126CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR29 } Listado 8.14: Esta clase representa un c´ ırculo del que se puede calcular su ´rea. a1 package c o l e c c i o n e s . f i g u r a s ;23 public c l a s s R e c t a n g u l o implements F i g u r a {4 private double b a s e ;5 private double a l t u r a ;67 public R e c t a n g u l o ( ) {8 super ( ) ;9 }1011 public R e c t a n g u l o ( double base , double a l t u r a ) {12 super ( ) ;13 this . base = base ;14 this . altura = altura ;15 }1617 @Override18 public double g e t A r e a ( ) {19 return b a s e ∗ a l t u r a ;20 }2122 @Override23 public S t r i n g t o S t r i n g ( ) {24 S t r i n g B u i l d e r b u i l d e r = new S t r i n g B u i l d e r ( ) ;25 b u i l d e r . append ( R e c t a n g u l o [ a l t u r a = ) ;26 b u i l d e r . append ( a l t u r a ) ;27 b u i l d e r . append ( , b a s e = ) ;28 b u i l d e r . append ( b a s e ) ;29 b u i l d e r . append ( ] ) ;30 b u i l d e r . append ( A r e a = ) ;31 b u i l d e r . append ( g e t A r e a ( ) ) ;32 return b u i l d e r . t o S t r i n g ( ) ;33 }34 } Listado 8.15: Esta clase representa un rect´ngulo del que se puede calcular su a a ´rea.1 package c o l e c c i o n e s . f i g u r a s ;23 public c l a s s T r i a n g u l o R e c t a n g u l o implements F i g u r a {4 private double b a s e ;5 private double a l t u r a ;67 public T r i a n g u l o R e c t a n g u l o ( ) {8 super ( ) ;9 }1011 public T r i a n g u l o R e c t a n g u l o ( double base , double a l t u r a ) {12 super ( ) ;13 this . base = base ;14 this . altura = altura ;15 }1617 @Override18 public double g e t A r e a ( ) {19 return b a s e ∗ a l t u r a / 2 ;20 }2122 @Override23 public S t r i n g t o S t r i n g ( ) {24 S t r i n g B u i l d e r b u i l d e r = new S t r i n g B u i l d e r ( ) ;25 b u i l d e r . append ( T r i a n g u l o R e c t a n g u l o [ a l t u r a = ) ;26 b u i l d e r . append ( a l t u r a ) ;27 b u i l d e r . append ( , b a s e = ) ;
  • 126. 8.4. COLECCIONES 12728 builder . append ( b a s e ) ;29 builder . append ( ] ) ;30 builder . append ( A r e a = ) ;31 builder . append ( g e t A r e a ( ) ) ;32 return builder . toString () ;33 }34 } Listado 8.16: Esta clase representa un tri´ngulo rect´ngulo del que se puede a a calcular su ´rea. a1 package c o l e c c i o n e s . f i g u r a s ;23 import j a v a . u t i l . A r r a y L i s t ;45 public f i n a l c l a s s P r i n c i p a l {6 public s t a t i c void main ( S t r i n g [ ] a r g s ) {7 A r r a y L i s t F i g u r a f i g u r a s = new A r r a y L i s t F i g u r a () ;8 f i g u r a s . add (new C i r c u l o ( 1 ) ) ;9 f i g u r a s . add (new R e c t a n g u l o ( 1 , 2 ) ) ;10 f i g u r a s . add (new T r i a n g u l o R e c t a n g u l o ( 1 , 2 ) ) ;11 for ( Figura f i g u r a : f i g u r a s )12 System . o u t . p r i n t l n ( f i g u r a ) ;13 }14 } Listado 8.17: Ejemplo de uso de la clase ArrayList F igura . La ejecuci´n de esta peque˜a aplicaci´n muestra el siguiente resultado: o n o Circulo [radio=1.0] Area=3.141592 Rectangulo [altura=2.0, base=1.0] Area=2.0 TrianguloRectangulo [altura=2.0, base=1.0] Area=1.0 Si en el ArrayList F igura del Listado 8.17 intent´semos a˜adir una a n instancia de una clase que no implementa el interface Figura, obtendr´ ıamos un error en tiempo de compilaci´n. Si no indic´semos el tipo de los datos que o a maneja la colecci´n en el momento de la creaci´n del ArrayList, el compilador o o no hubiese detectado el error, y se producir´ en tiempo de compilaci´n al extraer ıa o el elemento err´neo y modelarlo a la interface com´n Figura, tal y como muestra o u el Listado 8.18 y el resultado de su ejecuci´n. o1 package c o l e c c i o n e s . f i g u r a s ;23 import j a v a . u t i l . A r r a y L i s t ;45 public c l a s s P r i n c i p a l 2 {6 public s t a t i c void main ( S t r i n g [ ] a r g s ) {7 A r r a y L i s t f i g u r a s = new A r r a y L i s t ( ) ;8 f i g u r a s . add (new C i r c u l o ( 1 ) ) ;9 f i g u r a s . add (new R e c t a n g u l o ( 1 , 2 ) ) ;10 f i g u r a s . add (new T r i a n g u l o R e c t a n g u l o ( 1 , 2 ) ) ;11 f i g u r a s . add (new I n t e g e r ( 1 ) ) ;12 for ( Object f i g u r a : f i g u r a s )13 System . o u t . p r i n t l n ( ( F i g u r a ) f i g u r a ) ;14 }15 } Listado 8.18: Ejemplo de uso de la clase ArrayList sin especificar el tipo de los elementos de la colecci´n. Se producir´ un error en tiempo de ejecuci´n. o a o
  • 127. 128CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR Circulo [radio=1.0] Area=3.141592 Rectangulo [altura=2.0, base=1.0] Area=2.0 TrianguloRectangulo [altura=2.0, base=1.0] Area=1.0 Exception in thread main java.lang.ClassCastException: java.lang.Integer cannot be cast to colecciones.figuras.Figura at colecciones.figuras.Principal.main(Principal.java:13) Otros m´todos utiles de la clase ArrayList E (que comparte con el resto e ´ de clases que implementan la interface List) son: E get(int posicion), de- vuelve el elementos en la posici´n indicada; void clear() elimina todos los ele- o mentos; boolean contains(Object o), de vuelve true si el elementos est´ en a la lista y false en caso contrario; boolean isEmpty(), devuelve true si no el ArrayList E no contiene ning´n elemento y false en caso contrario; int u size(), devuelve el n´mero de elementos. u 8.5. Trabajo con fechas El paquete de clases de utilidad de Java nos proporciona un conjunto de clases para trabajar con fechas y especificar el formato cuando la fecha se muestre como texto. 8.5.1. La clase Date La clase Date representa un instante del tiempo con una precisi´n de milise- o gundos. Para obtener el instante de tiempo actual, simplemente creamos una instancia de esta clase a partir de su constructor por defecto. La informaci´n se o almacena como un entero long cuyo origen de tiempos es el 1 de Enero de 1970 a las 00:00:00 horas. Muchos de los m´todos de esta clase est´n obsoletos, en particular, todos e a aquellos m´todos relacionados con el trabajo con fechas. Para trabajar con fe- e chas, por ejemplo para saber qu´ d´ de la semana ser´ mi pr´ximo cumplea˜os, e ıa a o n se utiliza la clase GregorianCalendar tal y como veremos en la Secci´n 8.5.2. o Un sencillo ejemplo de uso de la clase Date se muestra en el Listado 8.19, lo que mostrar´ por consola: ıa Ahora: Fri Jul 02 10:19:40 CEST 2010 donde se est´ utilizando un formato anglosaj´n para mostrar la fecha. a o1 System . o u t . p r i n t l n ( A h o r a : + new Date ( ) ) ; Listado 8.19: Uso de la clase Date para mostrar el instante actual. La clase SimpleDateFormat nos permite definir el formato con el que que- remos mostrar las fechas. Para una descripci´n de los s´ o ımbolos que se pueden utilizar al especificar el formato, se puede consultar la documentaci´n de es- o ta clase en esta direcci´n http://java.sun.com/javase/6/docs/api/java/ o text/SimpleDateFormat.html, aqu´ s´lo mostraremos el ejemplo del Listado ı o 8.20, donde se est´ utilizando EEEE para mostrar el d´ de la semana, dd para a ıa mostrar el ordinal del d´ dentro del mes, MMMM para mostrar el nombre ıa
  • 128. ´ 8.6. MATEMATICAS 129 del mes completo, yyyy para mostrar el a˜o, hh para mostrar la hora en for- n mato 24 horas, mm para mostrar los minutos de la hora, y ss para mostrar los segundos dentro del minuto. Con este formato, el texto obtenido es: Ahora: viernes 02 de julio de 2010 (10:30:29)1 SimpleDateFormat s d f = new SimpleDateFormat ( E E E E d d ’ d e ’ M M M M ’ d e ’ y y y y ’( ’ h h ’: ’ m m ’: ’ s s ’) ’ ) ;2 System . o u t . p r i n t l n ( A h o r a : + s d f . f o r m a t (new Date ( ) ) ) ; Listado 8.20: Uso de la clase SimpleDateFormat para definir el formato de una fecha como una cadena de texto. 8.5.2. Las clases Calendar y GregorianCalendar La clase Calendar nos permite convertir instantes temporales, representados por la clase Date, a un fecha expresada en d´ meses, a˜o, horas, minutos, ıas, n segundos. La clase Calendar es abstracta, por lo que no se puede instanciar. La clase Calendar nos proporciona la funcionalidad m´ ınima para trabajar con fechas, y otras extensiones de esta clase implementan calendarios concretos. Este es el caso de la clase GregorianCalendar que implementa el c´lculo de fechas a en el calendario gregoriano. Esta clase nos permite, por ejemplo, saber qu´ d´ e ıa de la semana ser´ dentro de 40 d´ tal y como muestra el Listado 8.21. a ıas,1 SimpleDateFormat s d f = new SimpleDateFormat ( E E E E d d ’ d e ’ M M M M ’ d e ’ yyyy ) ;2 G r e g o r i a n C a l e n d a r c a l e n d a r i o = new G r e g o r i a n C a l e n d a r ( ) ;3 System . o u t . p r i n t l n ( A h o r a . + s d f . f o r m a t ( c a l e n d a r i o . getTime ( ) ) ) ;4 c a l e n d a r i o . add ( C a l e n d a r . DAY OF YEAR, 4 0 ) ;5 System . o u t . p r i n t l n ( D e n t r o d e 4 0 d ı a s : + s d f . f o r m a t ( c a l e n d a r i o . ´ getTime ( ) ) ) ; Listado 8.21: La clase GregorianCalendar nos permite trabajar con fechas como, por ejemplo, sumar una cantidad de d´ a la fecha actual para conocer ıas la nueva fecha Todas las constantes para indicar el d´ mes, etc´tera est´n definidas en la ıa, e a clase Calendar. 8.6. Matem´ticas a En el paquete est´ndar de Java encontramos algunas clases para hacer c´lculos a a matem´ticos, algunas de ellas son la clase Math y la clase Random. a 8.6.1. La clase Math La clase Math nos proporciona algunas de las funciones matem´ticas trigo- a nom´tricas, logar´ e ıtmicas y otras de utilidad. Todos los m´todos de esta clase e son static, por lo que no hace falta crear una instancia para poder utilizarlos. El Listado 8.22 muestra algunos ejemplos sencillos de las funciones matem´ti- a cas que proporciona la clase Math.
  • 129. 130CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR1 System . o u t . p r i n t l n ( S t r i n g . f o r m a t ( E l s e n o d e % 2 . 2 f e s % 1 . 3 f , Math . PI / 4 , Math . s i n ( Math . PI / 4 ) ) ) ;2 System . o u t . p r i n t l n ( S t r i n g . f o r m a t ( L a r a ´ z c u a d r a d a d e 2 e s % 1 . 4 f , ı Math . s q r t ( 2 ) ) ) ;3 System . o u t . p r i n t l n ( S t r i n g . f o r m a t ( E l l o g a r i t m o n a t u r a l d e % 1 . 3 f e s % 1 . 3 f , Math . E , Math . l o g ( Math . E) ) ) ; Listado 8.22: Algunas funciones de la clase Math. El resultado de la ejecuci´n del Listado 8.22 es el siguiente: o El seno de 0,79 es 0,707 La ra´z cuadrada de 2 es 1,4142 ı El logaritmo natural de 2,718 es 1,000 8.6.2. La clase Random La clase Random genera secuencias aleatorias de n´meros o valores booleanos. La u secuencia de n´meros es reproducible si al crear la instancia de Random utiliza- u mos la misma semilla. Los valores de las secuencias son equiprobables, excepto si utilizamos el m´todo double nextGaussian(), en cuyo caso la secuencia ge- e nerada sigue una distribuci´n aleatoria de media 0 y desviaci´n est´ndar 1. El o o a Listado 8.23 muestra algunos ejemplos de uso de los m´todos que proporciona e esta clase.1 Random e q u i p r o b a b l e s = new Random ( ) ;2 System . o u t . p r i n t l n ( U n a s e c u e n c i a s a l e a t o r i a e q u i p r o b a b l e d e n ´ m e r o s u entre 0 y 100. ) ;3 f o r ( i n t i = 0 ; i 1 0 ; i ++)4 System . o u t . p r i n t ( e q u i p r o b a b l e s . n e x t I n t ( 1 0 0 ) + ; ) ;56 System . o u t . p r i n t l n ( U n a s e c u e n c i a s a l e a t o r i a g a u s s i a n a d e n ´ m e r o s . ) ; u7 f o r ( i n t i = 0 ; i 1 0 ; i ++)8 System . o u t . p r i n t ( S t r i n g . f o r m a t ( %.2 f ; , e q u i p r o b a b l e s . n e x t G a u s s i a n () ) ) ; Listado 8.23: Secuencias de valores aleatorios generados por la clase Random. Cuestiones. 1. Si lees el API de la clase Calendar observar´s que aunque esta clase es abs- a tracta, posee el m´todo public static Calendar getInstance() que e devuelve una referencia a Calendar perfectamente funcional y con la que podemos trabajar con fechas como si de la clase GregorianCalendar se tratase. ¿C´mo es esto posible siendo la clase Calendar abstracta?. o Ejercicios. 1. Escribe un programa que calcule la cuota mensual de una hipoteca por el sistema franc´s, dada por la siguiente f´rmula: e o capital · n mensualidad = 1 1− (1 + n)(12·a˜ os) n
  • 130. ´8.6. MATEMATICAS 131 interes donde n = . 1200Lecturas recomendadas. El Cap´ ıtulo 17 de la referencia [2] muestra nuevas clases de utilidad.
  • 131. 132CAP´ ´ ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR
  • 132. Cap´ ıtulo 9Programaci´n con gen´ricos o eContenidos 9.1. ¿Qu´ son los tipos de datos gen´ricos? . . . . . . . 133 e e 9.2. M´todos gen´ricos . . . . . . . . . . . . . . . . . . . 134 e e 9.3. Clases gen´ricas . . . . . . . . . . . . . . . . . . . . 135 e 9.4. Ampliaci´n del tipo gen´rico . . . . . . . . . . . . 138 o e 9.4.1. Tipos gen´ricos con l´ e ımite superior . . . . . . . . . . 139 9.4.2. Comodines . . . . . . . . . . . . . . . . . . . . . . . 139 9.5. Borrado de tipo y compatibilidad con c´digo he-o redado . . . . . . . . . . . . . . . . . . . . . . . . . . 141Introducci´n o En este cap´ıtulo se presenta la programaci´n con gen´ricos, que fue intro- o educida en Java en la versi´n 5. Los gen´ricos nos permiten definir clases que o etrabajar´n con instancias de objetos de los que no especificamos su tipo en ael momento de la definici´n de la clase. El tipo de las referencias que la clase omanejar´ se especifica en el momento de crear las instancias de la clase gen´rica. a e El concepto de programaci´n con gen´ricos es muy potente, como has visto o een la secci´n 8.4 dedicada a las clases colecci´n. Las clases colecci´n son conte- o o onedores de referencias de las que se especifica su tipo en el momento de crearla instancia de la clase colecci´n. Como vimos, una ventaja del uso de gen´ricos o ees asegurar que las referencias con las que trabaja la clase gen´rica son de tipo ecompatible con el especificado en el momento de la instanciaci´n de la clase ogen´rica, de lo contrario, obtendremos un error en tiempo de compilaci´n, error e oque pasar´ desapercibido sin el uso de tipos gen´ricos. ıa e9.1. ¿Qu´ son los tipos de datos gen´ricos? e eSin detenernos en detalles, en la secci´n 8.4 vimos c´mo utilizar las clases co- o olecci´n, y vimos que estas clases pueden trabajar con cualquier tipo de datos, obasta con indicarlo en el momento de instanciar la clase colecci´n. Vimos la o 133
  • 133. 134 CAP´ ´ ´ ITULO 9. PROGRAMACION CON GENERICOS gran ventaja que esto supon´ al no trabajar con referencias a la clase Object ıa que deb´ ıamos modelar al tipo adecuado al extraer los elementos de la colecci´n. o Al trabajar sin gen´ricos podemos cometer errores (introducir una referencia de e tipo incompatible con el resto de elementos en la colecci´n), de los que no nos o daremos cuenta en tiempo de compilaci´n, causando graves problemas durante o la ejecuci´n de nuestras aplicaciones. El uso de gen´ricos hace posible la detec- o e ci´n de estos errores de incompatibilidad de tipos durante la fase de compilaci´n o o haciendo que nuestro c´digo sea m´s robusto. o a Un tipo de datos gen´rico es un tipo de datos que no se especifica, unicamente e ´ se indica que se utilizar´ alg´n tipo de dato pero no se indica el tipo concreto a u hasta que no se utiliza. Los tipos de datos gen´ricos se pueden utilizar en la e definici´n de un m´todo, o en la definici´n de una clase. o e o 9.2. M´todos gen´ricos e e Un m´todo definido en una clase puede trabajar con un tipo gen´rico aunque la e e clase no lo haga. El Listado 9.1 muestra un ejemplo de un m´todo que trabaja e con un tipo gen´rico. F´ e ıjate en la declaraci´n del m´todo private Tvoid o e muestraNombreClase(T t), la T indica que se va a utilizar un tipo gen´rico, e de modo que la lista de argumentos (T t) se interpreta como una referencia de tipo gen´rico T. El modo de uso es una simple llamada al m´todo como e e por ejemplo metodoGenerico.muestraNombreClase(new Float(1));, en este caso, el tipo gen´rico T se sustituye por el tipo particular Float, de modo que e el m´todo se reescribe como private void muestraNombreClase(Float t). e1 package g e n e r i c o s ;23 import t i p o s . P e r s o n a ;45 public f i n a l c l a s s MetodoGenerico {6 private MetodoGenerico ( ) {7 super ( ) ;8 }910 private T void muestraNombreClase (T t ) {11 System . o u t . p r i n t l n ( S o y u n a i n s t a n c i a d e l a c l a s e : + t . g e t C l a s s ( ) . getCanonicalName ( ) ) ;12 }1314 public s t a t i c void main ( S t r i n g [ ] a r g s ) {15 MetodoGenerico m e t o d o G e n e r i c o = new MetodoGenerico ( ) ;16 m e t o d o G e n e r i c o . muestraNombreClase (new F l o a t ( 1 ) ) ;17 m e t o d o G e n e r i c o . muestraNombreClase (new P e r s o n a ( ) ) ;18 }19 } Listado 9.1: Definici´n y uso de un m´todo gen´rico. o e e Sintaxis de Java Para indicar que un m´todo trabaja con tipos gen´ricos se escribe T entre e e el modificador de acceso y el tipo de dato de retorno del m´todo. e Lo que ocurre en el momento de la compilaci´n en el ejemplo anterior, es o que se sustituye el s´ ımbolo de tipo gen´rico T por el tipo concreto (Float) en e
  • 134. ´ 9.3. CLASES GENERICAS 135 el ejemplo del Listado 9.1. Si el m´todo trabaja con un par de tipos gen´ricos que pueden ser diferentes e e se indica como private T, U void metodo(T t, U u). Convenci´n de codificaci´n o o Se usa la letra T para indicar el primer tipo gen´rico, si es necesario indicar e m´s tipos gen´ricos se toman las letras may´sculas que siguen a T, por ejemplo a e u T, U. La convenci´n utiliza las siguientes letras para indicar tipos gen´ricos: o e E, Indica Elemento, como en el caso de las clase Colecci´n. o K, Indica Clave, como en el caso de los Mapas. N, Indica Numero. T, S, U, V, etc. Indica Tipo. V, Indica Valor, como en el caso de los Mapas. 9.3. Clases gen´ricas e Supongamos que queremos definir una clase que represente una medida tomada por un sensor, de modo que cada medida almacena el valor del dato medido y un comentario descriptivo. Inicialmente no conocemos los tipos de medidas que nos puede devolver un sensor, existen sensores de temperatura que miden temperaturas, y tambi´n sensores de viento que miden la intensidad y direcci´n e o del viento, por lo que no podemos decidir en el momento de la definici´n de la o clase Sensor la naturaleza del dato que representa la medida. ¿C´mo lo podemos o resolver? Evidentemente, utilizando gen´ricos. La idea es dejar sin especificar el e tipo de datos que representa la medida tal y como se muestra en el Listado 9.2.1 package s e n s o r e s ;23 public abstract c l a s s S e n s o r T {4 protected T medida ;5 private S t r i n g d e s c r i p c i o n ;67 public T g e t M e d i c i o n ( ) {8 return medida ;9 }1011 public f i n a l void s e t D e s c r i p c i o n ( S t r i n g d e s c r i p c i o n ) {12 this . d esc ri pc ion = d esc ri pci on ;13 }1415 public S t r i n g g e t D e s c r i p c i o n ( ) {16 return d e s c r i p c i o n ;17 }18 } Listado 9.2: La clase Sensor no especifica el tipo de dato que proporciona, se indica con T. Ahora es el momento de crear algunos sensores concretos. El primer sensor que implementaremos es un sensor de temperaturas. Ya que la temperatura se puede especificar como un n´mero real, elegimos la clase Float para representar u
  • 135. 136 CAP´ ´ ´ ITULO 9. PROGRAMACION CON GENERICOS la medida de temperatura de modo que nuestra clase SensorTemperatura la podemos definir tal y como muestra el Listado 9.3.1 package s e n s o r e s ;23 import j a v a . u t i l . Random ;45 public c l a s s S e n s o r T e m p e r a t u r a extends S e n s o r F l o a t {6 private s t a t i c f i n a l f l o a t TEMPERATURA MAXIMA = 4 5 ;7 private Random t e m p e r a t u r a = new Random ( ) ;89 public S e n s o r T e m p e r a t u r a ( ) {10 super ( ) ;11 medida = new F l o a t (TEMPERATURA MAXIMA∗ t e m p e r a t u r a . n e x t F l o a t ( ) ) ;12 setDescripcion () ;13 }1415 public f i n a l void s e t D e s c r i p c i o n ( ) {16 super . s e t D e s c r i p c i o n ( D a t o d e t e m p e r a t u r a e n g r a d o s C e l s i u s ) ;17 }18 } Listado 9.3: La clase SensorTemperatura define un sensor capaz de registrar temperaturas representadas como Float Para que se vea con mayor claridad el uso de gen´ricos, vamos a completar el e ejemplo definiendo un nuevo sensor, esta vez un sensor de velocidad del viento capaz de tomar datos de la intensidad y direcci´n del viento. Esta vez ning´n o u tipo de dato primitivo nos sirve para representar la medida de la velocidad del viento, ya que este tiene intensidad y direcci´n, as´ que definimos una nueva o ı clase que represente la medida de la velocidad del viento, tal y como muestra el Listado 9.4.1 package s e n s o r e s ;23 public c l a s s V e l o c i d a d V i e n t o {4 private f l o a t i n t e n s i d a d ;5 private D i r e c c i o n d i r e c c i o n ;67 public c l a s s D i r e c c i o n {8 f l o a t vx ;9 f l o a t vy ;1011 public D i r e c c i o n ( f l o a t vx , f l o a t vy ) {12 t h i s . vx = vx ;13 t h i s . vy = vy ;14 }1516 public S t r i n g t o S t r i n g ( ) {17 return [ + vx + , + vy + ] ;18 }19 }2021 public V e l o c i d a d V i e n t o ( ) {22 super ( ) ;23 }2425 public V e l o c i d a d V i e n t o ( f l o a t i n t e n s i d a d , f l o a t vx , f l o a t vy ) {26 this . intensidad = intensidad ;27 d i r e c c i o n = new D i r e c c i o n ( vx , vy ) ;28 }2930 public double g e t I n t e n s i d a d ( ) {31 return i n t e n s i d a d ;32 }3334 public D i r e c c i o n g e t D i r e c c i o n ( ) {
  • 136. ´ 9.3. CLASES GENERICAS 13735 return d i r e c c i o n ;36 }3738 @Override39 public S t r i n g t o S t r i n g ( ) {40 return I n t e n s i d a d : + i n t e n s i d a d + D i r e c c i ´ n : + d i r e c c i o n ; o41 }42 } Listado 9.4: La clase VelocidadVieto define una clase que almacena la intensidad y la direcci´n de una medida del viento. o Con esta nueva clase, la definici´n de un sensor de velocidad del viento es la o mostrada en el Listado 9.51 package s e n s o r e s ;23 import j a v a . u t i l . Random ;45 import s e n s o r e s . V e l o c i d a d V i e n t o . D i r e c c i o n ;67 public c l a s s S e n s o r V e l o c i d a d V i e n t o extends S e n s o r V e l o c i d a d V i e n t o {8 private s t a t i c f i n a l f l o a t VELOCIDAD MAXIMA = 1 0 0 ;9 private Random v i e n t o = new Random ( ) ;1011 public S e n s o r V e l o c i d a d V i e n t o ( ) {12 super ( ) ;13 setMedida ( ) ;14 setDescripcion () ;15 }1617 public f i n a l void s e t D e s c r i p c i o n ( ) {18 super . s e t D e s c r i p c i o n ( M i d e l a v e l o c i d a d y d i r e c c i ´ n d e l v i e n t o . ) ; o1920 }2122 private f i n a l void s e t M e d i d a ( ) {23 f l o a t a n g u l o = ( f l o a t ) ( v i e n t o . n e x t F l o a t ( ) ∗ Math . PI ) ;24 f l o a t s e n o = ( f l o a t ) ( Math . s i n ( a n g u l o ) ) ;25 f l o a t c o s e n o = ( f l o a t ) ( Math . c o s ( a n g u l o ) ) ;26 medida = new V e l o c i d a d V i e n t o (VELOCIDAD MAXIMA∗ v i e n t o . n e x t F l o a t ( ) , seno , coseno ) ;27 }28 } Listado 9.5: La clase SensorVelocidadVieto define un sensor capaz de registrar la velocidad del viento. En ambos casos de sensores, estamos generando de modo aleatorio el valor del dato le´ ıdo, y este no se modifica durante la ejecuci´n de la aplicaci´n. Cuando o o veamos en el Cap´ ıtulo 14 c´mo trabajar con Hilos en Java, reescribiremos el o c´digo de esta aplicaci´n para que sea m´s realista. Finalmente, un ejemplo del o o a uso de estas clase se muestra en el Listado 9.6.1 package p r i n c i p a l ;23 import s e n s o r e s . S e n s o r T e m p e r a t u r a ;4 import s e n s o r e s . S e n s o r V e l o c i d a d V i e n t o ;56 public f i n a l c l a s s P r i n c i p a l {7 private P r i n c i p a l ( ) {8 super ( ) ;9 }1011 private void e j e c u t a ( ) {12 S e n s o r T e m p e r a t u r a s e n s o r T = new S e n s o r T e m p e r a t u r a ( ) ;13 System . o u t . p r i n t l n ( T e m p e r a t u r a : + s e n s o r T . g e t M e d i c i o n ( ) ) ;
  • 137. 138 CAP´ ´ ´ ITULO 9. PROGRAMACION CON GENERICOS14 S e n s o r V e l o c i d a d V i e n t o s e n s o r V = new S e n s o r V e l o c i d a d V i e n t o ( ) ;15 System . o u t . p r i n t l n ( V i e n t o : + s e n s o r V . g e t M e d i c i o n ( ) ) ;16 }1718 public s t a t i c void main ( S t r i n g [ ] a r g s ) {19 P r i n c i p a l p r i n c i p a l = new P r i n c i p a l ( ) ;20 principal . ejecuta () ;21 }22 } Listado 9.6: Este listado muestra c´mo usar los sensores los tipos de sensores o anteriormente definidos. Un ejemplo del resultado de la ejecuci´n del c´digo del Listado 9.6 es: o o Temperatura: 1.4107025 Viento: Intensidad: 40.831844 Direcci´n: [0.7000265, -0.7141168] o Siguiendo el mismo esquema, basta definir el tipo de dato que mide el sen- sor, podemos crear cualquier tipo de sensor extendiendo a la clase Sensor, y autom´ticamente ser´ capaz de devolvernos el dato que mide. a a 9.4. Ampliaci´n del tipo gen´rico o e Para seguir profundizando en las posibilidades de los tipos gen´ricos, retome- e mos el ejemplo de la Secci´n 8.4 de las figuras: c´ o ırculos, tri´ngulos rect´ngu- a a los y rect´ngulos de las que podemos calcular su ´rea. Sabemos que la cla- a a se ArrayListE es un contenedor que trabaja con gen´ricos, luego podemos e crear un ArrayListFigura para almacenar en ´l cualquier tipo de figura que e se pueda dibujar, tal y como muestra el Listado 9.7.1 // Una l i s t a de f i g u r a s .2 A r r a y L i s t F i g u r a f i g u r a s = new A r r a y L i s t F i g u r a () ;3 // C´r c u l o s , t r i ´ n g u l o s r e c t a n g u l o s y r e c t a n g u l o s s on f i g u r a s . ı a ´ ´4 f i g u r a s . add (new C i r c u l o ( ) ) ;5 f i g u r a s . add (new T r i a n g u l o R e c t a n g u l o ( ) ) ;6 f i g u r a s . add (new R e c t a n g u l o ( ) ) ; Listado 9.7: Una lista de figuras. Ahora creamos una lista s´lo para almacenar c´ o ırculos, y una vez creada, ya que la clase Circulo implementa el interface Figura, intentamos asignar a la lista de figuras figuras la lista que s´lo almacena c´ o ırculos circulos, tal y como muestra el Listado 9.8.7 // Ahora un A r r a y L i s t s o l o de c´ r c u l o s ´ ı8 A r r a y L i s t C i r c u l o c i r c u l o s = new A r r a y L i s t C i r c u l o () ;9 c i r c u l o s . add (new C i r c u l o ( 3 ) ) ;10 c i r c u l o s . add (new C i r c u l o ( 5 ) ) ;11 // f i g u r a s = c i r c u l o s ; // ERROR ! ! ! Listado 9.8: Una lista de c´ ırculos. Para nuestra sorpresa, en la l´ ınea de c´digo 11 hay un error, no podemos o asignar a un ArrayListFiguras un ArrayListCirculo, aunque la clase Circulo implemente el interface Figura, la conversi´n de tipos no es posi- o ble. Si pudi´semos hacer la asignaci´n, podr´ e o ıamos a˜adir a la lista de c´ n ırculos cualquier otra figura a trav´s de la referencia figuras. e
  • 138. ´ ´ 9.4. AMPLIACION DEL TIPO GENERICO 139 Si intentamos escribir un m´todo capaz de mostrar el ´rea de todas las figuras e a de una lista tal y como muestra el Listado 9.9, no tendremos ning´n error cuando u mostremos las ´reas de las figuras de la referencia figuras, pero obtendremos a el mismo error que en el Listado 9.8 si lo intentamos con la referencia circulos. El motivo es el mismo que antes, el tipo ArrayListFigura es diferente al tipo ArrayListCirculo.20 private void m u e s t r a A r e a s ( A r r a y L i s t F i g u r a l i s t a ) {21 for ( Figura elemento : l i s t a ) {22 System . o u t . p r i n t l n ( e l e m e n t o . g e t C l a s s ( ) . getCanonicalName ( ) + : + elemento . getArea ( ) ) ;23 }24 } Listado 9.9: Un m´todo que recibe una lista de elementos de tipo interface e Figura. ¿C´mo podemos resolver esta encrucijada? ¿C´mo podemos indicar que que- o o remos una lista de cualquier cosa que implemente la Figura para poder asignar a una lista de figuras unas veces listas de s´lo c´ o ırculos y otras veces listas de s´lo rect´ngulos? o a Java nos da una soluci´n para este caso, los tipos gen´ricos con l´ o e ımite supe- rior. 9.4.1. Tipos gen´ricos con l´ e ımite superior El Listado 9.10 muestra un m´todo en el que se da un l´ e ımite superior al tipo de datos gen´rico. E extends Figura indica que los elementos que se pueden e almacenar en la colecci´n pueden ser de cualquier subtipo de Figura. F´ o ıjate que aunque Figura es una interface se utiliza la palabra reservada extends y no implements. De este modo, podemos llamar a este m´todo con cualquier e ArrayListT siempre que sus elementos implementen la interface Figura.22 private E extends F i g u r a void m u e s t r a A r e a s 2 ( A r r a y L i s t E l i s t a ) {23 for ( Figura elemento : l i s t a ) {24 System . o u t . p r i n t l n ( e l e m e n t o . g e t C l a s s ( ) . getCanonicalName ( ) + : + elemento . getArea ( ) ) ;25 }26 } Listado 9.10: Un m´todo que recibe una lista gen´rica de elementos que e e implementan la interface Figura. De este modo podemos restringir los tipos concretos de las clases gen´ricas. e En el Listado 9.10 estamos restringiendo el tipo concreto de la clase gen´rica a e alg´n tipo que extienda, o implemente, el tipo Figura. u 9.4.2. Comodines La l´ ınea 13 del Listado 9.11 presenta una nueva construcci´n del lenguaje: o ArrayList? extends FigurafigurasGeneral, donde ? es un comod´ Esta ın. construcci´n significa un ArrayList de cualquier tipo que implemente (o ex- o tienda) a Figura, es decir, a la referencia figurasGeneral s´ que le podemos ı asignar cualquier otro ArrayList de tipos concretos si esos tipos implementan
  • 139. 140 CAP´ ´ ´ ITULO 9. PROGRAMACION CON GENERICOS (o extienden) el interface Figura. De nuevo, f´ıjate que aunque, como en este caso, las clases finales implementan un interfaz, el ArrayList utiliza la pala- bra reservada extends, dicho de otro modo se utiliza siempre extends con el significado de subtipo sea este por extensi´n (extends) o por implementaci´n o o (implements) de una interface.12 // Una l i s t a con l´ m i t e s u p e r i o r ı13 A r r a y L i s t ? extends F i g u r a f i g u r a s G e n e r a l = c i r c u l o s ;14 A r r a y L i s t R e c t a n g u l o r e c t a n g u l o s = new A r r a y L i s t R e c t a n g u l o () ;15 r e c t a n g u l o s . add (new R e c t a n g u l o ( ) ) ;16 r e c t a n g u l o s . add (new R e c t a n g u l o ( 1 , 2 ) ) ;17 figurasGeneral = rectangulos ; Listado 9.11: Con el uso de comodines podemos definir listas de tipos que extiendan la interace Figura Ahora s´ tanto en la l´ ı, ınea 13 como 17 podemos hacer la asignaci´n. Pero o debemos pagar un precio por esta nueva posibilidad, y este es que no pode- mos a˜adir elementos al ArrayList a trav´s de la referencia figurasGeneral. n e No podemos escribir algo como figurasGeneral.add(new Circulo(). Es importante no olvidar esta restricci´n. o ¿Para qu´ nos sirve entonces esta posibilidad? Aunque no podamos a˜adir e n nuevos elementos a esta lista, s´ que podemos trabajar con los elementos que ı hay en ella, de modo que podemos reescribir el m´todo del Listado 9.9 para e mostrar el ´rea de todas las figuras contenidas en la lista, con independencia a del tipo de elementos con el que se defini´ la lista tal y como muestra el Listado o 9.12.28 private void m u e s t r a A r e a s 3 ( A r r a y L i s t ? extends F i g u r a l i s t a ) {29 for ( Figura elemento : l i s t a ) {30 System . o u t . p r i n t l n ( e l e m e n t o . g e t C l a s s ( ) . getCanonicalName ( ) + : + elemento . getArea ( ) ) ;31 }32 } Listado 9.12: Un m´todo que recibe una lista gen´rica de elementos que e e implementan la interface Figura utilizando comodines. El m´todo e private void muestraAreas3(ArrayList? extends Figuralista), es capaz de mostrar el ´rea de los elementos de cual- a quier lista de figuras, y no hemos utilizado el l´ımite superior en la restricci´n o de tipo E extends Figura como en el caso del Listado 9.10. De modo an´logo, podemos indicar que una clase gen´rica trabaja con re- a e ferencias de cualquier clase padre de una clase dada, por si esto nos pudiera interesar. En este caso la sintaxis de la construcci´n se muestra en el Listado o 9.13.1 A r r a y L i s t ? super C i r c u l o o t r a L i s t a ; Listado 9.13: El tipo de esta clase puede ser cualquier padre de la clase Circulo.
  • 140. ´9.5. BORRADO DE TIPO Y COMPATIBILIDAD CON CODIGO HEREDADO1419.5. Borrado de tipo y compatibilidad con c´di- o go heredadoComo has podido ver, la programaci´n con gen´ricos es muy potente, de hecho, o etodas las clases del Java Collection Framework usan tipos gen´ricos. Sin embar- ego, existe un problema de incompatibilidad con c´digo heredado anterior a la oversi´n 5 de Java en las que no existen tipos gen´ricos. ¿C´mo se resuelve? de o e ouno modo transparente para el programador, el compilador de Java sustituyelos tipos gen´ricos por verdaderos tipos cuando se determinan estos en tiempo ede compilaci´n, es decir, si el m´todo del Listado 9.10 en el c´digo se llama una o e ovez con un ArrayListCirculo y otra vez con un ArrayListRecttangulo,se crean dos versiones de este m´todo con cada uno de estos dos tipos concretos. eA esta t´cnica se la llama Borrado de tipo. eEjercicios. 1. Crea otros tipos de sensores, por ejemplo un sensor de presi´n que mida la o presi´n atmosf´rica, y un sensor de color que mida colores. En este ultimo o e ´ caso, elige el espacio de colores RGB. 2. Crea una clase gen´rica Garage que permita almacenar en ellas cualquier e tipo de veh´ ıculos Coches, Furgonetas y Motos por ejemplo, es una ubica- ci´n especificada por un n´mero real (como la plaza de garage) o el DNI o u del usuario especificado como un String.Lecturas recomendadas. En esta direcci´n http://java.sun.com/docs/books/tutorial/java/ o generics/index.html puedes encontrar la referencia b´sica de Sun so- a bre el uso de gen´ricos en Java. e En esta direcci´n http://java.sun.com/docs/books/tutorial/extra/ o generics/index.html puedes encontrar otra interesante y detallada re- ferencia, de Gilad Bracha, a la programaci´n con gen´ricos en Java. o e
  • 141. 142 CAP´ ´ ´ ITULO 9. PROGRAMACION CON GENERICOS
  • 142. Cap´ ıtulo 10Construcci´n de proyectos ocon AntContenidos 10.1. Qu´ es Ant . . . . . . . . . . . . . . . . . . . . e . . . 144 10.2. Definici´n del proyecto . . . . . . . . . . . . . o . . . 144 10.2.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . 145 10.2.2. Tareas . . . . . . . . . . . . . . . . . . . . . . . . . . 145 10.3. Compilar el c´digo fuente de un proyecto . . o . . . 146 10.4. Propiedades . . . . . . . . . . . . . . . . . . . . . . . 146 10.5. Estructuras path-like . . . . . . . . . . . . . . . . . 147 10.6. Ejecuci´n de las Pruebas Unitarias . . . . . . o . . . 148 10.7. Generaci´n de la documentaci´n . . . . . . . o o . . . 150 10.8. Empaquetado de la aplicaci´n . . . . . . . . . o . . . 151 10.9. Ejecuci´n y limpieza . . . . . . . . . . . . . . . o . . . 151Introducci´n oHasta ahora, hemos utilizado un entorno de desarrollo integrado, como Eclipse,para realizar todas las tareas relativas a nuestro proyecto. Sabemos c´mo compi- olar nuestro c´digo, c´mo generar la documentaci´n con javadoc, c´mo ejecutar o o o onuestro c´digo, c´mo empaquetar nuestro c´digo y c´mo realizar pruebas sobre o o o onuestro c´digo con JUnit. o Todas estas tareas han necesitado nuestra intervenci´n, para ejecutar el c´di- o ogo de nuestra aplicaci´n hemos tenido que pulsar el bot´n correspondiente en o oEclipse, y del mismo modo, hemos tenido que actuar sobre el IDE para ejecutarlas pruebas unitarias. Eclipse ha sido el IDE que hemos elegido, pero como ya hemos comentado,existen otros excelentes entornos de desarrollo, como por ejemplo NetBeans1 .La elecci´n entre unos y otros acaba siendo cuesti´n de gustos y adaptaci´n m´s o o o a 1 Neatbeans se puede descargar para su instalaci´n desde http://netbeans.org/ o 143
  • 143. 144 CAP´ ´ ITULO 10. CONSTRUCCION DE PROYECTOS CON ANTque de la funcionalidad que los entornos proporcionan, ya que todos ellos pro-porcionan una funcionalidad parecida, al menos, suficiente para lo que nosotrosnecesitamos en este momento. En cada uno de estos entorno de desarrollo, el modo de ejecutar una aplica-ci´n, o el modo de lanzar la herramienta de documentaci´n cambia. Incluso el o omodo en que el entorno de desarrollo organiza las carpetas del proyecto puedecambiar, un entorno puede usar el nombre src y otro source. Estos peque˜os ncambios hacen necesaria la intervenci´n del desarrollador para migrar proyectos ode un entorno a otro. Ser´ interesante que, con independencia del entorno de desarrollo, o incluso ıasi no utiliz´semos ning´n entorno de desarrollo, fuese posible realizar las tareas a ucomunes sobre un proyecto Java de modo est´ndar. a La herramienta de construcci´n de proyectos Ant nos proporciona preci- osamente eso, un modo de trabajar con nuestros proyectos con independenciadel entorno de desarrollo elegido, o incluso poder trabajar directamente sobrenuestro proyecto desde consola.10.1. Qu´ es Ant eAnt es una herramienta de construcci´n de proyectos. Pero su utilidad no se odetiene ah´ con Ant podemos hacer mucho m´s que simplemente compilar el ı, ac´digo de nuestro proyecto, podemos realizar, de modo autom´tico, otras mu- o achas tareas como la ejecuci´n de prueba sobre nuestro c´digo, la generaci´n de o o oinformes a partir de los resultados de las pruebas, la generaci´n de la documen- otaci´n de nuestro proyecto y un largo, larg´ o ısimo etc´tera. Ant es extensible, de emodo que incluso podemos definir nuestras propias tareas, ya que Ant est´ es- acrito en Java. Ant es un proyecto de la Fundaci´n Apache que puedes encontrar en esta odirecci´n http://ant.apache.org, donde tambi´n se encuentran los sencillos o edetalles de instalaci´n, basta con descomprimir el fichero que se puede descargar odesde la p´gina web de la Fundaci´n Apache y establecer las correspondientes a ovariables de entorno.10.2. Definici´n del proyecto o La definici´n de un proyecto Ant siempre se hace en un fichero llamado obuild.xml. Como ejemplo de uso de Ant, vamos a utilizar el proyecto de con-versi´n de temperaturas presentado en el Cap´ o ıtulo 5 y las pruebas unitariassobre este proyecto presentadas en el Cap´ ıtulo 6. Dentro del proyecto de conversi´n de temperaturas crea un fichero y ll´ma- o alo build.xml, el fichero de descripci´n del proyecto es un fichero xml. Si este oest´ndar tecnol´gico no te es familiar, interesa que antes de seguir adelante co- a onozca esta tecnolog´ En esta direcci´n http://www.w3.org/standards/xml/ ıa. ocore encontrar´s una introducci´n a esta tecnolog´ proporcionada por el World a o ıaWide Web Consortium, el consorcio que se encarga de los est´ndares Web2 a 2 Este organismo es el encargado del proceso de estandarizaci´n de las tecnolog´ o ıas Web, sup´gina web est´ repleta de informaci´n con respecto a estos est´ndares tecnol´gicos. Al igual a a o a oque la p´gina web de Sun sobre Java es la referencia b´sica en la web sobre el lenguaje de a aprogramaci´n Java, la p´gina web del W3C es la referencia b´sica para los est´ndares web. o a a a
  • 144. ´ 10.2. DEFINICION DEL PROYECTO 145 La etiqueta ra´ bajo la cual se define todo el proyecto es project que tiene ız un atributo obligatorio name con el que especificamos el nombre del proyecto. El Listado 10.1 muestra la definici´n b´sica de un proyecto. o a1 p r o j e c t name= C o n v e r s i o n T e m p e r a t u r a s 2 ...3 / p r o j e c t Listado 10.1: Definici´n de un proyecto Ant. o Si la sintaxis xml no te es familiar, f´ıjate que la etiqueta de apertura project est´ cerrada con la etiqueta /project3 . a Un proyecto Ant est´ formado, por uno o m´s objetivos, y cada uno de estos a a objetivos puede estar formado por una o m´s tareas. Cada tarea se realizar´n en a a el orden en el que fue especificada dentro del objetivo. Veamos c´mo se define o un objetivo en Ant. 10.2.1. Objetivos Para definir un objetivo utilizamos la etiqueta target que de nuevo tiene un atributo obligatorio, el nombre del objetivo. De nuevo, cada objetivo debe ir cerrado con su correspondiente etiqueta de cierre tal y como muestra el Listado 10.2.1 p r o j e c t name= C o n v e r s i o n T e m p e r a t u r a s 2 t a r g e t name= e m p e z a n d o 3 ...4 / t a r g e t5 / p r o j e c t Listado 10.2: Definici´n de un objetivo Ant. o Cada uno de los objetivos contiene una o m´s tareas que se ejecutar´n en a a el orden en que fueron definidas cuando ejecutemos el correspondiente objetivo. Veamos c´mo se especifican las tareas pertenecientes a un objetivo. o 10.2.2. Tareas En Ant existe una enorme cantidad de tareas predefinidas, por ejemplo, existe una tarea para compilar el c´digo fuente de nuestra aplicaci´n, y otra tarea o o para crear un fichero jar a partir del c´digo compilado de nuestra aplicaci´n. A o o lo largo de este cap´ ıtulo iremos describiendo las tareas m´s utilizadas. En este a momento, y como ejemplo, vamos a utilizar una tarea muy sencilla cuya funci´no es simplemente mostrar un mensaje de texto en consola, tal y como muestra el Listado 10.3.1 p r o j e c t name= C o n v e r s i o n T e m p e r a t u r a s 2 t a r g e t name= e m p e z a n d o 3 e c h oEmpezando con Ant ./ e c h o4 / t a r g e t5 / p r o j e c t Listado 10.3: La tarea echo muestra un mensaje de texto por consola. 3 En un fichero xml bien formado toda etiqueta abierta debe estar cerrada con su corres- pondiente etiqueta.
  • 145. 146 CAP´ ´ ITULO 10. CONSTRUCCION DE PROYECTOS CON ANT Ejecutar un objetivo desde l´ınea de instrucciones es muy sencillo, basta si- tuarse en el directorio donde se encuentre el fichero build.xml y teclear ant empezando, lo que invocar´ la ejecuci´n del objetivo empezando definido en el a o fichero build.xml. El resultado de la ejecuci´n ser´ parecido a: o a Rayuela:ant oscar$ ant empezando Buildfile: build.xml empezando: [echo] Empezando con Ant. BUILD SUCCESSFUL Total time: 0 seconds Pasemos a ver c´mo utilizar algunas otras tareas m´s utiles que echo. o a ´ 10.3. Compilar el c´digo fuente de un proyecto o La etiqueta javac nos permite compilar c´digo Java. Para poder usar esta o etiqueta debemos indicar el directorio donde est´ el c´digo fuente mediante el a o atributo srcdir, e indicar donde se escribir´n los ficheros de clases compilados a mediante el atributo destdir. El Listado 10.4 muestra el uso de la etiqueta javac.1 p r o j e c t name= C o n v e r s i o n T e m p e r a t u r a s 2 t a r g e t name= c o m p i l e d e s c r i p t i o n= C o m p i l a e l p r o y e c t o 3 mkdir d i r= . . / e x c e p c i o n e s / b u i l d / c l a s s e s /4 j a v a c s r c d i r= . . / e x c e p c i o n e s 5 d e s t d i r= . . / e x c e p c i o n e s / b u i l d / c l a s s e s /6 / t a r g e t7 / p r o j e c t Listado 10.4: Compilar un proyecto usando Ant. F´ ıjate que previamente a la tarea de compilaci´n de la l´ o ınea 3, hemos uti- lizado la tarea mkdir para crear el directorio de destino de las clases compila- das. Ahora ya puedes compilar el c´digo de tu proyecto invocando al objetivo o compile bien desde consola, bien desde la vista Ant de Eclipse. 10.4. Propiedades El objetivo compile tal y como lo hemos descrito tiene un inconveniente, y es que si decidimos cambiar el fichero de destino de los ficheros compilados por ejemplo desde el original ../excpeciones/build/classes, a otro directorio como por ejemplo ../excepciones/build/project/classes, tendremos que cambiar todas la ocurrencias del destino original. Para solucionar esta situaci´n, o podemos utilizar las propiedades que nos permiten asociar a un nombre un valor, y hacer referencia al valor a trav´s de su nombre en cualquier lugar del fichero e build.xml, tal y como muestra el Listado 10.5.1 p r o j e c t name= C o n v e r s i o n T e m p e r a t u r a s 2 !−− D i r e c t o r i o d e l c o d i g o f u e n t e −− ´
  • 146. 10.5. ESTRUCTURAS PATH-LIKE 147 3 p r o p e r t y name= s r c . d i r l o c a t i o n= . . / e x c e p c i o n e s / 4 !−− D i r e c t o r i o de c l a s e s c o m p i l a d a s −− 5 p r o p e r t y name= b u i l d . d i r l o c a t i o n= b u i l d / 6 !−− S u b d i r e c t o r i o de l a s c l a s e s c o m p i l a d a s d e l p r o y e c t o −− 7 p r o p e r t y name= b u i l d . c l a s s e s . d i r l o c a t i o n= $ { b u i l d . d i r } / c l a s s e s / 8 9 t a r g e t name= c o m p i l e d e s c r i p t i o n= C o m p i l a e l p r o y e c t o 10 mkdir d i r= $ { b u i l d . c l a s s e s . d i r } /11 j a v a c s r c d i r= $ { s r c . d i r } 12 d e s t d i r= $ { b u i l d . c l a s s e s . d i r } /13 / t a r g e t14 / p r o j e c t Listado 10.5: Uso de las propiedades. En este caso, cada una de las propiedades hace referencia a una direcci´n o representada por el atributo location. 10.5. Estructuras path-like Con las propiedades podemos definir valores a los que poder hacer referencia por su nombre. Las estructuras path-like son a´n m´s potentes, y nos permiten u a definir grupos de directorios o ficheros. Por ejemplo, en nuestro proyecto de la aplicaci´n de conversi´n de temperaturas, tenemos programadas una serie de o o clases de pruebas unitarias que necesitamos compilar antes de ejecutar. Para compilar las clases de pruebas unitarias necesitaremos la biblioteca junit.jar, adem´s del directorio donde se encuentran las clases de prueba que queremos a compilar. Para definir grupos de directorios y ficheros Ant nos proporciona la etiqueta path. El Listado 10.6 muestra el uso de esta etiqueta con el objetivo de compilar las clases de pruebas unitarias. 1p r o j e c t name= C o n v e r s i o n T e m p e r a t u r a s 2 !−− D i r e c t o r i o d e l c o d i g o f u e n t e −− ´ 3 p r o p e r t y name= s r c . d i r l o c a t i o n= . . / e x c e p c i o n e s / 4 !−− D i r e c t o r i o de c l a s e s c o m p i l a d a s −− 5 p r o p e r t y name= b u i l d . d i r l o c a t i o n= b u i l d / 6 !−− S u b d i r e c t o r i o de l a s c l a s e s c o m p i l a d a s d e l p r o y e c t o −− 7 p r o p e r t y name= b u i l d . c l a s s e s . d i r l o c a t i o n= $ { b u i l d . d i r } / c l a s s e s / 8 !−− D i r e c t o r i o de l a s c l a s e s de prueba −− 9 p r o p e r t y name= t e s t . d i r l o c a t i o n= . . / t e s t / t e s t /10 !−− S u b d i r e c t o r i o de l a s c l a s e s c o m p i l a d a s de prueba −− 11 p r o p e r t y name= t e s t . c l a s s e s . d i r l o c a t i o n= $ { b u i l d . d i r } / t e s t - c l a s s e s /12 !−− D i r e c t o r i o de b i b l i o t e c a s d e l p r o y e c t o −− 13 p r o p e r t y name= l i b l o c a t i o n= . . / l i b /1415 path i d= t e s t . c o m p i l e . c l a s s p a t h 16 f i l e s e t d i r= $ { l i b } i n c l u d e s= * . j a r /17 p a t h e l e m e n t l o c a t i o n= $ { b u i l d . c l a s s e s . d i r } /18 / path1920 t a r g e t name= c o m p i l e d e s c r i p t i o n= C o m p i l a e l p r o y e c t o 21 mkdir d i r= $ { b u i l d . c l a s s e s . d i r } /22 j a v a c s r c d i r= $ { s r c . d i r } 23 d e s t d i r= $ { b u i l d . c l a s s e s . d i r } /24 / t a r g e t2526 t a r g e t name= c o m p i l e - t e s t s 27 depends= c o m p i l e 28 d e s c r i p t i o n= C o m p i l a l o s t e s t s . 29 mkdir d i r= $ { t e s t . c l a s s e s . d i r } /30 j a v a c s r c d i r= $ { t e s t . d i r } 31 d e s t d i r= $ { t e s t . c l a s s e s . d i r }
  • 147. 148 CAP´ ´ ITULO 10. CONSTRUCCION DE PROYECTOS CON ANT32 c l a s s p a t h r e f i d= t e s t . c o m p i l e . c l a s s p a t h /33 / j a v a c34 / t a r g e t35 / p r o j e c t Listado 10.6: Uso de estructuras path-like para definir la ruta a las bibliotecas del proyecto. En las l´ıneas 9, 11 y 13 del Listado 10.6 estamos definiendo las propiedades que hacen referencia al directorio con el c´digo fuente de las clases de prueba, o al directorio destino de las clases compiladas de prueba y al directorio donde est´n todas las bibliotecas del proyecto respectivamente. a Por su lado, entre las l´ıneas 15-18, mediante una estructura path-like, esta- mos definiendo donde est´n las bibliotecas necesarias para compilar las clases a de prueba (junit.jar y harmcret.jar) y donde est´n las clases compiladas del a proyecto. Finalmente, entre las l´ıneas 26-34 estamos definiendo un objetivo para com- pilar las clases de prueba. F´ıjate en la l´ ınea 27, en esa l´ ınea estamos indicando que el objetivo compile-test depende de la tarea compile. Evidentemente, para poder compi- lar las clases de prueba, las clases a probar deben estar previamente compiladas, mediante el atributo depends de la etiqueta target se fuerza a cubrir los obje- tivos especificados en antes de cubrir el objetivo actual. 10.6. Ejecuci´n de las Pruebas Unitarias o El Listado 10.7 muestra c´mo ejecutar una bater´ de pruebas y grabar el re- o ıa sultado a un fichero como un informe con formato de texto. 1p r o j e c t name= C o n v e r s i o n T e m p e r a t u r a s d e f a u l t= t e s t 2 !−− D i r e c t o r i o d e l c o d i g o f u e n t e −− ´ 3 p r o p e r t y name= s r c . d i r l o c a t i o n= . . / e x c e p c i o n e s / 4 !−− D i r e c t o r i o de c l a s e s c o m p i l a d a s −− 5 p r o p e r t y name= b u i l d . d i r l o c a t i o n= b u i l d / 6 !−− S u b d i r e c t o r i o de l a s c l a s e s c o m p i l a d a s d e l p r o y e c t o −− 7 p r o p e r t y name= b u i l d . c l a s s e s . d i r l o c a t i o n= $ { b u i l d . d i r } / c l a s s e s / 8 !−− D i r e c t o r i o de l a s c l a s e s de prueba −− 9 p r o p e r t y name= t e s t . d i r l o c a t i o n= . . / t e s t / t e s t /10 !−− S u b d i r e c t o r i o de l a s c l a s e s c o m p i l a d a s de prueba −− 11 p r o p e r t y name= t e s t . c l a s s e s . d i r l o c a t i o n= $ { b u i l d . d i r } / t e s t - c l a s s e s /12 !−− D i r e c t o r i o de b i b l i o t e c a s d e l p r o y e c t o −− 13 p r o p e r t y name= l i b l o c a t i o n= . . / l i b /14 !−− D i r e c t o r i o de i n f o r m e s −− 15 p r o p e r t y name= r e p o r t s . d i r l o c a t i o n= r e p o r t s /16 !−− D i r e c t o r i o p a r a l o s i n f o r m e s en f o r m a t o t e x t o −− 17 p r o p e r t y name= r e p o r t s . t x t . d i r l o c a t i o n= $ { r e p o r t s . d i r } / t x t /1819 !−− Path p a r a c o m p i l a r l a s c l a s e s de prueba −− 20 path i d= t e s t . c o m p i l e . c l a s s p a t h 21 f i l e s e t d i r= $ { l i b } i n c l u d e s= * . j a r /22 p a t h e l e m e n t l o c a t i o n= $ { b u i l d . c l a s s e s . d i r } /23 / path2425 !−− Path p a r a e j e c u t a r l a s c l a s e s de prueba −− 26 path i d= t e s t . c l a s s p a t h 27 path r e f i d= t e s t . c o m p i l e . c l a s s p a t h /28 p a t h e l e m e n t path= $ { t e s t . c l a s s e s . d i r } /29 / path3031 t a r g e t name= c o m p i l e d e s c r i p t i o n= C o m p i l a e l p r o y e c t o
  • 148. ´ 10.6. EJECUCION DE LAS PRUEBAS UNITARIAS 14932 mkdir d i r= $ { b u i l d . c l a s s e s . d i r } /33 j a v a c s r c d i r= $ { s r c . d i r } 34 d e s t d i r= $ { b u i l d . c l a s s e s . d i r } /35 / t a r g e t3637 t a r g e t name= c o m p i l e - t e s t s 38 depends= c o m p i l e 39 d e s c r i p t i o n= C o m p i l a l o s t e s t s . 40 mkdir d i r= $ { t e s t . c l a s s e s . d i r } /41 j a v a c s r c d i r= $ { t e s t . d i r } 42 d e s t d i r= $ { t e s t . c l a s s e s . d i r } 43 c l a s s p a t h r e f i d= t e s t . c o m p i l e . c l a s s p a t h /44 / j a v a c45 / t a r g e t4647 t a r g e t name= t e s t 48 depends= c o m p i l e - t e s t s 49 d e s c r i p t i o n= E j e c u t a l o s t e s t s u n i t a r i o s 50 mkdir d i r= $ { r e p o r t s . d i r } /51 mkdir d i r= $ { r e p o r t s . t x t . d i r } /52 j u n i t printsummary= t r u e 53 h a l t o n f a i l u r e= f a l s e 54 f a i l u r e p r o p e r t y= t e s t . f a i l u r e s 55 c l a s s p a t h r e f i d= t e s t . c l a s s p a t h /56 f o r m a t t e r t y p e= p l a i n /57 t e s t name= t e s t . A l l T e s t s 58 t o d i r= $ { r e p o r t s . t x t . d i r } /59 / j u n i t60 / t a r g e t61 / p r o j e c t Listado 10.7: Ejecutar la bater´ de pruebas unitarias. ıa En el Listado 10.7 se ha a˜adido el atributo default=test al proyecto n para indicar que el objetivo test es el objetivo por defecto, si no se selecciona ning´n otro, este es el objetivo que se invoca al ejecutar Ant. Tambi´n se han u e a˜adido las propiedades y estructuras path-like necesarias para la ejecuci´n de n o las pruebas. Si unicamente queremos ejecutar algunas de las pruebas y no toda ´ la suite utilizar´ ıamos la variante mostrada en el Listado 10.8, donde se muestran s´lo las l´ o ıneas a˜adidas al fichero build.xml. n 1 !−− D i r e c t o r i o p a r a l o s i n f o r m e s en f o r m a t o xml −− 2 p r o p e r t y name= r e p o r t s . x m l . d i r l o c a t i o n= $ { r e p o r t s . d i r } / x m l / 3 4 ... 5 6 t a r g e t name= t e s t - x m l 7 depends= c o m p i l e . t e s t s 8 d e s c r i p t i o n= E j e c u t a l o s t e s t s u n i t a r i o s 9 mkdir d i r= $ { r e p o r t s . d i r } /10 mkdir d i r= $ { r e p o r t s . x m l . d i r } /11 j u n i t printsummary= t r u e 12 h a l t o n f a i l u r e= f a l s e 13 f a i l u r e p r o p e r t y= t e s t . f a i l u r e s 14 c l a s s p a t h r e f i d= t e s t . c l a s s p a t h /15 f o r m a t t e r t y p e= x m l /16 b a t c h t e s t t o d i r= $ { r e p o r t s . x m l . d i r } 17 f i l e s e t d i r= $ { t e s t . c l a s s e s . d i r } 18 i n c l u d e name= * * / T e s t * . c l a s s /19 / f i l e s e t20 / b a t c h t e s t21 / j u n i t22 / t a r g e t Listado 10.8: Ejecutar s´lo algunas de las pruebas. o F´ ıjate, que en este ultimo caso, adem´s estamos indicando que el formato ´ a de los informes sea xml, esta posibilidad es interesante ya que a partir de estos
  • 149. 150 CAP´ ´ ITULO 10. CONSTRUCCION DE PROYECTOS CON ANT informes podremos hacer sobre ellos una transformaci´n para generarlos en for- o mato html tal y como muestra el Listado 10.9, donde, de nuevo, s´lo aparecen o las l´ ıneas a˜adidas al fichero build.xml. n 1 !−− D i r e c t o r i o p a r a l o s i n f o r m e s en f o r m a t o html −− 2 p r o p e r t y name= r e p o r t s . h t m l . d i r l o c a t i o n= $ { r e p o r t s . d i r } / h t m l / 3 4 ... 5 6 t a r g e t name= t e s t . r e p o r t s 7 depends= t e s t 8 d e s c r i p t i o n= G e n e r a l o s i n f o r m e s d e l o s t e s t s e n f o r m a t o x m l 9 j u n i t r e p o r t t o d i r= $ { r e p o r t s . x m l . d i r } 10 f i l e s e t d i r= $ { r e p o r t s . x m l . d i r } 11 i n c l u d e name= T E S T - * . x m l /12 / f i l e s e t13 r e p o r t f o r m a t= f r a m e s 14 t o d i r= $ { r e p o r t s . h t m l . d i r } /15 / j u n i t r e p o r t16 f a i l i f = t e s t . f a i l u r e s 17 message= S e h a n p r o d u c i d o e r r o r e s e n l o s t e s t s . /18 / t a r g e t Listado 10.9: Generar informes de las pruebas en formato xml. 10.7. Generaci´n de la documentaci´n o o Otra tarea que se puede automatizar es la generaci´n de la documentaci´n o o de nuestro proyecto tal y como muestra el Listado 10.10. La tarea javadoc tiene algunos atributos interesantes, como por ejemplo access que nos permite indicar los m´todos y atributos de los que se generar´ la documentaci´n seg´n su e a o u modificador de acceso. En nuestro ejemplo, se generar´ documentaci´n de todos a o los m´todos y atributos cuya visibilidad sea private o mayor. Otros atributos e utiles son author, si su valor es true se a˜adir´ informaci´n del autor a la ´ n a o documentaci´n, y version, si su valor es true se a˜adir´ informaci´n de la o n a o versi´n. o 1 !−− D i r e c t o r i o p a r a l a d o c u m e n t a c i ´ n −− o 2 p r o p e r t y name= r e p o r t s . j a v a d o c l o c a t i o n= $ { r e p o r t s . d i r } / j a v a d o c / 3 4 ... 5 6 t a r g e t name= j a v a d o c 7 depends= c o m p i l e 8 d e s c r i p t i o n= G e n e r a l a d o c u m e n t a c i o ? n d e l p r o y e c t o . 9 j a v a d o c s o u r c e p a t h= $ { s r c . d i r } 10 d e s t d i r= $ { r e p o r t s . j a v a d o c } 11 a u t h o r= t r u e version= t r u e 12 u s e= t r u e a c c e s s= p r i v a t e 13 l i n k s o u r c e= t r u e e n c o d i n g= ISO - 8 8 5 9 - 1 14 w i n d o w t i t l e= $ { a n t . p r o j e c t . n a m e } 15 c l a s s p a t h16 p a t h e l e m e n t path= $ { t e s t . c l a s s e s . d i r } /17 p a t h e l e m e n t path= $ { b u i l d . c l a s s e s . d i r } /18 / c l a s s p a t h19 / j a v a d o c20 / t a r g e t Listado 10.10: Generaci´n de la documentaci´n del proyecto. o o
  • 150. ´ 10.8. EMPAQUETADO DE LA APLICACION 151 10.8. Empaquetado de la aplicaci´n o Ant Tambi´n nos proporciona la tarea jar para empaquetar nuestra aplica- e ci´n, tal y como muestra el Listado 10.11. En este caso, hemos empleado la o etiqueta property con el atributo value para definir el nombre del fichero empaquetado. La sintaxis de la tarea es bastante autoexplicativa, hay que indi- car el nombre del fichero empaquetado con el atributo destfile de la etiqueta jar. E indicar los ficheros que se van a incluir dentro del fichero empaquetado mediante una estructura path-like. Podemos tambi´n indicar el contenido del e fichero de manifiesto con la etiqueta manifest. 1 !−− D i r e c t o r i o p a r a e l f i c h e r o empaquetado −− 2 p r o p e r t y name= d i s t . d i r l o c a t i o n= d i s t / 3 !−− Nombre d e l f i c h e r o empaquetado −− 4 p r o p e r t y name= d i s t . n a m e v a l u e= C o n v e r s o r T e m p e r a t u r a s . j a r / 5 6 ... 7 8 t a r g e t name= p a c k a g e 9 depends= c o m p i l e 10 d e s c r i p t i o n= G e n e r a e l f i c h e r o j a r 11 mkdir d i r= $ { d i s t . d i r } /12 j a r d e s t f i l e = $ { d i s t . d i r } / $ { d i s t . n a m e } 13 f i l e s e t d i r= $ { b u i l d . c l a s s e s . d i r } /14 m a n i f e s t15 a t t r i b u t e16 name= M a i n - C l a s s 17 v a l u e= c o n v e r s o r . P r i n c i p a l /18 / m a n i f e s t19 / j a r20 / t a r g e t Listado 10.11: Empaquetado del proyecto. 10.9. Ejecuci´n y limpieza o Tambi´n podemos ejecutar nuestra aplicaci´n, como un fichero empaquetado, e o usando la tarea java de Ant, tal y como se muestra en el Listado 10.12. 1 t a r g e t name= e x e c u t e 2 depends= p a c k a g e 3 d e s c r i p t i o n= E j e c u t a l a a p l i c a c i o ? n . 4 j a v a 5 j a r= $ { d i s t . d i r } / $ { d i s t . n a m e } 6 f o r k= t r u e / 7 / t a r g e t Listado 10.12: Ejecuci´n del proyecto como un fichero empaquetado. o Cabe destacar que para ejecutar la aplicaci´n se debe crear una nueva ins- o tancia de la m´quina virtual, cosa que indicamos con el valor true del atributo a fork de la tarea java. Tambi´n podemos borrar los directorios con las clases compiladas y los infor- e mes por si nos es de utilidad. El Listado 10.13 muestra el objetivo clean formado por un conjunto de tareas delete que borran los directorios deseados. 1 t a r g e t name= c l e a n d e s c r i p t i o n= L i m p i a e l p r o y e c t o 2 d e l e t e d i r= $ { d i s t . d i r } /
  • 151. 152 CAP´ ´ ITULO 10. CONSTRUCCION DE PROYECTOS CON ANT3 d e l e t e d i r= $ { b u i l d . d i r } /4 d e l e t e d i r= $ { r e p o r t s . d i r } /5 / t a r g e t Listado 10.13: Borrado de directorios. En el Ap´ndice A se muestra el listado completo del fichero build.xml. e Lecturas recomendadas. Sin duda la referencia b´sica sobre Ant se encuentra el la propia p´gina a a web del proyecto, esta es la direcci´n directa http://ant.apache.org/ o manual/index.html. El cap´ ıtulo 1 de la referencia [13] presenta la herramienta Ant de modo conciso pero muy informativo. En espa˜ol, es interesante el cap´ n ıtulo 3 de la referencia [5].
  • 152. Cap´ ıtulo 11Interfaces gr´ficas de ausuarioContenidos 11.1. APIs para la programaci´n de interfaces gr´ficos o a de usuario en Java: AWT y Swing . . . . . . . . . 154 11.2. Contenedores y Componentes . . . . . . . . . . . . 155 11.3. Gestores de Aspecto (Layout Managers) . . . . . 155 11.4. Detecci´n de eventos: Escuchadores . . . . . . . . o 157 11.5. Algunos componentes Swing . . . . . . . . . . . . . 162 11.5.1. JLabel, muestra texto o iconos . . . . . . . . . . . . 162 11.5.2. JButton, botones que el usuario puede pulsar . . . . 162 11.5.3. JTextField, campos de introducci´n de texto . . . o . 163 11.5.4. JRadioButton, botones de opciones . . . . . . . . . . 164 11.5.5. JCheckBox, botones de selecci´n m´ltiple . . . . . o u . 166 11.5.6. JList, listas de selecci´n . . . . . . . . . . . . . . . o . 166 11.6. El patr´n de dise˜ o Modelo/Vista/Controlador . o n 168Introducci´n oHasta ahora, la interacci´n con nuestras aplicaciones ha sido a trav´s de consola, o elos datos de entrada los tecleamos en consola, y la respuesta la obten´ ıamostambi´n directamente en consola. e Cada vez es menor el n´mero de aplicaciones con interfaces de usuario basa- udos en consola. Uno de los principales inconvenientes de este tipo de interfacesde usuario es que son poco intuitivos y por lo tanto susceptibles de crear confu-si´n en el usuario y como resultado que los datos de entrada al programa sean oerr´neos. o Por contrapartida, las aplicaciones basadas en interfaces gr´ficos de usuario ason m´s intuitivas y la entrada de datos puede estar acotada, evitando que el ausuario introduzca valores err´neos. o Java proporciona dos grandes APIs para programar interfaces gr´ficas de a 153
  • 153. 154 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIOusuario: Abstract Windows Toolkit (AWT) y Swing. En este cap´ ıtulo veremoscuales son las caracter´ ısticas de ambas y justificaremos por qu´ elegir una sobre eotra. La programaci´n de interfaces gr´ficos de usuario est´ basada en la idea de o a aque los componentes gr´ficos, tales como botones y cajas de edici´n de texto, a oson capaces de lazar eventos cuando el usuario interacciona sobre ellos. Porejemplo, cada vez que el usuario hace click sobre un bot´n, este lanza un evento ocomo respuesta, y ser´ tarea del programador escribir el c´digo necesario para a oescuchar el tipo de eventos que le interese y actuar en consecuencia cuando seproduzca uno. Los interfaces gr´ficos de usuario son anteriores a la aparici´n del lenguaje a ode programaci´n Java, y en su programaci´n se han descubierto interesantes o opatrones de dise˜o, uno de ellos, ampliamente utilizado en otros lenguajes de nprogramaci´n como por ejemplo Smalltalk, es el patr´n de dise˜o Modelo/Vis- o o nta/Controlador. Este patr´n de dise˜o agrupa las clases de una aplicaci´n seg´n o n o usu responsabilidad, de modo que una clase s´lo puede formar parte bien del Mo- odelo, bien de la Vista o bien del Controlador. Veremos con detalle este patr´node dise˜o y alguna t´cnica util para su implementaci´n. n e ´ o11.1. APIs para la programaci´n de interfaces o gr´ficos de usuario en Java: AWT y Swing aEn Java, existen dos APIs para la programaci´n de interfaces gr´ficos de usuario o aAWT (Abstract Window Toolkit) y Swing. AWT fue la primera API disponibleen Java y sus principales caracter´ ısticas son: La creaci´n de componentes gr´ficos se delega al Sistema Operativo. o a El Sistema Operativo se encarga de dibujar los componentes gr´ficos y de a la detecci´n de eventos. o El aspecto de la aplicaci´n es el nativo del Sistema Operativo. o La principal desventaja de AWT es que descansa directamente sobre el Sis-tema Operativo quien interviene tanto en la creaci´n de componentes gr´ficos o acomo en la detecci´n de eventos, de modo que la aplicaci´n se puede ralentizar o osi la interfaz contiene muchos elementos gr´ficos, y por otro lado no se pueden aintroducir cambios en el aspecto de los componentes. El API Swing viene a liberar la creaci´n de interfaces gr´ficos de la carga o aque supone la dependencia con respecto al Sistema Operativo. Las principalescaracter´ısticas de este API son: Swing se encarga de dibujar los componentes y de detectar la interacci´n o sobre ellos. El conjunto de componentes es m´s grande que el que proporciona el a Sistema Operativo. Se tiene control absoluto sobre el aspecto de los componentes. Por todo ellos, Swing ha ido desplazando a AWT en la creaci´n de interfaces ogr´ficos de usuario en Java. a
  • 154. 11.2. CONTENEDORES Y COMPONENTES 155 Figura 11.1: Colocaci´n de Componentes con FlowLayout. o11.2. Contenedores y ComponentesDentro de Swing tenemos dos grandes grupos de elementos: los Contenedoresy los Componentes. La diferencia entre ellos es que los Contenedores puedenalbergar otros Contenedores o Componentes dentro de ellos, y los Componentesson los elementos gr´ficos con los que el usuario puede interaccionar, como por aejemplo botones, listas, etc´tera. e Los tres Contenedores que disponibles en Swing son JFrame que represen-ta una ventana con marco, JWindow que representa una ventana sin marco, yJPanel que no tiene un aspecto visual, su cometido es albergar otros Contene-dores o Componentes dentro de ´l. e La idea b´sica es que vamos a utilizar los JPanel como mu˜ecas rusas, de a nmodo que colocaremos Componentes dentro de JPanel y esos JPanel dentro deotros JPanel con m´s Componentes para ir creando el aspecto deseado para el ainterfaz gr´fico de nuestra aplicaci´n. a o11.3. Gestores de Aspecto (Layout Managers)Cuando se programan interfaces gr´ficos de usuario, un aspecto importante es ala colocaci´n de los Componentes dentro de la ventana de nuestra aplicaci´n. o oJava nos facilita esta tarea mediante el uso de Gestores de Aspecto (LayoutManagers en ingl´s). Estos Gestores de Aspecto son los encargados de colocar elos Componentes que vamos a˜adiendo en los Contenedores. Cada uno de los nGestores de Aspecto sigue una pol´ ıtica de colocaci´n de los componentes, as´ o ı,por ejemplo, BoxLayout coloca cada nuevo componente al la izquierda del ultimo ´componente a˜adido, como en el sentido de la escritura del espa˜ol, de tal modo n nque si no hay espacio suficiente para insertar un nuevo Componente en la l´ ıneaactual, porque se ha llegado al borde de la ventan, el Componente se a˜adir´ al n aprincipio de una nueva l´ınea por debajo de la actual. La Figura 11.1 muestraun ejemplo de este comportamiento. Este Gestor de Aspecto es el que por defecto utiliza JPanel cada
  • 155. 156 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO Figura 11.2: Colocaci´n de Componentes con BorderLayout. o vez que a˜adimos sobre ´l un Componente. JFrame posee otro Gestor de n e Aspecto por defecto BorderLayout. Este Gestor de Aspecto define 5 zo- nas BorderLayout.CENTER, BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.EAST, BorderLayout.WEST, dentro de las cuales s´lo pode- o mos a˜adir un Componente o Contenedor con el m´todo add(Component n e componente, int zona), si no indicamos la zona (add(Component componente), el nuevo componente ser´ a˜adido a la regi´n central. La a n o Figura 11.2 muestra una ventana en la que se observa la disposici´n de las o cinco zonas de BorderLayout. El c´digo fuente de este sencilla ejemplo aparece o en el Listado 11.1.1 package g u i ;23 import j a v a . awt . BorderLayout ;4 import j a v a . awt . C o n t a i n e r ;56 import j a v a x . s w i n g . JButton ;7 import j a v a x . s w i n g . JFrame ;8 import j a v a x . s w i n g . S w i n g U t i l i t i e s ;910 public f i n a l c l a s s EjemploBorderLayout {11 private EjemploBorderLayout ( ) {12 super ( ) ;13 }1415 private void creaGUI ( ) {16 JFrame v e n t a n a = new JFrame ( B o r d e r L a y o u t M a n a g e r ) ;17 Container contenedor = ventana . getContentPane ( ) ;18 c o n t e n e d o r . add (new JButton ( C e n t r o ) ) ;19 c o n t e n e d o r . add (new JButton ( N o r t e ) , BorderLayout .NORTH) ;20 c o n t e n e d o r . add (new JButton ( S u r ) , BorderLayout .SOUTH) ;21 c o n t e n e d o r . add (new JButton ( E s t e ) , BorderLayout . EAST) ;22 c o n t e n e d o r . add (new JButton ( O e s t e ) , BorderLayout .WEST) ;23 ventana . s e t S i z e ( 4 0 0 , 400) ;24 v e n t a n a . s e t V i s i b l e ( true ) ;25 }2627 public s t a t i c void main ( S t r i n g [ ] a r g s ) {28 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {29 @Override30 public void run ( ) {
  • 156. ´ 11.4. DETECCION DE EVENTOS: ESCUCHADORES 15731 new EjemploBorderLayout ( ) . creaGUI ( ) ;32 }33 }) ;34 }35 } Listado 11.1: Ejemplo de uso de BorderLayout El Listado 11.1 contiene algunos detalles importante. En la l´ ınea 16, esta- mos almacenando una referencia al contenedor de la ventana principal, que es donde a˜adimos los botones. Las l´ n ıneas 17-21 a˜aden un bot´n, instancia de n o JButton a cada uno de las cinco regiones que define un BorderLayout. En la l´ ınea 22 estamos indicando el tama˜o de la ventana, y finalmente, en la l´ n ınea 23 hacemos visible la ventana. En las l´ ınea 28-33 estamos utilizando un t´cnica e que quedar´ definitivamente clara cuando veamos c´mo programar hilos en Java a o en el Cap´ıtulo 14, en este momento basta decir que en esas l´ ıneas de c´digo se o est´ creando un hilo para atender al interfaz gr´fico de usuario de modo que no a a interfiere con el hilo de la aplicaci´n principal. o Los Gestores de Aspecto por defecto se pueden cambiar con el m´todo e setLayout(java.awt.LayoutManager) al que se le pasa una instancia del nue- vo Gestor de Aspecto que queremos utilizar. Otros Gestores de Aspecto son GridLayout, que permite definir una rejilla con filas y columnas, y podemos a˜adir un componente dentro de cada una n de las posiciones dentro de la rejilla; y BoxLayout nos permite disponer los componentes verticalmente, uno encima de otro, u horizontalmente, uno a la izquierda de otro. 11.4. Detecci´n de eventos: Escuchadores o Si has probado a ejecutar el c´digo del Listado 11.1 quiz´s te hayas dado cuenta o a del siguiente detalle, al pulsar sobre el bot´n de cerrar la ventana que aparece o en el marco de esta, la ventana se cierra pero la aplicaci´n sigue ejecut´ndose. o a No existe ning´n error en la aplicaci´n, simplemente, al cerrar la ventana, lo que u o en realidad est´ ocurriendo es que se hace invisible, pero con ello no su fuerza a que acabe la ejecuci´n de la aplicaci´n. o o Para entender lo que est´ realmente ocurriendo tenemos que conocer de a qu´ modo act´an los Contenedores y Componentes en Java1 . e u La idea b´sica es que cuando el usuario interacciona con los Contenedores a o los Componentes estos lanzan eventos como respuesta, el programador debe escuchar estos eventos, y cuando los recibe actuar en consecuencia. Dicho de otro modo, el programador escribe c´digo que observa cuando un evento ocurre, o y le indica al componente que quiere ser informado cuando el evento ocurra. Por su lado, el componente informa al observador de que el evento ha ocurrido cada vez que este se produce. De nuevo nos encontramos con un patr´n de dise˜o o n llamado Observador. La secci´n 16.9 presenta este patr´n de dise˜o con detalle. o o n La Figura 11.3 muestra gr´ficamente la din´mica en la detecci´n de eventos. a a o De modo resumido, los pasos que debemos dar para responder cuando un evento ocurra son: 1 Este comportamiento es igualmente seguido en otros muchos lenguajes de programaci´n o
  • 157. 158 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO Figura 11.3: Din´mica de la detecci´n de eventos en Swing. a oFigura 11.4: Din´mica de la detecci´n de eventos para el caso particular de a oWindowEvent. 1. Conocer cual es el tipo de eventos genera el Contenedor o Componente que nos interesa. 2. Conocer cual es el interface capaz de escuchar a ese tipo de eventos y escribir una clase que lo implemente. A esta clase la llamaremos clase escuchadora. 3. A˜adir una instancia de la clase escuchadora a la lista de observadores del n Contenedor o el Componente. Para ver de modo detallado como los pasos anteriores se concretan enc´digo, supongamos que deseamos cerrar la aplicaci´n cuando el usuario ha- o oga click sobre el bot´n de cierre de la ventana. El primer paso es cono- ocer qu´ tipo de eventos lanza JFrame, que es nuestra ventana, cuando el eusuario hace click sobre el bot´n correspondiente; para ello podemos con- osultar la p´gina http://download.oracle.com/docs/cd/E17409_01/javase/ atutorial/uiswing/events/eventsandcomponents.html, donde encontramosque JFrame lanza eventos de tipo WindowEvent. El siguiente paso es conocer cual es el interface capaz de escuchar este tipode eventos. En la anterior direcci´n web encontramos que es WindowListener2 . oEste interface declara m´todos cuyo significado aparece en la Tabla 11.1. eLa Figura 11.4 muestra la din´mica en la detecci´n de eventos generados por a oJFrame. La clase que implemente este interface debe definir cada uno de los m´to- edos de la Tabla 11.1. Si lo unico que queremos hacer es cerrar la aplicaci´n ´ ocuando el usuario cierra la ventana, el unico m´todo al que a˜adiremos c´digo ´ e n oes public void windowClosing(WindowEvent e). Finalmente a˜adiremos una instancia de la clase que implementa el ninterface WindowListener como escuchador a la ventana, con el m´todo eaddWindowListener3 . 2 Observa la nomenclatura usada en el nombrado de eventos y sus correspondientes escu-chadores: si encontramos un evento de tipo xxxEvent, el interface capaz de escucharlo sellamar´ xxxListener a 3 F´ ıjate de nueva en la nomenclatura utilizada para nombrar el m´todo que a˜ ade el escu- e nchador al Contenedor, si el escuchador es xxxListener, el m´todo que lo a˜ade al Contenedor e no Componente es addxxxListener(interface xxxListener)
  • 158. ´ 11.4. DETECCION DE EVENTOS: ESCUCHADORES 159 windowOpened(WindowEvent e) Se invoca cuando la ventana se abre. windowClosing(WindowEvent e) Se invoca cuando se intenta ce- rrar la ventana. windowClosed(WindowEvent e) Se invoca cuando la ventana se ha cerrado definitivamente. windowIconified(WindowEvent e) Se invoca cuando la ventana se minimiza. windowDeiconified(WindowEvent e) Se invoca cuando la ventana pasa de estar minimizada a tener su estado normal. windowActivated(WindowEvent e) Se invoca cuando la ventana pasa a ser la ventana activa. windowDeactivated(WindowEvent e) Se invoca cuando la ventana deja de ser la ventana activa. Tabla 11.1: M´todos declarados en la interface WindowListener todos ellos e son public void. El Listado 11.2 muestra un ejemplo completo de un escuchador para los eventos de la ventana que cierra la aplicaci´n cuando el usuario pulsa el bot´n o o de cerrar ventana.1 package g u i ;23 import j a v a . awt . e v e n t . WindowEvent ;4 import j a v a . awt . e v e n t . WindowListener ;56 import j a v a x . s w i n g . JFrame ;7 import j a v a x . s w i n g . S w i n g U t i l i t i e s ;8 // Esta c l a s e implementa WindowListener l u e g o e s e s c u c h a d o r de WindowEvent9 public c l a s s EjemploWindowListener implements WindowListener {10 private EjemploWindowListener ( ) {11 super ( ) ;12 }1314 private void creaGUI ( ) {15 // Creamos l a v e n t a n a16 JFrame v e n t a n a = new JFrame ( A p l i c a c i ´ n q u e s e c i e r r a c o n l a v e n t a n . ) o ;17 // A˜ adimos como e s c u c h a d o r l a i n s t a n c i a de e s t a c l a s e n18 v e n t a n a . addWindowListener ( t h i s ) ;19 ventana . s e t S i z e ( 4 0 0 , 400) ;20 v e n t a n a . s e t V i s i b l e ( true ) ;21 }2223 public s t a t i c void main ( S t r i n g [ ] a r g s ) {24 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {25 @Override26 public void run ( ) {27 new EjemploWindowListener ( ) . creaGUI ( ) ;28 }29 }) ;30 }3132 // Los m´ todos que s i g u e n e s t a n d e c l a r a d o s en l a e ´ interface WindowListener33 @Override34 public void windowOpened ( WindowEvent e ) {35 }36
  • 159. 160 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO37 // E s t e e s e l u n i c o m´ todo con c o d i g o ´ e ´38 @Override39 public void w i n d o w C l o s i n g ( WindowEvent e ) {40 System . o u t . p r i n t l n ( C e r r a n d o l a v e n t a n a . . . ) ;41 System . e x i t ( 0 ) ;42 }4344 @Override45 public void windowClosed ( WindowEvent e ) {46 }4748 @Override49 public void w i n d o w I c o n i f i e d ( WindowEvent e ) {50 }5152 @Override53 public void w i n d o w D e i c o n i f i e d ( WindowEvent e ) {54 }5556 @Override57 public void wi ndowA cti vate d ( WindowEvent e ) {58 }5960 @Override61 public void w i n d o w D e a c t i v a t e d ( WindowEvent e ) {62 }63 } Listado 11.2: Aplicaci´n que finaliza cuando se cierra la ventana. La clase o principal implementa el interface WindowListener, por lo que se puede a˜adir n como escuchador de eventos WindowEvent. Si el Listado 11.2 te ha parecido tedioso, ya que hemos teni- do que definir todos los m´todos dej´ndolos vac´ e a ıos excepto el m´todo e windowClosing(WindowEvent e), tu sensaci´n es acertada. En los casos en los o que una interface tiene declarados muchos m´todos, de los que usualmente e s´lo se escribe c´digo para algunos de ellos, Java nos proporciona un clase de o o conveniencia, llamada adaptadora, que implementa la interface definiendo to- dos los m´todos vac´ ¿Cual es la ventaja de utilizar estas clases adaptadoras?, e ıos. pues que nuestros escuchadores en vez de implementar el interface extienden la clase adaptadora, y s´lo sobrescriben los m´todos necesarios, la implementa- o e ci´n del resto de m´todos ser´ la que nos proporcione la clase adaptadora, es o e a decir, ser´n todos vac´ El Listado 11.3 muestra un ejemplo cuyo comporta- a ıos. miento es el mismo que el ejemplo del Listado 11.2, pero utilizando una clase interna an´nima que extiende la clase adaptadora WindowAdapter. o1 package g u i ;23 import j a v a . awt . e v e n t . WindowAdapter ;4 import j a v a . awt . e v e n t . WindowEvent ;56 import j a v a x . s w i n g . JFrame ;7 import j a v a x . s w i n g . S w i n g U t i l i t i e s ;89 public f i n a l c l a s s E j e m p l o C l a s e A d a p t a d o r a {10 private E j e m p l o C l a s e A d a p t a d o r a ( ) {11 super ( ) ;12 }1314 private void creaGUI ( ) {15 // Creamos l a v e n t a n a16 JFrame v e n t a n a = new JFrame ( E s c u c h a d o r c o n c l a s e a d a p t a d o r a . ) ;17 // A˜ adimos como e s c u c h a d o r una i n s t a n c i a de una c l a s e i n t e r n a an´ nima n o18 // que e x t i e n d e a WindowAdapter y s ´ l o s o b r e s c r i b e e l m´ todo o e windowClosing .
  • 160. ´ 11.4. DETECCION DE EVENTOS: ESCUCHADORES 16119 v e n t a n a . addWindowListener (new WindowAdapter ( ) {20 @Override21 public void w i n d o w C l o s i n g ( WindowEvent e ) {22 System . e x i t ( 0 ) ;23 }24 }) ;25 ventana . s e t S i z e ( 4 0 0 , 400) ;26 v e n t a n a . s e t V i s i b l e ( true ) ;27 }2829 public s t a t i c void main ( S t r i n g [ ] a r g s ) {30 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {31 @Override32 public void run ( ) {33 new E j e m p l o C l a s e A d a p t a d o r a ( ) . creaGUI ( ) ;34 }35 }) ;36 }37 } Listado 11.3: Uso de la clase adaptadora WindowAdapter para cerrar la aplicaci´n al cerrar la ventana. o Por otro lado, la propia clases JFrame nos ofrece un m´todo para definir el e comportamiento de la aplicaci´n cuando se cierra la ventana, este m´todo es o e setDefaultCloseOperation(int modo). El Listado 11.4 muestra un ejemplo de uso de este m´todo. e1 package g u i ;23 import j a v a x . s w i n g . JFrame ;4 import j a v a x . s w i n g . S w i n g U t i l i t i e s ;56 public c l a s s E j e m p l o S e t D e f a u l t C l o s e O p e r a t i o n {7 private E j e m p l o S e t D e f a u l t C l o s e O p e r a t i o n ( ) {8 super ( ) ;9 }1011 private void creaGUI ( ) {12 // Creamos l a v e n t a n a13 JFrame v e n t a n a = new JFrame ( E s c u c h a d o r c o n c l a s e a d a p t a d o r a . ) ;14 // Usamos e l m´ todo de c o n v e n i e n c i a s e t D e f a u l t C l o s e O p e r a t i o n e15 v e n t a n a . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;16 ventana . s e t S i z e ( 4 0 0 , 400) ;17 v e n t a n a . s e t V i s i b l e ( true ) ;18 }1920 public s t a t i c void main ( S t r i n g [ ] a r g s ) {21 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {22 @Override23 public void run ( ) {24 new E j e m p l o S e t D e f a u l t C l o s e O p e r a t i o n ( ) . creaGUI ( ) ;25 }26 }) ;27 }28 } Listado 11.4: Uso del m´todo setDefaultCloseOperation(int) para acabar la e aplicaci´n cuando se cierra la ventana. o Esta t´cnica de escucha de eventos es transversal a todos los Contenedores e y Componentes que forman parte de Swing. Los Contenedores y Componentes lanzan eventos que seremos capaces de escuchar implementando el interface adecuado en una clase, y registrando esa clase como escuchador del Contenedor o Componente. En la secci´n siguiente vamos a ver algunos de los Componentes Swing m´s o a
  • 161. 162 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO comunes y se mostrar´n ejemplos de c´mo escuchar los eventos que se producen a o cuando el usuario interacciona sobre ellos. 11.5. Algunos componentes Swing Esta secci´n no pretende ser una presentaci´n exhaustiva de Componentes Swing, o o si no una muestra de c´mo utilizar la t´cnica que se ha mostrado en la secci´n o e o anterior para escuchar los eventos que producen. 11.5.1. JLabel, muestra texto o iconos El primer y m´s sencillo Componente Swing es JLabel que se muestra como a una cadena de texto o un icono que el usuario no puede modificar, pero s´ elı programador. Este componente no lanza ning´n tipo de evento, ya que el usua- u rio, como hemos dicho, no puede interaccionar sobre ´l, nos sirve para mostrar e texto o iconos. 11.5.2. JButton, botones que el usuario puede pulsar El siguiente componente en la lista es JButton con el que podemos crear botones que el usuario puede pulsar. Cada vez que el usuario pulsa un JButton, este lanza un evento de tipo ActionEvent, si queremos escuchar este tipo de evento, necesi- tamos implementar la interface ActionListener que unicamente declara un ´ m´todo public void actionPerformed(ActionEvent e) que ser´ invocado e a cada vez que el usuario pulse el bot´n. Finalmente, registraremos el escuchador o al bot´n con el m´todo addActionListener(ActionListener escuchador) de o e la clase JButton. El Listado 11.5 muestra un ejemplo de detecci´n de los eventos ActionEvent, o donde tambi´n se ha incluido un JLabel. En la l´ e ınea 21 creamos el bot´n como o una instancia de JButton, en las l´ ıneas 22-27 a˜adimos al bot´n un escuchador n o como una clase interna an´nima que implementa el interface ActionListener o y definimos el m´todo que declara esta interfaz public void actionPerformed e (ActionEvent e). Otro detalle interesante es que hemos utilizado un JPanel para a˜adir sobre ´l los componentes JLabel y JButton y aprovechar que el n e Gestor de Aspecto de un JPanel es FlowLayout y coloca los Componentes en el sentido de la escritura. Otro detalle nuevo es el uso del m´todo public void e pack(); este m´todo calcula el tama˜o ´ptimo de la ventana para contener e n o todos los componentes que se le han a˜adido, de este modo estamos delegando n en Swing el c´lculo del tama˜o de la ventana. a n1 package g u i ;23 import j a v a . awt . e v e n t . A c t i o n E v e n t ;4 import j a v a . awt . e v e n t . A c t i o n L i s t e n e r ;56 import javax . s w i n g . JButton ;7 import javax . s w i n g . JFrame ;8 import javax . swing . JLabel ;9 import javax . swing . JPanel ;10 import javax . swing . S w i n g U t i l i t i e s ;1112 public c l a s s EjemploJButton {13 private EjemploJButton ( ) {14 super ( ) ;
  • 162. 11.5. ALGUNOS COMPONENTES SWING 16315 }1617 private void creaGUI ( ) {18 JFrame v e n t a n a = new JFrame ( U n J L a b e l y u n J B u t t o n ) ;19 J P a n e l c o n t e n e d o r = new J P a n e l ( ) ;20 c o n t e n e d o r . add (new J L a b e l ( P u l s a e l b o t ´ n : ) ) ; o21 JButton jbBoton = new JButton ( P ´ l s a m e ) ; u22 jbBoton . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) {23 @Override24 public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t e ) {25 System . o u t . p r i n t l n ( B o t o n p u l s a d o ) ; ´26 }27 }) ;28 c o n t e n e d o r . add ( jbBoton ) ;29 v e n t a n a . g e t C o n t e n t P a n e ( ) . add ( c o n t e n e d o r ) ;30 v e n t a n a . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;31 v e n t a n a . pack ( ) ;32 v e n t a n a . s e t V i s i b l e ( true ) ;33 }3435 public s t a t i c void main ( S t r i n g [ ] a r g s ) {36 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {37 @Override38 public void run ( ) {39 new EjemploJButton ( ) . creaGUI ( ) ;40 }41 }) ;42 }4344 } Listado 11.5: Ejemplo de uso de JButton 11.5.3. JTextField, campos de introducci´n de texto o La clase JTextField dibuja una caja de edici´n de texto de una unica l´ o ´ ınea donde el usuario puede introducir texto. En el momento de la instanciaci´n de o JTextField podemos indicar el n´mero de columnas. u Un JTextField lanza eventos de tipo ActionEvent cada vez que el usuario pulsa el bot´n Enter y este componente tiene el foco. Este es el mismo evento o que lanza un bot´n cuando el usuario lo pulsa, luego el procedimiento para o escuchar los eventos es el mismo que en el caso de JButton. El Listado 11.6 muestra un ejemplo de uso de JTextField. F´ ıjate que esta vez en la l´ ınea 22 hemos optado por cambiar el Gestor de Aspecto del JFrame es vez de utilizar un JPanel intermedio. En la l´ ınea 29 hemos utilizado el m´todo String getText() e para obtener el texto introducido por el usuario en el JTextField.1 package g u i ;23 import java . awt . C o n t a i n e r ;4 import java . awt . FlowLayout ;5 import java . awt . e v e n t . A c t i o n E v e n t ;6 import java . awt . e v e n t . A c t i o n L i s t e n e r ;78 import javax . s w i n g . JFrame ;9 import javax . swing . JLabel ;10 import javax . swing . JTextField ;11 import javax . swing . S w i n g U t i l i t i e s ;1213 public c l a s s E j e m p l o J T e x t F i e l d {14 private J T e x t F i e l d j t f T e x t o ;1516 private E j e m p l o J T e x t F i e l d ( ) {17 super ( ) ;18 }
  • 163. 164 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO1920 public void creaGUI ( ) {21 JFrame v e n t a n a = new JFrame ( ) ;22 v e n t a n a . s e t L a y o u t (new FlowLayout ( ) ) ;23 Container contenedor = ventana . getContentPane ( ) ;24 c o n t e n e d o r . add (new J L a b e l ( I n t r o d u c e u n t e x t o : ) ) ;25 j t f T e x t o = new J T e x t F i e l d ( 5 0 ) ;26 j t f T e x t o . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) {27 @Override28 public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t e ) {29 System . o u t . p r i n t l n ( E l t e x t o e s c r i t o e s : + j t f T e x t o . g e t T e x t ( ) ) ;30 }31 }) ;32 v e n t a n a . add ( j t f T e x t o ) ;33 v e n t a n a . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;34 v e n t a n a . pack ( ) ;35 v e n t a n a . s e t V i s i b l e ( true ) ;36 }3738 public s t a t i c void main ( S t r i n g [ ] a r g s ) {39 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {40 @Override41 public void run ( ) {42 new E j e m p l o J T e x t F i e l d ( ) . creaGUI ( ) ;43 }44 }) ;45 }46 } Listado 11.6: Ejemplo de uso de JTextField 11.5.4. JRadioButton, botones de opciones El siguiente Componente en complejidad es el bot´n de radio JRadioButton que o dibuja un bot´n asociado a una opci´n, como por ejemplo para elegir la forma o o de pago Tarjeta/Transferencia/Cheque. Muchas veces las opciones presentadas al usuario son excluyentes, como en el caso anterior, de modo que seleccionar una de ellas implica que se de-selecciona la anterior si hubiese alguna. JRadioButton puede lanzar dos tipos de eventos interesante, nuestro co- nocido ActionEvent y el nuevo ItemEvent. El evento ItemEvent nos da m´s a informaci´n sobre lo que ha ocurrido que ActionEvent, ya que nos dice si lo o que ha ocurrido es una selecci´n o una des-selecci´n del bot´n. o o o Ya sabemos que los eventos de tipo ActionEvent los podemos es- cuchar con una clase que implemente el interface ActionListener a˜adida al Componente que queremos escuchar con el m´todo n e addActionListener(ActionListener escuchador), y que este interface s´lo declara un m´todo public void actionPerformed(ActionEvent e) que o e se invoca cada vez que el bot´n se pulsa. o En el caso de un evento de tipo ItemEvent lo podemos escu- char con una clase que implemente el interface ItemListener siempre que a˜adamos la instancia al Componente que queremos escuchar con n addItemListener(ItemListener escuchador). Este interface s´lo decla- o ra un m´todo public void itemStateChanged(ItemEvent e) que se invo- e ca cada vez que el usuario selecciona o des-selecciona un JRadioButton. Pa- ra conocer si lo que ha ocurrido es una selecci´n o una de-selecci´n, po- o o demos utilizar el m´todo getStateChange() de la clase ItemEvent que nos e devolver´ ItemEvent.SELECTED si lo que ha ocurrido es una selecci´n, o a o ItemEvent.DESELECTED si lo que ha ocurrido es una de-selecci´n. o
  • 164. 11.5. ALGUNOS COMPONENTES SWING 1651 package g u i ;23 import java . awt . BorderLayout ;4 import java . awt . e v e n t . A c t i o n E v e n t ;5 import java . awt . e v e n t . A c t i o n L i s t e n e r ;6 import java . awt . e v e n t . ItemEvent ;7 import java . awt . e v e n t . I t e m L i s t e n e r ;89 import javax . s w i n g . BoxLayout ;10 import javax . s w i n g . ButtonGroup ;11 import javax . s w i n g . JFrame ;12 import javax . swing . JPanel ;13 import javax . s w i n g . JRadioButton ;14 import javax . swing . S w i n g U t i l i t i e s ;1516 public f i n a l c l a s s EjemploJRadioButton {17 private EjemploJRadioButton ( ) {18 super ( ) ;19 }2021 private J P a n e l c r e a C o n t e n e d o r ( S t r i n g p o s i c i o n ) {22 J P a n e l c o n t e n e d o r = new J P a n e l ( ) ;23 c o n t e n e d o r . s e t L a y o u t (new BoxLayout ( c o n t e n e d o r , BoxLayout . Y AXIS ) ) ;24 E s c u c h a d o r e s c u c h a d o r = new E s c u c h a d o r ( ) ;25 JRadioButton jrbMucho = new JRadioButton ( M u c h o ) ;26 jrbMucho . a d d A c t i o n L i s t e n e r ( e s c u c h a d o r ) ;27 jrbMucho . a d d I t e m L i s t e n e r ( e s c u c h a d o r ) ;28 c o n t e n e d o r . add ( jrbMucho ) ;29 JRadioButton j r b N o r m a l = new JRadioButton ( N o r m a l ) ;30 jrbNormal . a d d A c t i o n L i s t e n e r ( escuchador ) ;31 jrbNormal . addItemListener ( escuchador ) ;32 c o n t e n e d o r . add ( j r b N o r m a l ) ;33 JRadioButton j r b P o c o = new JRadioButton ( P o c o ) ;34 jrbPoco . addActionListener ( escuchador ) ;35 jrbPoco . addItemListener ( escuchador ) ;36 c o n t e n e d o r . add ( j r b P o c o ) ;37 i f ( p o s i c i o n == BorderLayout . EAST) {38 ButtonGroup grupo = new ButtonGroup ( ) ;39 grupo . add ( jrbMucho ) ;40 grupo . add ( j r b N o r m a l ) ;41 grupo . add ( j r b P o c o ) ;42 }43 return c o n t e n e d o r ;44 }4546 private void creaGUI ( ) {47 JFrame v e n t a n a = new JFrame ( E j e m p l o J R a d i o B u t t o n ) ;48 v e n t a n a . add ( c r e a C o n t e n e d o r ( BorderLayout .WEST) , BorderLayout .WEST) ;49 v e n t a n a . add ( c r e a C o n t e n e d o r ( BorderLayout . EAST) , BorderLayout . EAST) ;50 v e n t a n a . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;51 v e n t a n a . pack ( ) ;52 v e n t a n a . s e t V i s i b l e ( true ) ;53 }5455 private c l a s s E s c u c h a d o r implements A c t i o n L i s t e n e r , ItemListener {56 public E s c u c h a d o r ( ) {57 super ( ) ;58 }5960 @Override61 public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t e ) {62 System . o u t . p r i n t l n ( B o t ´ n p u l s a d o ) ; o63 }6465 @Override66 public void i t e m S t a t e C h a n g e d ( ItemEvent e ) {67 S t r i n g t e x t o = ( ( JRadioButton ) e . g e t S o u r c e ( ) ) . g e t T e x t ( ) ;68 i f ( e . g e t S t a t e C h a n g e ( ) == ItemEvent .DESELECTED)69 System . o u t . f o r m a t ( B o t ´ n %s d e s e l e c c i o n a d o . n , t e x t o ) ; o70 e l s e i f ( e . g e t S t a t e C h a n g e ( ) == ItemEvent . SELECTED)71 System . o u t . f o r m a t ( B o t ´ n %s s e l e c c i o n a d o . n , t e x t o ) ; o72 }73 }
  • 165. 166 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO7475 public s t a t i c void main ( S t r i n g [ ] a r g s ) {76 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {77 @Override78 public void run ( ) {79 new EjemploJRadioButton ( ) . creaGUI ( ) ;80 }81 }) ;82 }8384 } Listado 11.7: Ejemplo de uso de JRadioButton El Listado 11.7 muestra un ejemplo con este nuevo Componente. Adem´s a de lo ya comentado, f´ıjate que en las l´ ıneas 38-41 estamos utilizando una nueva clase ButtonGroup, esta clase agrupa los botones de manera excluyente, de modo que cuando alguno de los botones se selecciona, si hay alg´n otro bot´n u o previamente seleccionado este ultimo se des-seleccionar´. La clase ButtonGroup ´ a crea un grupo l´gico, y no tiene ninguna representaci´n gr´fica. En la l´ o o a ınea 67 puedes ver como hemos recuperado el texto escrito a la derecha de cada uno de los JRadioButton. Y finalmente, en la l´ ınea 23 hemos utilizado un nuevo Gestor de Aspecto, BoxLayout que nos permite disponer los Componentes verticalmente dentro de la ventana. 11.5.5. JCheckBox, botones de selecci´n m´ ltiple o u Usualmente los botones de tipo JRadioButton se utilizan cuando las opciones presentadas al usuario son mutuamente excluyentes entre s´ y se han a˜adido a ı, n un ButtonGroup para comportarse de este modo. Si lo que queremos presentar al usuario son opciones no excluyentes solemos utilizar botones de tipo JCheckBox. Estos botones se dibujan como una peque˜a caja que al seleccionarlo aparece n marcada con un tick. Los JCheckBox lanzan los mismos tipos de eventos que los JRadioButton, es decir eventos ActionEvent y eventos ItemEvent para indicar, estos ultimos, ´ si lo que ha ocurrido es una selecci´n o una de-selecci´n. Por lo tanto todo o o lo comentado en la secci´n 11.5.4 sobre los JRadioButton es v´lido para los o a JCheckBox. 11.5.6. JList, listas de selecci´n o La clase JList presentan al usuario varias opciones en forma de lista. El usuario puede seleccionar una o m´s opciones dependiendo del modo de selecci´n de la a o lista. Los eventos que un JList puede lazar cada vez que el usuario selec- ciona una opci´n de la lista son nuestro conocido ActionEvent y el nuevo o ListSelectionEvent. Este evento nos indica si la selecci´n se est´ efectuan- o a do (por ejemplo, el usuario pulsa sobre un elemento de la lista y, sin soltar el bot´n del rat´n, se desplaza sobre los elementos de la lista), o es la acci´n final, o o o cuando el usuario suelta el bot´n del rat´n. o o Para escuchar los eventos de tipo ItemSelectionEvent debemos imple- mentar la interface ItemSelectionListener que declara un unico m´to- ´ e do public void valueChanged(ListSelectionEvent e). Para consultar si
  • 166. 11.5. ALGUNOS COMPONENTES SWING 167 la selecci´n est´ en marcha o es definitiva podemos usar el m´todo o a e getValueIsAdjusting() de la clase ItemSelectionEvent. El Listado 11.8 muestra un ejemplo de uso de este componente. Otros detalles interesantes de este ejemplo son el uso del m´todo setVisibleRowCount(int) e de la l´ ınea 25 para indicar cuantos elementos son visibles en la lis- ta. En la l´ ınea 23 activamos el modo de selecci´n de los elementos de o la lista a ListSelectionModel.SINGLE SELECTION, de modo que s´lo se o podr´ seleccionar un elemento unico de la lista (otros modos posible son a ´ SINGLE INTERVAL SELECTION y MULTIPLE INTERVAL SELECTION).1 package g u i ;23 import j a v a . awt . C o n t a i n e r ;45 import javax . s w i n g . JFrame ;6 import javax . swing . J L i s t ;7 import javax . swing . J S c r o l l P a n e ;8 import javax . swing . L i s t S e l e c t i o n M o d e l ;9 import javax . swing . S w i n g U t i l i t i e s ;10 import javax . swing . event . L i s t S e l e c t i o n E v e n t ;11 import javax . swing . event . L i s t S e l e c t i o n L i s t e n e r ;1213 public f i n a l c l a s s E j e m p l o J L i s t {14 private E j e m p l o J L i s t ( ) {15 super ( ) ;16 }1718 private void creaGUI ( ) {19 JFrame v e n t a n a = new JFrame ( E j e m p l o J L i s t ) ;20 Container contenedor = ventana . getContentPane ( ) ;21 J L i s t o p c i o n e s = new J L i s t (new S t r i n g [ ] { L u n e s , M a r t e s , M i e r c o l e s ´ , Jueves , Viernes , Sabado , Domingo }) ; ´22 opciones . setVisibleRowCount (5) ;23 o p c i o n e s . s e t S e l e c t i o n M o d e ( L i s t S e l e c t i o n M o d e l . SINGLE SELECTION) ;24 o p c i o n e s . a d d L i s t S e l e c t i o n L i s t e n e r (new E s c u c h a d o r ( ) ) ;25 J S c r o l l P a n e s c r o l l = new J S c r o l l P a n e ( o p c i o n e s ) ;26 c o n t e n e d o r . add ( s c r o l l ) ;27 v e n t a n a . pack ( ) ;28 v e n t a n a . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;29 v e n t a n a . s e t V i s i b l e ( true ) ;30 }3132 private c l a s s E s c u c h a d o r implements L i s t S e l e c t i o n L i s t e n e r {33 @Override34 public void valueChanged ( L i s t S e l e c t i o n E v e n t e ) {35 i f ( e . g e t V a l u e I s A d j u s t i n g ( ) == true )36 System . o u t . p r i n t l n ( I t e m e n c u r s o : + ( ( J L i s t ) e . g e t S o u r c e ( ) ) . getSelectedValue () ) ;37 e l s e i f ( e . g e t V a l u e I s A d j u s t i n g ( ) == f a l s e )38 System . o u t . p r i n t l n ( I t e m d e f i n i t i v o : + ( ( J L i s t ) e . g e t S o u r c e ( ) ) . getSelectedValue () ) ;39 }40 }4142 public s t a t i c void main ( S t r i n g [ ] a r g s ) {43 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {44 @Override45 public void run ( ) {46 new E j e m p l o J L i s t ( ) . creaGUI ( ) ;47 }48 }) ;49 }5051 } Listado 11.8: Ejemplo de uso de JList
  • 167. 168 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO Por defecto, el Componente JList no tiene una barra de desplazamientopara poder visualizar los elementos de la lista, si queremos que la lista poseauno, lo tenemos que a˜adir tal y como se muestra en las l´ n ıneas 25 y 26. F´ ıjateque indicamos el Componente al que asociaremos la barra de desplazamiento enel momento de crear esta. Finalmente a˜adimos la barra de desplazamiento a nla ventana y no la lista original. Otros m´todos interesantes del Componente JList son Object egetSelectedValue() el elementos actualmente seleccionado, si la lista esde selecci´n unica; y Object [] getSelectedValues() si se lista es de o ´selecci´n m´ltiple. o u Con el componente JList acabamos la breve muestra de las posibilidadesde creaci´n de interfaces gr´ficos de usuario Swing. Esta secci´n no pretende ser o a oun exposici´n exhaustiva de todas las posibilidades que nos proporciona Swing, olo que pretende mostrar es la t´cnica de c´mo programar interfaces gr´ficos de e o ausuario con el patr´n de dise˜o Observable. o n11.6. El patr´n de dise˜ o Modelo/Vista/Con- o n troladorQuiz´s el patr´n de dise˜o Modelo/Vista/Controlador sea uno de los m´s utili- a o n azados en el desarrollo de proyectos inform´ticos, tanto es as´ que incluso existe a ıuna adaptaci´n al mundo de aplicaciones web de este patr´n de dise˜o. o o n Este patr´n de dise˜o define tres actores con las siguientes responsabilidades: o nModelo es el responsable de mantener y gestionar los datos de la aplicaci´n. oVista es la responsable del interfaz gr´fico de usuario y la detecci´n de eventos a o sobre los componentes.Controlador es quien hace corresponder la interacci´n del usuario con posible o cambios en el Modelo. Veamos , con un ejemplo, el papel de cada uno de estos actores. En la Figura11.5 se muestra el interfaz gr´fico de una aplicaci´n que calcula la cuota mensual a ode una hipoteca. El usuario puede introducir los tres datos que se necesita parael c´lculo en tres cajas de edici´n de texto, y cuando pulsa el bot´n Calcula a o oaparece la nueva cuota en la parte inferior de la ventana. En este caso, el Modelo contiene los datos de la hipoteca: cantidad hipoteca-da, duraci´n de la hipoteca, inter´s del pr´stamo y la cuota mensual. La Vista o e ees la encargada de crear el interfaz gr´fico y la detecci´n de los eventos sobre a oel interfaz. El Controlador sabe que cuando el usuario pulsa el bot´n Calcula, odebe leer los datos de la hipoteca y envi´rselos al Modelo para que este haga el ac´lculo. a En la Figura 11.6 se muestra la din´mica de este patr´n de dise˜o, que se a o ndetalla en los siguiente pasos: 1. El usuario interacciona sobre la Vista. 2. La Vista informa al Controlador de lo ocurrido.
  • 168. ´ ˜11.6. EL PATRON DE DISENO MODELO/VISTA/CONTROLADOR 169Figura 11.5: Un interfaz gr´fico para el c´lculo de la cuota mensual de una a ahipoteca. Figura 11.6: Din´mica del modelo MVC. a 3. El Controlador decide que datos necesita de la Vista para llevar a cabo la tarea como respuesta a la interacci´n del usuario. o 4. El Controlador actualiza el Modelo. 5. El Modelo informa a la Vista de que se ha actualizado. 6. La Vista pide los datos de su inter´s para visualizarlos. e En el ejemplo del c´lculo de la cuota mensual de una hipoteca esta din´mica a ase concretar´ del siguiente modo: ıa 1. El usuario introduce la cantidad, el tiempo y el inter´s de la hipoteca y e pulsa el bot´n Calcula. o 2. La Vista informa al Controlador de que el usuario ha pulsado el bot´n o Calcula. 3. La l´gica del negocio programada en el Controlador indica que si el usuario o pulsa el bot´n Calcula se debe recuperar la cantidad, el tiempo y el inter´s o e de la hipoteca que est´n en la Vista. a 4. El Controlador env´ estos datos al Modelo para que calcule la nueva ıa cuota. 5. El Modelo calcula la nueva cuota e informa de ello a la Vista 6. La Vista pide la nueva cuota y la visualiza.
  • 169. 170 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO Figura 11.7: Diagrama UML para el patr´n de dise˜o MVC. o n Aunque en un primer momento, este patr´n puede resultar farragoso, o pa- orecernos que hay muchas idas y venidas entre el c´digo de los actores, es todo lo ocontrario, gracias a esta divisi´n de responsabilidades los posibles cambios en la oimplementaci´n de uno de los actores es completamente transparente al resto. oImagina por ejemplo que hay un cambio en la Vista, si despu´s de cambiar, la eVista sigue proporcion´ndonos los datos sobre los que efectuar el c´lculo y le a apodemos seguir informando de los cambios en el Modelo para que se actualice,este cambio ser´ totalmente transparente tanto para el Controlador como para ael Modelo. F´ ıjate que para poder implementar este patr´n de dise˜o, la Vista debe o nconocer tanto al Controlador como al Modelo, y por su parte el Controladordebe conocer tanto a la Vista como al Modelo. Por su lado, el unico actor que ´necesita conocer el Modelo es a la Vista, de hecho, en la Figura 11.6 no hayninguna flecha que salga desde el Modelo hacia el Controlador. Que un actortenga conocimiento de los otros implica que tendr´ una referencia hacia el actor acon el que necesita intercambiar mensajes. Para dejar a´n m´s clara la potencia u ade este patr´n, vamos a implementar las referencias a una interface y no a una oclase concreta, es decir, la funcionalidad que un actor ofrece la vamos a recogeren un interface y adem´s tendremos la clase concreta que implementar´ el a ainterface, la Figura 11.7 muestra el esquema de clases citado. En el Ap´ndice B se encuentra el c´digo fuente de la aplicaci´n del c´lculo e o o ade la cuota mensual de una hipoteca.Cuestiones. 1. ¿Cuales son las ventajas y desventajas de utilizar el m´todo de conve- e niencia setDefaultCoseOperation(JFrame.EXIT ON CLOSE) para cerrar la aplicaci´n cuando el usuario cierra la ventana?. o
  • 170. ´ ˜11.6. EL PATRON DE DISENO MODELO/VISTA/CONTROLADOR 171 2. El c´digo del c´lculo de la cuota mensual de una hipoteca mostrado en o a el Ap´ndice B sigue la estrategia de no enviar datos desde la Vista al e Controlador cuando se produce el evento de pulsar el bot´n Calcula, es o decir, la Vista no env´ la cantidad hipoteca, el tiempo ni el inter´s, es ıa e el Controlador quien pide estos datos una vez que la Vista le informa de que el usuario puls´ el bot´n Calcula. Lo mismo ocurre cuando el Modelo o o cambia su estado, al informar a la Vista no le env´ el nuevo valor de ıa la cuota mensual de la hipoteca, simplemente le informa de que hay un nuevo valor disponible y es finalmente la Vista quien pide el nuevo valor al Modelo. ¿Cuales son las ventajas de esta aproximaci´n?. ¿Cuales son las desventa- o jas?.Ejercicios. 1. Recupera el ejercicio de la agenda telef´nica y crea un interfaz gr´fico de o a usuario para ella.Lecturas recomendadas. El cap´ ıtulo 1 de la referencia [8] presenta el patr´n de dise˜o MVC ha- o n ciendo referencia al resto de patrones que utiliza. La referencia [3] tambi´n presenta de modo riguroso y muy ameno el e patr´n de dise˜o MVC. o n
  • 171. 172 CAP´ ´ ITULO 11. INTERFACES GRAFICAS DE USUARIO
  • 172. Cap´ ıtulo 12AppletsContenidos 12.1. ¿Qu´ son los Applets? . . . . . . . . . . . . . . . e . . 173 12.2. Ciclo de vida de un Applet . . . . . . . . . . . . . 174 12.3. C´digo HTML para contener un Applet . . . . o . . 175 12.4. Lectura de par´metros de la p´gina HTML . . a a . . 176 12.5. Convertir una aplicaci´n Swing en un Applet o . . 176 12.6. Comunicaci´n entre Applets . . . . . . . . . . . o . . 177Introducci´n o Para muchos de nosotros, el primer contacto con Java fue a trav´s de los eApplets, esos peque˜os programas que se ejecutan dentro de una p´gina web y n acon los que se puede interaccionar. Algunos ejemplos se pueden encontrar en lassiguientes direcciones: http://www.falstad.com/mathphysics.html, http://openastexviewer.net/web/thinlet.html. En este cap´ ıtulo se va a presentar la programaci´n de Apples, sus carac- oter´ ısticas, particularidades, y c´mo, con poco trabajo extra podemos convertir onuestras aplicaciones Swing en Applets siempre que se cumpla una serie derestricciones.12.1. ¿Qu´ son los Applets? eLos Applets son aplicaciones Java que se ejecutan en el contexto de un navegadorweb. A trav´s de c´digo HTML reservamos una zona de la p´gina web para e o avisualizar el Applet, y es el navegador web el encargado de descargar las clasesdel Applet desde la url especificada, iniciar una instancia de la m´quina virtual ade Java y ejecutar el Applet. La seguridad, en el caso de los Applets, es un importante factor a tener encuenta. Para ejecutar un Applet es necesario descargar desde la web las clasesque se ejecutar´n en nuestra m´quina. F´ a a ıjate el riesgo que en principio se corresi no hubiese restricciones de seguridad, descargas un programa que no sabes 173
  • 173. 174 CAP´ ITULO 12. APPLETSquien ha escrito ni con qu´ prop´sito y lo ejecutas en tu m´quina. Si no hubiesen e o arestricciones de seguridad un programa malicioso podr´ acceder a tu disco duro ıapara leer informaci´n personal, o podr´ borrar o modificar ficheros, o escribir o ıaen tu disco duro. Por todo lo anterior, un Applet tiene las siguientes restricciones de seguridad: Un Applet no puede leer del disco duro del cliente. Un Applet no puede escribir al disco duro del cliente. Un Applet no puede abrir conexiones de red a ning´n otro servidor salvo u aquel desde el que se descarg´. o Un Applet no puede ejecutar aplicaciones en el cliente. Un Applet no puede acceder a la informaci´n privada del usuario. o Estas restricciones de seguridad y el hecho de que finalmente el Applet seejecutar´ en un M´quina Virtual Java los hacen muy seguros. a a12.2. Ciclo de vida de un AppletUn Applet se ejecuta en el contexto de un navegador web y tiene fuertes res-tricciones de seguridad, tal y como hemos visto. El hecho de que los Applets seejecuten en el contexto de un navegador web implica que su ciclo de vida no es elde una aplicaci´n de escritorio, como las que ya hemos aprendido a programar. oEl ciclo de vida de un Applet est´ directamente relacionado con las llamadas aque el navegador web hace a m´todos del Applet. e Para que una de nuestras clases sea un Applet debe extender a la claseJApplet que define los siguientes m´todos relacionados con su ciclo de vida: e public void init(), el navegador web llama a este m´todo cuando el e Applet ha sido efectivamente cargado. Este m´todo es el primero que se e invoca en un Applet. public void start(), el navegador web llama a este m´todo para indi- e carle que debe empezar su ejecuci´n. o public void paint(Graphics g), el navegador web llama a este m´to- e do cada vez que se debe dibujar el contenido del Applet, y nos permite el acceso al contexto gr´fico de bajo nivel Graphics. Un detalle muy im- a portante es que desde nuestro c´digo nunca llamaremos directamente a o este m´todo, para forzar su llamada utilizaremos el m´todo public void e e repaint(). public void stop(), el navegador web llama a este m´todo para indicar e que el Applet debe detener su ejecuci´n, por ejemplo, cuando se abandona o la p´gina web que contiene el Applet. a public void destroy(), el navegador web llama a este m´todo antes de e eliminar el Applet de memoria, en cuyo caso se llamar´ previamente al a m´todo stop(). e
  • 174. ´12.3. CODIGO HTML PARA CONTENER UN APPLET 175 Figura 12.1: Llamada a los m´todos de un Applet durante su ciclo de vida. e La Figura 12.1 muestra gr´ficamente el orden de las llamadas entre los m´to- a edos que constituyen el ciclo de vida de un Applet. Para programar de modo eficiente un Applet debemos seguir estas reglas: losrecursos que el Applet necesita para su ejecuci´n deben ser creados en su m´todo o einit(), y esos mismos recursos deben ser liberados en su m´todo destroy(). eNunca llamaremos, desde nuestro c´digo, al m´todo paint(Graphics g) para o eforzar el dibujado del Applet, para ellos utilizaremos el m´todo repaint(). e12.3. C´digo HTML para contener un Applet oPara poder visualizar un Applet al cargar una p´gina HTML debemos utilizar la aetiqueta html como se muestra en el Listado 12.1. El atributo achive nos sirvepara indicar el fichero empaquetado de nuestra aplicaci´n; con la etiqueta code oespecificamos la clase que implementa el Applet, como veremos en la siguientesecci´n; y mediante las etiquetas width y height especificamos el ancho y alto odentro de la p´gina web reservado para la visualizaci´n del Applet. a ohtml head t i t l eEl p r i m e r a p p l e t/ t i t l e / head body a p p l e t a r c h i v e= h i p o t e c a . j a r c o d e= a p p l e t s . h i p o t e c a . H i p o t e c a A p p l e t width =519 h e i g h t =65 S i p u e d e s v e r e s t o tu n a v e g a d o r no s o p o r t a Java . / a p p l e t / body/ htmlListado 12.1: C´digo HTML que muestra la aplicaci´n de la hipoteca dentro de o ouna p´gina web. a Otros atributos de la etiqueta applet que nos pueden ser de utilidad son: alt, muestra un texto alternativo si el navegador no puede visualizar el Applet. align, el alineado del Applet dentro de la p´gina web. a hspace, el margen a la izquierda y derecha del Applet en unidades de p´ ıxeles.
  • 175. 176 CAP´ ITULO 12. APPLETS vspace, el margen superior e inferior del Applet en unidades de p´ ıxeles. name, el nombre del Applet. Etiqueta importante en la comunicaci´n entre o Applets que residen en la misma p´gina web, como veremos en la Secci´n a o 12.6. 12.4. Lectura de par´metros de la p´gina HTML a a La etiqueta applet puede contener otras etiquetas de inter´s adem´s de las e a que ya hemos visto. Con la etiqueta param especificamos un par´metro con a su valor, por ejemplo param name=saludovalue=Hola/. Desde el c´digo o de nuestro Applet podremos leer los par´metros definidos dentro de la etiqueta a Applet con el m´todo String getParameter(String nombreParametro) que e recibe como argumento el nombre del par´metro que queremos leer (saludo a en el ejemplo anterior). Esto nos permite definir par´metros de entrada a nuestro a Applet sin necesidad de modificar el c´digo de nuestro Applet, en vez de ello, o los definiremos en el c´digo HTML que contiene al Applet. o En la Secci´n 12.6 se mostrar´ c´mo hacer uso de esta t´cnica. o a o e 12.5. Convertir una aplicaci´n Swing en un Ap- o plet Un Applet, a efectos pr´cticos, es una aplicaci´n Java con la particularidad de a o que se ejecuta en el contexto de un navegador web. Un Applet tiene una zona, dentro de la p´gina web, donde se va a visualizar, y lo que podemos visualizar a es, entre otras cosas un interfaz gr´fico de usuario. Dicho de otro modo, si a una aplicaci´n Swing cumple con las restricciones de seguridad impuestas a los o Applets, podremos, con pocas modificaciones, transformarla en un Applet. Este es un sencillo recetario para convertir una aplicaci´n Swing en un Ap- o plet: 1. No podemos hacer uso de JFrame, en vez de esta clase utilizaremos JApplet. 2. Un Applet no tiene constructores, el c´digo dentro del constructor en la o aplicaci´n Swing lo escribiremos dentro del m´todo public void init() o e del Applet. 3. No se pueden utilizar m´todos de JFrame relativos al tama˜o de la ventana e n (setSize(...)), al t´ıtulo de esta (setTitle(String titulo), o su posi- ci´n (setLocation(int, int), ya que la posici´n y el tama˜o del Applet o o n se especifican dentro del c´digo HTML. o 4. Un Applet no puede tener escuchadores de tipo WindowListener. Siguiendo estos sencillos pasos, el Listado 12.2 muestra c´mo convertir la o aplicaci´n Swing del Listado B.7 del Ap´ndice B del c´lculo de la cuota mensual o e a de una hipoteca en un Applet.1 package a p p l e t s . h i p o t e c a ;2
  • 176. ´ 12.6. COMUNICACION ENTRE APPLETS 1773 import j a v a x . s w i n g . J A p p l e t ;45 import gui . hipoteca . controlador . Controlador ;6 import gui . hipoteca . controlador . ControladorImpl ;7 import gui . hipoteca . modelo . Modelo ;8 import gui . hipoteca . modelo . ModeloImpl ;9 import gui . hipoteca . v i s t a . Vista ;10 import gui . hipoteca . v i s t a . VistaImpl ;1112 public c l a s s H i p o t e c a A p p l e t extends J A p p l e t {13 private s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 1L ;1415 @Override16 public void i n i t ( ) {17 V i s t a v i s t a = new V i s t a I m p l ( ) ;18 Modelo modelo = new ModeloImpl ( ) ;19 C o n t r o l a d o r c o n t r o l a d o r = new C o n t r o l a d o r I m p l ( ) ;20 modelo . s e t V i s t a ( v i s t a ) ;21 vista . setControlador ( controlador ) ;22 v i s t a . s e t M o d e l o ( modelo ) ;23 c o n t r o l a d o r . s e t M o d e l o ( modelo ) ;24 controlador . setVista ( vista ) ;2526 setContentPane ( v i s t a . getContenedor ( ) ) ;27 }28 } Listado 12.2: Applet con la aplicaci´n del c´lculo de la cuota mensual de una o a hipoteca Como tuvimos cuidado de aislar todo lo relativo al actor Vista siguien- do el patr´n de dise˜o MVC, la transformaci´n de aplicaci´n Swing a Ap- o n o o plet ha sido muy sencilla, de hecho, hemos podido aprovechar todas las clases dentro los paquetes gui.hipoteca.modelo, gui.hipoteca.vista y gui.hipoteca.controlador. 12.6. Comunicaci´n entre Applets o Applets que residen dentro de la misma p´gina web pueden obtener referencias a a los otros Applets dentro de la misma p´gina y a trav´s de estas referencias a e un Applet puede llamar a los m´todos de otro Applet dentro de la misma p´gi- e a na web. Para ello utilizaremos el m´todo Applet getApplet(String nombre) e que recibe como argumento el nombre del Applet del que queremos obtener una referencia. Recuerda que el nombre de un Applet lo podemos definir con el atributo name de la etiqueta applet. Este m´todo pertenece al contexto e donde se est´ ejecutando el Applet, que no es otra cosa que la propia p´gina a a web. Para obtener este contexto desde un Applet utilizamos el m´todo public e AppletContext getAppletContext(). El Listado 12.3 muestra un sencillo ejemplo de comunicaci´n entre Applets o residentes en la misma p´gina web mostrado en el Listado 12.4. La Figura 12.2 a muestra c´mo se visualiza la p´gina web en un navegador. o a1 package a p p l e t s . c o m u n i c a c i o n ;23 import j a v a . awt . BorderLayout ;4 import j a v a . awt . e v e n t . A c t i o n E v e n t ;5 import j a v a . awt . e v e n t . A c t i o n L i s t e n e r ;67 import j a v a x . s w i n g . J A p p l e t ;8 import j a v a x . s w i n g . JButton ;9 import j a v a x . s w i n g . J L a b e l ;
  • 177. 178 CAP´ ITULO 12. APPLETS10 import j a v a x . s w i n g . J P a n e l ;11 import j a v a x . s w i n g . J T e x t F i e l d ;1213 public c l a s s C o m u n i c a c i o n A p p l e t s extends J A p p l e t {14 private s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 1L ;15 private J T e x t F i e l d j t f T u D i c e s ;16 private J L a b e l j l E l D i c e ;17 private C o m u n i c a c i o n A p p l e t s e l O t r o = n u l l ;18 J P a n e l c o n t e n e d o r = new J P a n e l (new BorderLayout ( ) ) ;1920 @Override21 public void i n i t ( ) {22 J P a n e l miEntrada = new J P a n e l ( ) ;23 miEntrada . add (new J L a b e l ( T u d i c e s : ) ) ; ´24 j t f T u D i c e s = new J T e x t F i e l d ( 5 0 ) ;25 miEntrada . add ( j t f T u D i c e s ) ;26 JButton j b E n v i a r = new JButton ( E n v i a r ) ;27 j b E n v i a r . a d d A c t i o n L i s t e n e r (new E s c u c h a d o r ( ) ) ;28 miEntrada . add ( j b E n v i a r ) ;29 J P a n e l s u E n t r a d a = new J P a n e l ( ) ;30 j l E l D i c e = new J L a b e l ( E s c u c h a n d o . . . . ) ;31 s u E n t r a d a . add ( j l E l D i c e ) ;32 c o n t e n e d o r . add ( suEntrada , BorderLayout .SOUTH) ;33 c o n t e n e d o r . add ( miEntrada , BorderLayout .NORTH) ;34 setContentPane ( contenedor ) ;35 }3637 public void r e c i b e M e n s a j e ( S t r i n g m e n s a j e ) {38 j l E l D i c e . setText ( M e n s a j e : + mensaje ) ;39 repaint () ;40 }4142 private c l a s s E s c u c h a d o r implements A c t i o n L i s t e n e r {43 @Override44 public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t e ) {45 i f ( e l O t r o == n u l l )46 elOtro = ( ComunicacionApplets ) getAppletContext ( ) . getApplet ( getParameter ( ElOtro ) ) ;47 elOtro . recibeMensaje ( jtfTuDices . getText ( ) ) ;48 }49 }5051 } Listado 12.3: C´digo HTML de la p´gina visualizada en la Figura 12.2 o a1 html2 head3 t i t l e C o n v e r s a c i ´ n e n t r e A p p l e t s / t i t l e o4 /head56 body7 a p p l e t8 a r c h i v e= c o m u n i c a c i o n A p p l e t s . j a r 9 c o d e= a p p l e t s . c o m u n i c a c i o n . C o m u n i c a c i o n A p p l e t s 10 width =80011 h e i g h t =7012 name= S u p e r i o r 13 param14 name= E l O t r o 15 v a l u e= I n f e r i o r /16 S i p u e d e s v e r e s t o tu n a v e g a d o r no s o p o r t a Java .17 / a p p l e t 18 br /19 a p p l e t20 a r c h i v e= c o m u n i c a c i o n A p p l e t s . j a r 21 c o d e= a p p l e t s . c o m u n i c a c i o n . C o m u n i c a c i o n A p p l e t s 22 width =80023 h e i g h t =7024 name= I n f e r i o r 25 param26 name= E l O t r o
  • 178. ´ 12.6. COMUNICACION ENTRE APPLETS 179 Figura 12.2: Llamada a los m´todos de un Applet durante su ciclo de vida. e27 v a l u e= S u p e r i o r /28 S i p u e d e s v e r e s t o tu n a v e g a d o r no s o p o r t a Java .29 /a p p l e t 30 /body31 /html Listado 12.4: Applet con la aplicaci´n del c´lculo de la cuota mensual de una o a hipoteca Ejercicios. 1. Convierte la aplicaci´n de la Agenda en un Applet para que puedas inter- o accionar con ella a trav´s de un navegador web. e Lecturas recomendadas. En esta direcci´n web http://download.oracle.com/javase/tutorial/ o deployment/applet/index.html podr´s encontrar toda la informaci´n a o necesaria para programar Applets.
  • 179. 180 CAP´ ITULO 12. APPLETS
  • 180. Cap´ ıtulo 13Control de errores conMyLyn y BugzillaContenidos 13.1. Sistema de control de tareas MyLyn . . . . . . . . 182 13.1.1. Cual es el objetivo de MyLyn . . . . . . . . . . . . . 182 13.1.2. Trabajar con MyLyn . . . . . . . . . . . . . . . . . . 182 13.1.2.1. Trabajar con tareas . . . . . . . . . . . . . 182 13.1.2.2. Trabajar con categor´ . . . . . . . . . . . ıas 186 13.1.2.3. Trabajar con Working Sets . . . . . . . . . 187 13.2. Sistema de gesti´n de errores Bugzilla . . . . . . . o 188 13.2.1. Cual es el objetivo de Bugzilla . . . . . . . . . . . . 188 13.2.2. Instalaci´n de Bugzilla . . . . . . . . . . . . . . . . . o 188 13.2.3. Trabajar con Bugzilla . . . . . . . . . . . . . . . . . 195 13.3. Acceso a Bugzilla desde MyLyn y Eclipse . . . . . 199 13.3.1. Beneficios de la combinaci´n de Bugzilla y MyLyn o desde Eclipse . . . . . . . . . . . . . . . . . . . . . . 201 13.3.2. Trabajo con MyLyn y Bugzilla desde Eclipse . . . . 201 13.3.2.1. A˜adir errores a Bugzilla desde Eclipse . . n 201 13.3.2.2. Recuperar errores desde Bugzilla como ta- reas en MyLyn . . . . . . . . . . . . . . . . 201Introducci´n oLos Entornos de Desarrollo Integrado son una excelente herramienta para lacreaci´n y mantenimiento de c´digo. Sin embargo, cuando se trabaja en pro- o oyectos cuyo c´digo est´ organizado en un gran n´mero de clases dentro de sus o a ucorrespondiente paquetes, navegar por le c´digo para encontrar un m´todo o o euna clase puede llegar a consumir mucho tiempo. MyLyn es una herramienta,integrada en Eclipse a trav´s de un plug-in, que oculta el c´digo que no es re- e olevante a la tarea que estamos llevando a cabo. Aunque MyLyn es mucho m´s aque eso. 181
  • 181. 182CAP´ ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA Por otro lado, una tarea importante en todo proyecto inform´tica es el segui- amiento y gesti´n de errores en el c´digo. Bugzilla es una excelente herramienta o oque cumple esta funci´n. o Quiz´s, lo mejor de ambas herramientas es la posibilidad de combinarlas adentro de Eclipse de tal manera que podemos inicial el seguimiento de un erroren Bugzilla desde el propio Eclipse y darlo de alta como una tarea en MyLynpara poder centrar nuestra atenci´n s´lo en el c´digo relativo a ese error. o o o13.1. Sistema de control de tareas MyLynMyLyn es una potente herramienta que se encuentra en el paquete b´sico de aEclipse. Al descargarnos Eclipse nos estamos descargando tambi´n esta herra- emienta.13.1.1. Cual es el objetivo de MyLynCuando trabajamos en equipo y en grandes proyectos es usual que el c´digo ofuente del proyecto est´ organizado en un gran n´mero de paquetes, dentro de e ulos cuales encontramos un gran n´mero de clases. El n´mero total de ficheros u ucon c´digo fuente puede ser muy elevado. Generalmente, cuando desarrollamos ouna tarea dentro de un proyecto de gran envergadura, s´lo son relevantes a esta otarea un n´mero reducido de ficheros frente al total. Si el entorno de desarrollo uque estamos utilizando nos presenta todos los ficheros del proyecto, podemosperder bastante tiempo navegando por ellos y cribando los que s´ son relevantes ıa nuestra tarea. En esencia, el objetivo de MyLyn es permitir concentrarnos s´lo en el c´digo o ode un proyecto sobre el que estamos trabajando, ocultando el resto del c´digo del oproyecto que no es relevante a la tarea que estamos llevando a cabo. Pero MyLynno es s´lo eso, si no un sistema de control de trabajo que podemos utilizar bien de oforma local o bien en conexi´n con un sistema de control de errores. Y es en este oultimo caso cuando Bugzilla se convierte en una herramienta imprescindible.´ En MyLyn la unidad b´sica de trabajo es la tarea. Las tareas se pueden aorganizar en categor´ y estas a su vez se pueden agrupar en grupos de trabajo. ıasVeamos c´mo trabajar con cada uno de estos nuevos conceptos. o13.1.2. Trabajar con MyLynMyLyn dispone de una vista propia en Eclipse. El nombre de esta vista es TaskList. Si no aparece esta vista por defecto, puedes hacerla visible seleccionandola opci´n que se muestra en la Figura 13.1. o La nueva vista que se abrir´ en Eclipse se muestra en la Figura 13.2. a13.1.2.1. Trabajar con tareasLo primero que vamos a hacer es crear una nueva tarea, para ello, despliega elicono que se muestra en la Figura 13.3 y selecciona la opci´n New Task.... o A continuaci´n se nos abrir´ la ventana que se muestra en la Figura 13.4 o adonde debemos seleccionar la opci´n por defecto Local. Lo que estamos haciendo oen este punto es indicarle a MyLyn que la tarea que estamos creando es local a
  • 182. 13.1. SISTEMA DE CONTROL DE TAREAS MYLYN 183 Figura 13.1: Opci´n de men´ para abrir la vista de MyLyn. o u Figura 13.2: Aspecto de la vista Task List de MyLyn.
  • 183. 184CAP´ ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA Figura 13.3: Creaci´n de una nueva tarea en MyLyn. onuestra m´quina de desarrollo, no estamos utilizando ning´n repositorio externo. a uLa creaci´n de tareas en repositorios externos la cubriremos en la secci´n 13.3. o o Tras seleccionar el repositorio local, se nos abrir´ la ventana mostrada en la aFigura 13.5 donde podemos introducir las propiedades de la nueva tarea. En esta ventana podemos definir las siguiente propiedades para la nuevatarea: Nombre de la tarea. Status: el estado de la tarea, que puede ser Completo o Incompleto. Scheduled : cuando est´ planificado que trabajaremos en la tarea. a Due: en que fecha debe estar completada la tarea. Estimate: n´mero de horas estimadas de dedicaci´n a la tarea. u o Campo de comentarios. Una vez definidas estas propiedades de la nueva tarea, podemos guardarla,y al hacerlo, veremos que se actualiza la vista Task List con la nueva tarea,que aparecer´ en la carpeta Uncategorized. En esta carpeta se a˜aden, de modo a nautom´tico, todas las tareas que no se han asignado a ninguna categor´ a ıa. Antes de ver c´mo crear nuevas categor´ veamos cual es el trabajo b´sico o ıas acon las tareas. MyLyn nos permite concentrarnos s´lo en el