Sie sind auf Seite 1von 269

2

Esta obra de Oscar Belmonte et al. est a bajo una licencia Creative Commons Reconocimiento-NoComercial-CompartirIgual 3.0 Unported

Indice general
1. Introducci on 1.1. Origen del lenguaje de programaci on Java . . . . . . . . . . . . . 1.2. Caracter stica de Java . . . . . . . . . . . . . . . . . . . . . . . . 1.3. El entorno de desarrollo integrado Eclipse . . . . . . . . . . . . . 1.3.1. Principales caracter sticas del entorno de desarrollo Eclipse 1.3.2. Descarga e instalaci on de Eclipse . . . . . . . . . . . . . . 1.3.3. Congurando el aspecto de Eclipse: Perspectivas y Vistas 1.3.4. El primer ejemplo . . . . . . . . . . . . . . . . . . . . . . 1.4. Herramientas de desarrollo . . . . . . . . . . . . . . . . . . . . . . 1.4.1. A nadiendo nueva funcionalidad a Eclipse: los plug-ins . . 2. Clases 2.1. Denici on de una clase . . . . . . . . . . . . . . 2.2. Miembros de una clase . . . . . . . . . . . . . . 2.2.1. Atributos de una clase . . . . . . . . . . 2.2.2. M etodos de una clase. . . . . . . . . . . 2.2.3. Constructores. . . . . . . . . . . . . . . 2.2.4. Sobrecarga de m etodos y constructores . 2.3. Tipos de datos en Java. . . . . . . . . . . . . . 2.3.1. Arrays de datos en Java. . . . . . . . . . 2.4. Estructuras de control. . . . . . . . . . . . . . . 2.4.1. Estructuras de control de repetici on. . . 2.4.2. Estructuras de control de selecci on. . . . 2.5. Modicadores de acceso. . . . . . . . . . . . . . 2.6. Modicadores static y final. . . . . . . . . . 2.7. El recolector de basura. . . . . . . . . . . . . . 2.8. Finalizaci on. . . . . . . . . . . . . . . . . . . . 2.9. Comentarios. Comentarios de documentaci on. . 13 13 14 15 16 16 16 18 21 22 23 24 25 25 26 28 32 33 34 36 37 39 40 42 43 44 45 51 52 52 54 56 59 59 60 61

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . .

3. Herencia e Interfaces 3.1. Herencia. . . . . . . . . . . . . . . . . . . . . . . . . 3.2. Extensi on de una clase. . . . . . . . . . . . . . . . . 3.2.1. Sobrescribir atributos. . . . . . . . . . . . . . 3.2.2. Sobrescribir m etodos. . . . . . . . . . . . . . 3.2.3. La palabra reservada super. . . . . . . . . . . 3.2.4. El constructor por defecto y la clase Object. 3.2.5. El operador instanceof. . . . . . . . . . . . 3.2.6. El modicador final. . . . . . . . . . . . . . 3

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

4 3.2.7. M etodos static. . . . Clases abstractas. . . . . . . . Interfaces. . . . . . . . . . . . Enumeraciones. . . . . . . . . Paquetes en Java. . . . . . . . Clases e interface anidados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

INDICE GENERAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 63 65 68 69 71 75 76 76 77 78 82 84 87 87 88 88 91 93 94 94 95 95 96 98 99 100 101 102 103 105 106 107 108 109 110 110 110 112 117 118 120 120 121 122

3.3. 3.4. 3.5. 3.6. 3.7.

4. Subversion 4.1. Qu e es un sistema de control de versiones? . . 4.2. Principales caracter sticas de Subversion . . . . 4.3. Creaci on de un repositorio . . . . . . . . . . . . 4.4. Trabajo con repositorios . . . . . . . . . . . . . 4.4.1. Obteniendo informaci on del repositorio 4.5. Integraci on con Eclipse . . . . . . . . . . . . . . 5. Excepciones 5.1. Qu e es una excepci on? . . . . . 5.1.1. Tipos de excepciones . . . 5.2. C omo se gestiona una excepci on 5.3. Creaci on de excepciones propias .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

6. Pruebas unitarias con JUnit 6.1. Qu e son las pruebas unitarias? . . . . . . . . . . . . . . . . . 6.1.1. Principios FIRST para el dise no de pruebas unitarias 6.2. Pruebas unitarias con JUnit . . . . . . . . . . . . . . . . . . . 6.2.1. Creaci on de clases de prueba . . . . . . . . . . . . . . 6.2.2. La anotaci on @Test . . . . . . . . . . . . . . . . . . . 6.2.3. Las anotaciones @Before y @After . . . . . . . . . . . 6.2.4. Las anotaciones @BeforeClass y @AfterClass . . . . 6.2.5. Pruebas con bater a de datos de entrada . . . . . . . . 6.2.6. Ejecutar varias clases de prueba. Test Suites . . . . . 6.3. Cobertura de las pruebas . . . . . . . . . . . . . . . . . . . . 6.3.1. EclEmma y su plug-in para Eclipse . . . . . . . . . . . 7. Entrada y Salida 7.1. Flujos (Streams ) . . . . . . . . . . . . . . . . 7.2. Flujos de bytes . . . . . . . . . . . . . . . . . 7.3. Flujos de caracteres . . . . . . . . . . . . . . 7.4. Conexi on entre ujos de bytes y de caracteres 7.5. El sistema de cheros y ujos a cheros . . . 7.5.1. El sistema de cheros . . . . . . . . . 7.5.2. Flujos a cheros . . . . . . . . . . . . 7.6. Serializaci on . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

8. Algunas clases de utilidad del paquete est andar 8.1. La clase Scanner . . . . . . . . . . . . . . . . . . . 8.2. Trabajo con cadenas de caracteres . . . . . . . . . 8.2.1. La clase String . . . . . . . . . . . . . . . 8.2.2. Las clases StringBuffer y StringBuilder 8.3. Clases recubridoras . . . . . . . . . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

INDICE GENERAL 8.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . 8.5. Trabajo con fechas . . . . . . . . . . . . . . . . . . 8.5.1. La clase Date . . . . . . . . . . . . . . . . . 8.5.2. Las clases Calendar y GregorianCalendar 8.6. Matem aticas . . . . . . . . . . . . . . . . . . . . . 8.6.1. La clase Math . . . . . . . . . . . . . . . . . 8.6.2. La clase Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5 124 128 128 129 129 129 130 133 133 134 135 138 139 139 141 143 144 144 145 145 146 146 147 148 150 151 151 153 154 155 155 157 162 162 162 163 164 166 166 168 173 173 174 175 176

9. Programaci on con gen ericos 9.1. Qu e son los tipos de datos gen ericos? . . . . . . . . . 9.2. M etodos gen ericos . . . . . . . . . . . . . . . . . . . . 9.3. Clases gen ericas . . . . . . . . . . . . . . . . . . . . . . 9.4. Ampliaci on del tipo gen erico . . . . . . . . . . . . . . 9.4.1. Tipos gen ericos con l mite superior . . . . . . . 9.4.2. Comodines . . . . . . . . . . . . . . . . . . . . 9.5. Borrado de tipo y compatibilidad con c odigo heredado 10.Construcci on de proyectos con Ant 10.1. Qu e es Ant . . . . . . . . . . . . . . . . . 10.2. Denici on del proyecto . . . . . . . . . . . 10.2.1. Objetivos . . . . . . . . . . . . . . 10.2.2. Tareas . . . . . . . . . . . . . . . . 10.3. Compilar el c odigo fuente de un proyecto 10.4. Propiedades . . . . . . . . . . . . . . . . . 10.5. Estructuras path-like . . . . . . . . . . . 10.6. Ejecuci on de las Pruebas Unitarias . . . . 10.7. Generaci on de la documentaci on . . . . . 10.8. Empaquetado de la aplicaci on . . . . . . . 10.9. Ejecuci on y limpieza . . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

11.Interfaces gr acas de usuario 11.1. APIs para la programaci on de interfaces gr acos de usuario en Java: AWT y Swing . . . . . . . . . . . . . . . . . . . . . . . . . 11.2. Contenedores y Componentes . . . . . . . . . . . . . . . . . . . . 11.3. Gestores de Aspecto (Layout Managers ) . . . . . . . . . . . . . . 11.4. Detecci on de eventos: Escuchadores . . . . . . . . . . . . . . . . . 11.5. Algunos componentes Swing . . . . . . . . . . . . . . . . . . . . . 11.5.1. JLabel, muestra texto o iconos . . . . . . . . . . . . . . . 11.5.2. JButton, botones que el usuario puede pulsar . . . . . . . 11.5.3. JTextField, campos de introducci on de texto . . . . . . . 11.5.4. JRadioButton, botones de opciones . . . . . . . . . . . . 11.5.5. JCheckBox, botones de selecci on m ultiple . . . . . . . . . 11.5.6. JList, listas de selecci on . . . . . . . . . . . . . . . . . . 11.6. El patr on de dise no Modelo/Vista/Controlador . . . . . . . . . . 12.Applets 12.1. Qu e son los Applets? . . . . . . . . . . . . 12.2. Ciclo de vida de un Applet . . . . . . . . . 12.3. C odigo HTML para contener un Applet . . 12.4. Lectura de par ametros de la p agina HTML

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

INDICE GENERAL 12.5. Convertir una aplicaci on Swing en un Applet . . . . . . . . . . . 176 12.6. Comunicaci on entre Applets . . . . . . . . . . . . . . . . . . . . . 177

13.Control de errores con MyLyn y Bugzilla 13.1. Sistema de control de tareas MyLyn . . . . . . . . . . . . . . . . 13.1.1. Cual es el objetivo de MyLyn . . . . . . . . . . . . . . . . 13.1.2. Trabajar con MyLyn . . . . . . . . . . . . . . . . . . . . . 13.2. Sistema de gesti on de errores Bugzilla . . . . . . . . . . . . . . . 13.2.1. Cual es el objetivo de Bugzilla . . . . . . . . . . . . . . . 13.2.2. Instalaci on de Bugzilla . . . . . . . . . . . . . . . . . . . . 13.2.3. Trabajar con Bugzilla . . . . . . . . . . . . . . . . . . . . 13.3. Acceso a Bugzilla desde MyLyn y Eclipse . . . . . . . . . . . . . 13.3.1. Benecios de la combinaci on de Bugzilla y MyLyn desde Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3.2. Trabajo con MyLyn y Bugzilla desde Eclipse . . . . . . .

181 182 182 182 188 188 188 195 199 201 201

14.Programaci on concurrente con Hilos 207 14.1. Qu e es un hilo? Utilidades. Consideraciones sobre el uso de hilos 208 14.2. Creaci on de hilos en Java . . . . . . . . . . . . . . . . . . . . . . 209 14.2.1. Creaci on de un Hilo extendiendo a la clase Thread . . . . 209 14.2.2. Creaci on de un Hilo mediante una clase interna . . . . . . 210 14.2.3. Creaci on de un Hilo mediante una clase interna an onima . 211 14.3. Ciclo de vida de un hilo . . . . . . . . . . . . . . . . . . . . . . . 212 14.4. Control de hilos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 14.5. Sincronizaci on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 14.5.1. Sincronizac on utilizando los cerrojos intr nsecos . . . . . . 215 14.5.2. Sincronizaci on utilizando el interface Lock . . . . . . . 218 15.Programaci on para la Red 15.1. Trabajo con URLs . . . . . . . . . . . 15.1.1. Qu e es una URL? . . . . . . . 15.1.2. Leer desde una URL . . . . . . 15.1.3. Escribir a una URL . . . . . . 15.2. Trabajo con Sockets . . . . . . . . . . 15.2.1. Qu e es un Socket? . . . . . . . 15.2.2. Sockets bajo el protocolo TCP 15.2.3. Sockets bajo el protocolo UDP 221 222 222 223 223 225 225 225 227 231 232 233 233 233 233 234 234 235 235 236 238

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

16.Patrones de dise no 16.1. Principios de POO . . . . . . . . . . . . . 16.2. Qu e son los patrones de dise no? . . . . . 16.3. Qu e es el acoplamiento entre clases y por 16.4. Grupos de patrones de dise no . . . . . . . 16.5. El patr on de dise no Singleton . . . . . . . 16.5.1. Situaci on que intenta resolver . . . 16.5.2. Ejemplo de implementaci on . . . . 16.6. El patr on de dise no Factory Method . . . 16.6.1. Situaci on que intenta resolver . . . 16.6.2. Ejemplo de implementaci on . . . . 16.7. El patr on de dise no Abstract Factory . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . qu e hay que evitarlo? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

INDICE GENERAL 16.7.1. Situaci on que intenta resolver 16.7.2. Ejemplo de implementaci on . 16.8. El patr on de dise no Strategy . . . . . 16.8.1. Situaci on que intenta resolver 16.8.2. Ejemplo de implementaci on . 16.9. El patr on de dise no Observer . . . . 16.9.1. Situaci on que intenta resolver 16.9.2. Ejemplo de implementaci on . 16.10. El patr on de dise no Decorator . . . . 16.10.1.Situaci on que intenta resolver 16.10.2.Ejemplo de implementaci on . A. build.xml B. Aplicaci on Hipoteca C. Ejemplo sincronizaci on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7 238 238 244 245 245 247 247 248 249 250 250 255 259 265

INDICE GENERAL

Prefacio
La escritura de un libro es una tarea ingente. La motivaci on para abordarla debe ser, al menos, tan grande como la tarea que se desea acometer. Para nosotros, la motivaci on ha consistido en escribir un libro que se distinguiera del resto de libros que abordan el aprendizaje del lenguaje de programaci on Java. Por un lado, existen excelentes libros que muestran c omo programar en Java. Por otro lado existen excelentes libros, en n umero inferior, que muestran c omo utilizar herramientas de ayuda y soporte al desarrollo de proyectos en Java. Pensamos que, entre ellos, exist a cabida para escribir un libro que abordase el aprendizaje de Java al mismo tiempo que las herramientas imprescindibles de ayuda al desarrollo. Dentro de nuestra Universidad, la Jaume I, hemos impartido, y seguimos haci endolo, cursos sobre el lenguaje de programaci on Java para todo tipo de alumnado: desde alumnos de las distintas titulaciones de inform atica, alumnos extranjeros en el Master Europeo Erasmus Mundus sobre tecnolog as Geoespaciales, hasta profesionales que quieren mantener al d a su conocimiento y mejorar sus expectativas laborales. Esta experiencia nos ha dado la conanza suciente como para animarnos a escribir el presente libro. Y, a pesar del contacto casi diario con Java y sus tecnolog as, reconocemos que a un nos queda mucho por aprender, que el mundo que brinda el aprendizaje de Java es inmenso y que se renueva constantemente. Esto u ltimo es s ntoma de que la comunidad alrededor de esta tecnolog a est a viva y posee un gran entusiasmo.

Objetivos del libro


Dos son los objetivos principales del este libro: Presentar el lenguaje de programaci on Java. Presentar algunas de las herramientas de desarrollo que ayudan en el desarrollo de proyectos utilizando Java. Con un poco m as de detalle, en el primer objetivo hemos pretendido no s olo presentar el lenguaje de programaci on, adem as indicamos unas directrices para crear c odigo de calidad, c odigo que sea f acil leer, f acil mantener y que se puede probar de manera autom atica. El segundo de los objetivos es casi una necesidad imperiosa a los equipos de desarrollo que siguen utilizando como herramienta de control de versiones un directorio compartido. O a aquellos equipos de desarrollo que siguen probando 9

10

INDICE GENERAL

sus aplicaciones de manera manual. O para aquellos equipos de desarrollo que utilizan como sistema de seguimiento de errores el correo electr onico. Y un largo etc etera de pr acticas desaconsejadas.

C omo est a organizado este libro


La Figura 1 muestra la organizaci on en cap tulos del presente libro. Cada uno de los recuadros representa un cap tulo. Los cap tulos se han agrupado en dos grandes bloques. En el primero de ellos Java b asico hemos agrupado los cap tulos que consideramos introductorios, y que representan el n ucleo de la programaci on orientada a objetos en Java. En el segundo grupo Java avanzado aparecen los cap tulos que consideramos aspectos avanzados del lenguaje con respecto a los cap tulos del primer grupo. En ambos grupos hay cap tulos que no aparecen en la l nea principal del ujo, estos cap tulos son los que presentan herramientas que consideramos de gran utilidad en el desarrollo de proyectos inform aticos utilizando tecnolog as Java. El orden de introducci on de estas herramientas ha sido fuente de largas conversaciones: Es conveniente introducir al principio la herramienta JUnit siguiendo una orientaci on hacia el desarrollo guiado por pruebas? Debemos delegar hasta el segundo bloque de cap tulos el dedicado a la construcci on de proyectos con Ant? Hemos optado por seguir un orden quiz as m as conservado y menos arriesgado, intentando presentar las herramientas en el momento en que conceptualmente se entienda cual es la necesidad que vienen a cubrir. Esperamos que esta ordenaci on haga el tr ansito suave entre el aprendizaje de Java como lenguaje de programaci on y las herramientas de ayuda al desarrollo.

Quien deber a leer este libro


El publico objetivo de este libro son los desarrolladores que quieran aprender el lenguaje de programaci on Java y ya posean conocimientos de programaci on estructurada y orientaci on a objetos. Los conceptos del lenguaje son presentados desde la base, suponiendo que es la primera vez que el lector se aproxima al lenguaje de programaci on Java. Pero este libro tambi en est a pensado para aquellas personas que conocen el lenguaje de programaci on Java y a un no han descubierto la gran cantidad de herramientas de ayuda que existen en el desarrollo de proyecto.

Agradecimientos
La secci on de agradecimientos es posiblemente una de las m as complicadas de escribir. Debe tener un equilibrio entre el espacio dedicado a ella y el reconocimiento a todas las personas, que de un modo u otro han contribuido a que un libro tenga su forma nal. Para no dejarnos por citar el nombre de nadie, preferimos ampliar nuestro agradecimiento a colectivos. En primer lugar a nuestro alumnos, por que a n de cuentas es a ellos a los que va dirigido este libro. Con sus preguntas, apreciaciones, comentarios y dudas nos han ayudado a darnos cuenta de donde estaban los escollos en la lectura de este libro.

INDICE GENERAL

11

Figura 1: Organizaci on del libro.

12

INDICE GENERAL

Tambi en a nuestros compa neros de la Universidad, porque con sus comentarios y recticaciones nos han ayudado a eliminar errores en los contenidos. Y nalmente a nuestros amigos por su animo constante para que esta labor llegase a buen puerto. A todos ellos gracias.

Cap tulo 1

Introducci on
Contenidos
1.1. Origen del lenguaje de programaci on Java . . . . 1.2. Caracter stica de Java . . . . . . . . . . . . . . . . . 1.3. El entorno de desarrollo integrado Eclipse . . . . 1.3.1. Principales caracter sticas del entorno de desarrollo Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.2. Descarga e instalaci on de Eclipse . . . . . . . . . . . 1.3.3. Congurando el aspecto de Eclipse: Perspectivas y Vistas . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4. El primer ejemplo . . . . . . . . . . . . . . . . . . . 1.4. Herramientas de desarrollo . . . . . . . . . . . . . 1.4.1. A nadiendo nueva funcionalidad a Eclipse: los plug-ins 13 14 15 16 16 16 18 21 22

Introducci on
En este cap tulo de introducci on se muestran los or genes del lenguaje de programaci on Java, sus principales caracter sticas y las diferencias con respecto a C++, lenguaje del que hereda gran parte de la sintaxis. En la segunda parte del cap tulo se presenta en Entorno de Desarrollo Integrado Eclipse, los fundamentos para crear proyectos Java utilizando este Entorno y c omo se puede enriquecer su funcionalidad mediante la instalaci on de plug-ins.

1.1.

Origen del lenguaje de programaci on Java

El lenguaje de programaci on Java tiene sus or genes en un lenguaje de programaci on anterior, llamado Oak (roble en ingl es), que naci o de un proyecto interno en Sun Microsystems en el a no 1991 llamado Green project. Oak fue creado con el objetivo de ser el lenguaje de programaci on con el que programar dispositivos electr onicos dom esticos, en particular aparatos de televisi on inteligentes e interactivos. Oak ten a, entre otras, las siguientes caracter sticas de inter es: 13

14

CAP ITULO 1. INTRODUCCION Orientado a objetos y de prop osito general. Robusto. Sintaxis parecida a C++. Independiente del hardware.

El proyecto de televisi on inteligente e interactiva nunca se materializ o. De modo simult aneo, a principios de la d ecada de los 90 surgi o Internet y con ella, la aparici on de los primeros navegadores web. Los l deres del Green project fueron conscientes de la importancia que iba a tener Internet y orientaron su lenguaje de programaci on Oak para que programas escritos en este lenguaje de programaci on se pudiesen ejecutar dentro del navegador web Mozilla. Y este fue el inicio de Java, as llamado porque cuando se intent o registrar el nombre Oak este ya estaba registrado. Las nuevas caracter sticas generales que se a nadieron son: Seguridad, ya que los programas que se ejecutan en un navegador se descargan desde Internet. Potencia, ya no se ten a la restricci on de la ejecuci on en dispositivos de electr onica de consumo. Java se ha consolidado como lenguaje de programaci on gracias a que su curva de aprendizaje es relativamente suave para programadores provenientes de C++. Adem as, la ventaja de que un programa escrito en Java se puede ejecutar en una gran cantidad de plataformas ha hecho de el un interesante lenguaje de programaci on por su ((universalidad)).

1.2.

Caracter stica de Java

Java es un lenguaje de programaci on orientado a objetos y de prop osito general que toma de otros lenguajes de programaci on algunas ideas fundamentales, en particular toma de Smalltalk el hecho de que los programas Java se ejecutan sobre una m aquina virtual . Y del lenguaje de programaci on C++ toma su sintaxis. El uso de la m aquina virtual garantiza la independencia de la plataforma en Java. Si disponemos de una m aquina virtual para nuestra plataforma, podremos ejecutar el mismo programa escrito en Java sin necesidad de volverlo a compilar. En el proceso de compilaci on de un programa en Java, se genera un c odigo intermedio, llamado bytecode , que la m aquina virtual interpreta y traduce a llamadas nativas del sistema sobre el que se ejecuta la m aquina virtual. As , una m aquina virtual para una plataforma Windows 7 de 64 bits, traducir a los bytecodes a c odigo nativo para esta plataforma, y otra m aquina virtual para una plataforma Linux de 64 bits traducir a los mismos bytecodes a c odigo nativo para esta otra plataforma. Los bytecodes son los mismos en ambos casos, las m aquinas virtuales sobre las que se ejecutan son nativas de la plataforma correspondiente. Puede parecer que este paso de traducir los bytecodes a c odigo nativo de la plataforma suponga una p erdida de rendimiento en la ejecuci on de los programas en Java, pero esto no es as gracias a la introducci on de la tecnolog a JIT (Just

1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE

15

In Time compilation). La idea b asica de esta tecnolog a es que la primera vez que se llama a un m etodo, este se interpreta generando c odigo nativo de la plataforma sobre la que se ejecuta la m aquina virtual, pero una vez generado este c odigo nativo, se almacena, de tal modo que la siguiente vez que se llama al mismo m etodo no es necesaria su interpretaci on ya que el c odigo nativo para ese m etodo se almacen o previamente. Otras caracter sticas generales de Java son: Seguridad desde el punto de vista del programador: Comprobaci on estricta de tipos. Gesti on de excepciones. No existen punteros. Recolector de basura. Seguridad desde el punto de vista del usuario de aplicaciones: Los programas se ejecutan sobre una m aquina virtual. Espacio de nombre. Soporta programaci on concurrente de modo nativo. Los tipos de datos est an estandarizados. S olo se admite herencia simple.

1.3.

El entorno de desarrollo integrado Eclipse

Un entorno integrado de desarrollo o IDE de sus siglas en ingl es (emphIntegrated Develop Environment) nos permite escribir c odigo de un modo c omodo. La comodidad reside en que los entornos de desarrollo integrados son mucho m as que un simple editor de textos. Algunas caracter sticas comunes a los IDE son: Coloreado de la sintaxis. Herramientas de b usqueda. Asistentes para la escritura de c odigo. Ejecuci on de aplicaciones sin abandonar el entorno. Herramientas de depuraci on de c odigo. Junto a estas caracter sticas, los modernos IDE poseen algunas otras realmente espectaculares como por ejemplo: Conexi on con sistemas de control de versiones. Conexi on con sistema de seguimiento de errores. Facilidades para la creaci on de tareas. Herramientas avanzadas para el an alisis de c odigo.

16

CAP ITULO 1. INTRODUCCION Herramientas de an alisis de rendimiento. Conexi on a gestores de bases de datos.

Eclipse es un IDE orientado al desarrollo de proyectos con tecnolog a Java, aunque no es el u nico lenguaje de programaci on al que da soporte. Eclipse es una herramienta de software libre, mantenido por la Eclipse Foundation.

1.3.1.

Principales caracter sticas del entorno de desarrollo Eclipse

Eclipse re une todas las caracter sticas comunes a los modernos IDE enumeradas m as arriba. Adem as posee un sistema de plug-ins con los que se pueden a nadir nuevas funcionalidades. Por ejemplo, mediante un plug-in nos podemos conectar al sistema de control de versiones Subversion

1.3.2.

Descarga e instalaci on de Eclipse

Eclipse se puede descargar desde el sitio web http://www.eclipse.org. Existen versiones para las principales plataformas y sistemas operativos. Una particularidad de Eclipse es que no necesita instalaci on. Una vez descargado el chero comprimido, lo u nico que debemos hacer para empezar a utilizarlo es descomprimirlo en alg un directorio y seguidamente ejecutar el binario correspondiente a nuestra plataforma. La Figura 1.1 muestra la p agina de inicio de Eclipse. Los iconos que muestra esta pantalla son enlaces a sitios de informaci on sobre Eclipse. La pantalla de inicio se puede cerrar pulsando el aspa que se encuentra a la derecha de la leyenda Welcome.

1.3.3.

Congurando el aspecto de Eclipse: Perspectivas y Vistas

El interface gr aco de usuario de Eclipse cuenta con dos conceptos fundamentales: las Perspectivas y las Vistas. Una Perspectiva es un contenedor de Vistas. En una misma Perspectiva podemos agrupar m as de una Vista. Las Vistas por su lado, son componentes visual que pueden mostrar desde un editor de c odigo, hasta un arbol de jerarqu a de clases en Java. En la gura 1.2 se muestra el aspecto de Eclipse al mostrar la Perspectiva por defecto orientada al desarrollo de aplicaciones Java. Esta perspectiva contiene varias vistas, por ejemplo la vista Package Explorer donde se muestra informaci on de la conguraci on de nuestros proyectos. La vista Problems muestra un listado con los errores y avisos presentes en el c odigo de nuestro proyecto. Cada una de estas vistas est a orientada a presentar un tipo de informaci on de nuestro proyecto o tareas relacionadas con el. El aspecto de las perspectivas se puede congurar. Te habr as dado cuenta que cada una de estas vistas est a etiquetada con un nombre dentro de una solapa, si haces click sobre una de estas solapas sin soltar el bot on del rat on, puedes trasladar la vista a cualquier otra posici on dentro de la perspectiva. Esto te permite organizar las vistas dentro de las perspectivas seg un tus preferencias. Existe una gran cantidad de vistas, puedes acceder a cualquiera de ellas a trav es de la opci on Show view del men u Window.

1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE

17

Figura 1.1: Pantalla inicial de Eclipse

Figura 1.2: Perspectiva inicial orientada al desarrollo de proyectos Java2 SE.

18

CAP ITULO 1. INTRODUCCION

Figura 1.3: Ventana para la creaci on de un proyecto Java utilizando Eclipse.

Figura 1.4: Estructura m nima de un proyecto en Eclipse.

1.3.4.

El primer ejemplo

Vamos a crear un primer proyecto Java con Eclipse. Para ello, simplemente haz click con el bot on derecho en la vista Package Explorer, y sobre el men u emergente que aparecer a selecciona New Project, se abrir a una ventana como la mostrada en la Figura 1.3. En esta ventana lo u nico que vamos a introducir es el nombre del proyecto. Una vez introducido el nombre del proyecto, pulsa el bot on Finish, ver as que el aspecto de Eclipse se actualiza para mostrar el nuevo proyecto reci en creado. En la vista Package Explorer aparecer a el nombre del nuevo proyecto reci en creado. La vista de proyecto sigue una estructura de arbol, que puedes desplegar, el resultado se muestra en la Figura 1.4 El siguiente paso que vamos a dar es crear una nueva clase en nuestro proyecto. Esta clase va a ser muy sencilla, y u nicamente nos va a servir para conocer cual es el procedimiento de creaci on, edici on, compilaci on y ejecuci on utilizando Eclipse. Para crear una nueva clase, haz click con el bot on derecho del rat on sobre el nombre del proyecto reci en creado, se abrir a un men u emergente, selecciona la opci on New Class, presta atenci on al icono que se dibuja a la izquierda de esta opci on, y ver as que ese mismo icono la encuentras en la barrar de herramientas en la parte superior de la ventana de Eclipse. Otro procedimiento, m as r apido, de crear una nueva clase en Eclipse es pulsar directamente

1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE

19

Figura 1.5: Creaci on de una nueva clase Java en Eclipse. ese icono en la barra de herramientas. Al seleccionar esta opci on, se abrir a la nueva ventana mostrada en la Figura 1.5. En esta ventana vamos a introducir tres piezas de informaci on: Un nombre de paquete en min usculas (en el Cap tulo 3 conocer as con detalle el signicado de los paquetes en Java. Un nombre de clase con la primera letra de cada palabra en may usculas y sin espacios entre ellas. Selecciona la opci on public static void main(String[] args) Esta tres piezas de informaci on aparecen en la Figura 1.5. Recuerda introducir el nombre del paquete y de la clase utilizando may usculas y min usculas tal y como se muestra en la Figura 1.5, en el Cap tulo 2 conoceremos algunas de estas convenciones de codicaci on Java. Finalmente pulsa el bot on Finish. Ver as que de nuevo se actualiza la estructura del proyecto, ahora podr as ver que se ha creado, bajo el nodo del arbol src un nuevo nodo con nombre hola y bajo el el nodo HolaMundo.java. Adem as, se ha abierto una nueva vista del editor de c odigo tal y como muestra la Figura 1.6. Hagamos una peque na modicaci on sobre este c odigo. A nade la siguiente l nea tal y como se muestra en la Figura 1.7. Esta instrucci on sirve para mostrar una cadena de texto por consola. Una vez escrita la nueva l nea de c odigo graba el chero, para ello pulsa la combinaci on de teclas Ctrl + S. El siguiente paso va a ser ejecutar el programa, para ello haz click con el bot on derecho del rat on sobre el editor de c odigo y en el men u emergente que aparecer a selecciona la opci on Run As Java Application.

20

CAP ITULO 1. INTRODUCCION

Figura 1.6: Aspecto del editor de c odigo Java.

Figura 1.7: El primer programa en Java Hola mundo.

1.4. HERRAMIENTAS DE DESARROLLO

21

Figura 1.8: Resultado de le ejecuci on del primer programa en Java Hola mundo. En la vista Console podr as ver el resultado de la ejecuci on del programa, tal y como muestra la Figura 1.8 Por defecto, cada vez que haces modicaciones en el c odigo de denici on de una clase y grabas el chero, Eclipse compila autom aticamente el c odigo modicado. De este modo la compilaci on se realiza de modo transparente a medida que vamos trabajando en nuestro proyecto. Con estos sencillos pasos hemos creado nuestro primer proyecto en Java, y una u nica clase en la que hemos introducido una l nea de c odigo que muestra un mensaje de texto por consola. El trabajo con Eclipse es realmente sencillo.

1.4.

Herramientas de desarrollo

Cuando nos planteamos desarrollar un proyecto inform atico ser a de gran ayuda elegir una serie de herramientas que nos faciliten actividades tales como el control de versiones de nuestro c odigo o la prueba autom atica de nuestros m etodos. En el estado actual de madurez de las tecnolog as de desarrollo de proyectos inform aticos, resulta impensable iniciar el desarrollo de un proyecto sin planicar el control de versiones, la gesti on y seguimiento de errores, las herramientas de despliegue de la aplicaci on, y un largo etc etera. Afortunadamente, en la actualidad, contamos con excelentes herramientas de software libre que cubren este tipo de tareas. E incluso, en algunos casos, existe m as de una soluci on de software libre, con lo que podemos evaluar varias de ellas y seleccionar la que mejor se adapte a nuestra forma de trabajar antes de empezar a utilizar. En este libro vamos a presentar algunas de estar herramientas. Las hemos elegido porque, a trav es de nuestra propia experiencia, nos han parecido las m as adecuadas a nuestro caso, pero como lector no debes seguir ciegamente nuestra elecci on. Mejor a un, t omala como punto de partida que le permita evaluar otras alternativas. La r apida evoluci on de la tecnolog a inform atica permite vaticinar que seguir an apareciendo cada vez m as y mejores alternativas. En particular vamos a presentar las siguientes herramientas: Subvesion Es una herramienta para la gesti on de versiones. JUnit Es un framework de pruebas autom aticas de c odigo. Ant Es una herramienta de construcci on de proyectos. MyLyn Es una herramienta de gesti on de tareas. Bugzilla Es una herramienta de gesti on y seguimiento de errores. Cada una de las anteriores herramientas cuentan con una gran popularidad dentro de la comunidad de desarrollo de proyectos inform aticos. Todas ellas

22

CAP ITULO 1. INTRODUCCION

cuentan con otras excelentes alternativas. Todas ellas se pueden utilizar en proyectos que utilicen un lenguaje de programaci on alternativo a Java, o existen versiones de ellas para otros lenguajes de programaci on.

1.4.1.

A nadiendo nueva funcionalidad a Eclipse: los plugins

Afortunadamente, desde Eclipse se puede interaccionar con todas las herramientas expuestas en la secci on anterior. Eclipse cuenta con un sistema de plug-ins de tal modo que podemos aumentar 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 odigo sin necesidad de abandonar Eclipse. En el Cap tulo 4 se mostrar a c omo instalar el plug-in para Eclipse y c omo trabajar con el.

Lecturas recomendadas
Un escueto resumen sobre lo que signica el lenguaje de programaci on Java se puede encontrar en [11]. Una referencia completa sobre el entorno de desarrollo Eclipse se puede encontrar en la p agina web http://www.eclipse.org.

Cap tulo 2

Clases en Java
Contenidos
2.1. Denici on de una clase . . . . . . . . . . . . . . . . 2.2. Miembros de una clase . . . . . . . . . . . . . . . . 2.2.1. Atributos de una clase . . . . . . . . . . . . . . . . . 2.2.2. M etodos de una clase. . . . . . . . . . . . . . . . . . 2.2.3. Constructores. . . . . . . . . . . . . . . . . . . . . . 2.2.4. Sobrecarga de m etodos y constructores . . . . . . . . 2.3. Tipos de datos en Java. . . . . . . . . . . . . . . . . 2.3.1. Arrays de datos en Java. . . . . . . . . . . . . . . . . 2.4. Estructuras de control. . . . . . . . . . . . . . . . . 2.4.1. Estructuras de control de repetici on. . . . . . . . . . 2.4.1.1. El bucle for . . . . . . . . . . . . . . . . . 2.4.1.2. El bucle while . . . . . . . . . . . . . . . . 2.4.1.3. El bucle do...while . . . . . . . . . . . . . 2.4.2. Estructuras de control de selecci on. . . . . . . . . . . 2.4.2.1. Bifurcaciones con la sentencia if...else. . 2.4.2.2. M ultiples caminos con la sentencia switch 2.5. Modicadores de acceso. . . . . . . . . . . . . . . . 2.6. Modicadores static y final. . . . . . . . . . . . . 2.7. El recolector de basura. . . . . . . . . . . . . . . . . 2.8. Finalizaci on. . . . . . . . . . . . . . . . . . . . . . . 2.9. Comentarios. Comentarios de documentaci on. . . 24 25 25 26 28 32 33 34 36 37 37 38 38 39 39 39 40 42 43 44 45

Introducci on
Las clases son la piedra angular de los lenguaje de programaci on orientados a objetos (POO). Las clases son abstracciones de entidades de la realidad que sirven como plantillas para la creaci on de ejemplares de la clase. A estos ejemplares en POO se les llama objetos o instancias de la clase. El proceso de abstracci on depende del contexto en el que se utilizar an los ejemplares, es decir, no es lo mismo abstraer la entidad del mundo real ((Persona)) para utilizarla en una aplicaci on 23

24

CAP ITULO 2. CLASES

de gesti on de los clientes de una cl nica, que para utilizarla en una aplicaci on de seguros o de banca. En general, las caracter sticas de la entidad real que nos interese utilizar en la aplicaci on y las operaciones que podamos realizar sobre estas abstracciones ser an diferentes. Java es un lenguaje de programaci on orientado a objetos, y aunque como veremos posee tipos b asicos para poder manejar enteros o caracteres, todo en Java es un objeto. De hecho en Java existen dos grandes tipos de datos: tipos de datos primitivos y tipos de datos referencia. En este cap tulo vamos a ver c omo, a partir de una abstracci on, podemos transcribirla a c odigo Java para crear una clase, y c omo a partir de una clase podemos crear instancias de esa clase. Finalmente avanzaremos la idea de reutilizaci on de una clase. En POO la reutilizaci on implica escribir una nueva clase sin partir de cero, sino tomando como base otra clase cuyo comportamiento ampliamos o modicamos. Los detalles de c omo ampliar o extender el comportamiento de una clase base se introducir an en el Cap tulo 3.

2.1.

Denici on de una clase

Supongamos que queremos programar una aplicaci on de agenda telef onica. El objetivo de nuestra agenda telef onica es gestionar una serie de contactos. Cada uno de estos contactos representa a una Persona. Dicho de otro modo cada uno de los contactos de la agenda est a creado a partir de la misma plantilla Persona, que es la abstracci on de una persona del mundo real en el contexto de la aplicaci on de la agenda telef onica. Qu e necesitamos especicar para crear un objeto o ejemplar de la clase Persona? Cada uno de los objetos creados a partir de esta clase contendr a una serie de valores que lo identican, como el nombre y los apellidos del contacto y su n umero de tel efono. El conjunto de todos los valores de un objeto va a determinar su estado en un momento concreto. Por otro lado, sobre cada uno de los objetos vamos a poder llevar a cabo un conjunto de operaciones denidas en la clase. Volviendo al ejemplo de la agenda telef onica, cada una de las ((Persona)) de la agenda va a tener una serie de datos de inter es, que pueden o no variar a lo largo del tiempo (un contacto de mi agenda puede cambiar de n umero de tel efono, pero no es probable que cambie de apellidos), y me va a ofrecer una serie de operaciones que puedo realizar sobre ella, como por ejemplo consultar su nombre.

Denici on Al conjunto de valores denidos en la clase se le llama atributos de la clase. Al conjunto de operaciones que dene una clase se le llama m etodos de la clase. Cuando hablamos de miembros de una clase hacemos referencia tanto a los atributos como a los m etodos de la clase. La denici on de una clase en Java empieza con la palabra reservada class, y el conjunto de atributos y m etodos de la clase se dene en un bloque delimitado por llaves, del siguiente modo

2.2. MIEMBROS DE UNA CLASE

25

1 2 3 4

c l a s s Persona { // D e c l a r a c i o n de a t r i b u t o s // D e f i n i c i o n de m e todos }

2.2.
2.2.1.

Miembros de una clase


Atributos de una clase

Ahora que ya sabemos que debemos abstraer una ((Persona)) del mundo real en el contexto de nuestra aplicaci on la siguiente pregunta es: Cuales son las caracter sticas, o datos, de una persona relevantes en el contexto de una agenda telef onica? Sin duda uno de estos datos es el n umero de tel efono de la persona; cada contacto de mi agenda tiene, de manera simplicada, un n umero de tel efono. Qu e otros datos pueden ser de inter es almacenar en una agenda telef onica?, parece evidente que, al menos, el nombre y los apellidos de cada uno de los contactos. Representemos gr acamente lo que tenemos hasta ahora   Persona posee: Nombre; Apellidos; Tel efono;   C omo se denen estos atributos en la clase? De cada uno de los atributos debemos especicar 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 umero entero o una cadena de caracteres; si queremos almacenar los n umeros de tel efono en formato internacional (Ej: (+34) 555 555 555) optaremos por representarlos como cadenas de caracteres. Los atributos los declararemos de este modo:
1 2 3 4 5 6

c l a s s Persona { S t r i n g nombre ; String apellidos ; String telefono ; // D e f i n i c i o n de m e todos }

F jate que, al escribir el nombre de la clase hemos empezado la palabra por una letra may uscula, y que al empezar el nombre de un atributo lo hemos empezado por min uscula. Esta es una convenci on de codicaci on en Java que conviene seguir puesto que est a ampliamente extendida entre los desarrolladores Java. Veremos m as reglas de convenci on en la Secci on 2.9. F jate tambi en que hemos denido cada atributo en un l nea distinta y que cada l nea acaba con el caracter ;.

26 Reglas de convenci on

CAP ITULO 2. CLASES

Seg un las reglas de convenci on m as extendidas en Java, al denir una clase, el nombre de la clase se debe escribir con la primera letra en may uscula y los nombres de los atributos y m etodos deben empezar por una letra en min uscula. Si estos nombres est an formados por m as de una palabra, la segunda y siguientes palabras que constituyen el nombre se escriben con su primera letra en may uscula. Por ejemplo: numeroTelefono. Veamos ahora c omo denir las operaciones que podremos realizar sobre las instancias de la clase Persona.

2.2.2.

M etodos de una clase.

Una vez hemos creado una instancia de la clase Persona, C omo podemos recuperar a partir de ella su nombre?, C omo podemos recuperar el nombre que almacenamos en un contacto de nuestra agenda?. Una posibilidad es simplemente leer el valor del atributo, pero como veremos en la secci on 2.5 el acceso directo a los atributos de una clase est a desaconsejado. La respuesta es: a trav es de una llamada a un m etodo que devuelva el nombre del contacto. En el caso de la recuperaci on del nombre, el tipo de dato de retorno es una cadena class String. Un m etodo que cumple este objetivo es el siguiente:
1 2 3

String getPersona () { return nombre ; }

Sintaxis La sintaxis de declaraci on de un m etodo es: {modicadores} tipoRetorno nombre(tipo argumento1, tipo argumento2, ...) { Bloque de denici on del m etodo; } En estas pocas l neas de c odigo hay varias novedades, ve amoslas: 1. Un m etodo tiene un nombre que lo identica, en este caso getNombre. 2. Delante del nombre del m etodo escribimos el tipo del valor de retorno, en nuestro caso, como lo que el m etodo devuelve es el nombre cuyo tipo es un String, este ser a el tipo del valor retorno. 3. Detr as del nombre del m etodo aparecen unos par entesis sin nada en su interior. Dentro de los par entesis se dene la lista de argumentos del m etodo. Si est as familiarizado con las matem aticas, los argumentos de los m etodos tienen el mismo signicado que los argumentos de las funciones matem aticas, por ejemplo seno(45o ) signica que queremos utilizar el c alculo del seno sobre el argumento 45 grados. En nuestro caso la lista est a vac a, lo que indica que no necesito especicar ning un argumento para poder hacer uso del m etodo.

2.2. MIEMBROS DE UNA CLASE 4. La denici on del m etodo va dentro de las llaves . 5. Para devolver un valor utilizamos la palabra reservada return.

27

Como ves, muchos conceptos nuevos en tan s olo tres l neas de c odigo. Y una nueva convenci on de codicaci on, si un m etodo devuelve el valor de un atributo empieza por la palabra inglesa get, de ah que hayamos escrito getNombre(). Con lo que ya hemos visto, es sencillo escribir dos nuevos m etodo que devuelvan los apellidos y el n umero de tel efono de una Persona. Aqu tienes el c odigo de la clase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

c l a s s Persona { S t r i n g nombre ; String apellidos ; String telefono ; String getPersona () { return nombre ; } String getApellidos () { return a p e l l i d o s ; } String getTelefono () { return t e l e f o n o ; } }

Listado 2.1: C odigo de la clase Persona De nuevo, f jate que si un m etodo no recibe argumentos su lista de argumentos est a vac a. Pero si un m etodo no devuelve ning un par ametro, hay que indicarlo expl citamente utilizando la palabra reservada void. Por ejemplo, el siguiente m etodo no devuelve ning un valor:
1 2 3

void nada ( ) { // C o digo d e l m e todo }

En muchas ocasiones resulta interesante poder modicar el valor de los atributos. Como ya hemos comentado anteriormente, un contacto de mi agenda podr a cambiar de n umero de tel efono, luego parece buena idea que la clase Persona me proporcione un m etodo que permita modicar el valor del atributo telefono, como el que se muestra en el siguiente ejemplo:
1 2 3

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 ) { t e l e f o n o = nuevoTelefono ; }

Listado 2.2: M etodo para modicar el valor del tel efono. De nuevo, hemos seguido una convenci on de codicaci on: Regla de convenci on Los m etodos que modican el valor de los atributos de una clase se nombran empezando con la palabra inglesa set seguida por el nombre del atributo, cuya primera letra se escribe en may usculas.

28

CAP ITULO 2. CLASES

De modo an alogo, podemos a nadir a la clase Persona dos nuevos m etodos para poder modicar el valor de los atributos nombre y apellidos, tal y como se muestra a continuaci on:
1 2 3 4 5 6 7

void setNombre ( S t r i n g nuevoNombre ) { nombre = nuevoNombre ; } 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 ) { apellidos = nuevosApellidos ; }

Listado 2.3: M etodos para modicar el nombre y los apellidos de una Persona Ya tenemos escritos m etodos que nos permiten leer y modicar los atributos de la clase Persona. Ahora queremos crear ejemplares de esta clase, para ello necesitamos escribir m etodos especiales que nos sirvan para crear instancias de la clase, a estos m etodos especiales se les llama constructores de la clase.

2.2.3.

Constructores.

Para crear un ejemplar de una clase utilizamos m etodos especiales llamados constructores de la clase. En las siguientes l neas de c odigo se muestra c omo se dene un constructor de la clase Persona:
1 2 3 4 5

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 , t h i s . nombre = nombre ; this . a p e l l i d o s = a p e l l i d o s ; this . t e l e f o n o = t e l e f o n o ; }

String

telefono ) {

Listado 2.4: Constructor con par ametros de la clase Persona Volvemos a tener nuevos conceptos en estas l neas de c odigo, ve amoslo: 1. Un constructor es un m etodo cuyo nombre coincide con el de la clase, en nuestro caso el nombre del m etodo es Persona que es precisamente el nombre de la clase. 2. Como cualquier otro m etodo, tiene un lista de argumentos que en este caso no est a vac a, si no que indica que va a recibir tres argumentos y los 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 en se llama nombre y es de tipo String. C omo resolvemos la ambig uedad entre el nombre del atributo y el nombre del argumento?, utilizando la palabra reservada this; si escribimos this.nombre estamos haciendo referencia al atributo, si s olo escribimos nombre, estamos haciendo referencia al argumento del m etodo. Veremos con m as detalle el signicado de la palabra reservada this en la secci on 2.3. 4. Un constructor no devuelve ning un valor de retorno, ya que estos m etodos especiales nos sirven para crear objetos.

2.2. MIEMBROS DE UNA CLASE Escribamos otro constructor para la clase Persona:
1

29

Persona ( ) { }

M as novedades conceptuales en estas l neas de c odigo: 1. La lista de argumentos de este constructor est a vac a. 2. No hemos escrito ninguna l nea de c odigo entre las llaves. A este constructor tan particular se le llama Constructor por defecto y hablaremos m as sobre el en el cap tulo 3 dedicado a la herencia en Java. De momento qu edate con la idea de que es importante que tus clases denan el constructor por defecto, de hecho, todas tus clases deber an denirlo. Si tu clase no proporciona ning un constructor, como en el caso del Listado 2.5, el compilador de Java crea el constructor por defecto para la clase, de modo que puedas crear instancias a partir de ella.
1 2 3 4 5 6 7

class SinConstructores { private i n t a ; i n t getA ( ) { return a ; } }

Listado 2.5: Una clase sin ning un constructor. El compilador de Java crear a el constructor por defecto por nosotros. Veamos todo el c odigo que hemos escrito para la clase Persona:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

package agenda ; public c l a s s P e r s o n a { S t r i n g nombre ; String apellidos ; String telefono ; Persona ( ) { } 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 , t h i s . nombre = nombre ; this . a p e l l i d o s = a p e l l i d o s ; this . t e l e f o n o = t e l e f o n o ; } S t r i n g getNombre ( ) { return nombre ; } String getApellidos () { return a p e l l i d o s ; } String getTelefono () { return t e l e f o n o ; } } String telefono ) {

Listado 2.6: C odigo de la clase Persona En el Listado 2.6 hemos agrupado los m etodos get/set para cada uno de los atributos, adem as hemos modicado la denici on de los m etodos set para

30

CAP ITULO 2. CLASES

deshacer la ambig uedad entre el nombre de los atributos y de los argumentos, tal y como hemos hecho en el caso del constructor con argumentos. Antes de pasar adelante, escribamos nuestra primera peque na aplicaci on en 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 on. Hay varias opciones para crear un nuevo proyecto en Eclipse, a trav es del men u puedes elegir File New Java Project, o bien puedes pulsar el bot on de creaci on de proyectos. Eclipse te solicitar a un nombre para el proyecto, introduce uno adecuado (por ejemplo ((AgendaTelefonica))), y ya puedes pulsar directamente la tecla Finish. Ver as que en la columna izquierda de Eclipse, donde se muestra la vista Package Explorer te aparece una carpeta con el mismo nombre que el proyecto reci en creado. Eclipse organiza los proyectos en carpetas, el c odigo de tu proyecto, cheros de bibliotecas y recursos necesarios estar an en la carpeta del proyecto. Para crear un nueva clase en Eclipse puedes hacerlo a trav es del men u File New Class, o bien pulsando directamente el bot on de creaci on de una nueva clase. Se abrir a una ventana de di alogo solicit andote un nombre para la nueva clase y el paquete donde se incluir a. Es muy recomendable que cada clase est e dentro de un paquete (veremos con m as detalle el signicado de los paquetes en Java en la Secci on 3.6). Seg un las convenciones de Java, los nombres de paquetes se escriben en min uscula. Escribe, por ejemplo, para el nombre del paquete agenda, y para el nombre de la clase Persona. Ver as que se abre la vista del editor de c odigo en Eclipse y que si despliegas la carpeta de proyecto te aparece el chero de clase Persona.java. Escribe la denici on de la clase seg un el Listado 2.6. 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 amala PruebaPersona y como nombre de paquete introduce agenda, y en el cuadro de di alogo de creaci on de la clase marca la casilla public static void main(String[] args), con ello Eclipse crear a de manera autom atica el m etodo principal main. Escribe el resto de c odigo que aparece en el Listado 2.7.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

package agenda ; public c l a s s PruebaPersona { / @param a r g s / public s t a t i c void main ( S t r i n g [ ] a r g s ) { // TODO Autog e n e r a t e d method s t u b P e r s o n a unaPersona = new P e r s o n a ( " Oscar " , " Belmonte " , " 1234 " ) ; 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 on accediendo directamente a los campos ." ) ; System . o u t . p r i n t l n ( " N o m b r e : " + unaPersona . nombre ) ; 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 ) ; 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 ) ; 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 on llamando a los m e t o d o s de la clase ." ) ; System . o u t . p r i n t l n ( " N o m b r e : " + unaPersona . getNombre ( ) ) ; 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 ( ) ) ; 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 ( ) ) ; } }

2.2. MIEMBROS DE UNA CLASE

31

Listado 2.7: C odigo de la clase Principal La clase Principal est a repleta de novedades. Esta clase tiene un u nico m etodo public static void main(String[] args), este m etodo es el punto de entrada a la ejecuci on de un programa Java. En las siguientes secciones veremos el signicado de todos los modicadores que tiene este m etodo delante de su nombre que es main. En la l nea n umero 10, vemos c omo se usa el operador new para crear una instancia de la clase, escribimos tras new un constructor de la clase, en este caso Persona(" Oscar", "Belmonte", "1234"),new utilizar a el constructor con tres argumentos de la clase Persona para crear una nueva instancia. F jate que a la izquierda de new tenemos Persona unaPersona =, esto indica que nos guardamos lo que el operador new devuelve en la variable de tipo referencia a Persona que llamamos unaPersona, en las siguientes secciones veremos con m as detalle qu e signica el concepto variable de tipo referencia, de momento la idea es que, para poder usar la instancia a la Persona reci en creada utilizaremos la variable de referencia unaPersona.

Reglas de convenci on Los nombre de los paquetes y subpaquetes se escriben en min usculas. En las l neas 12-14 recuperamos la informaci on a partir de la variable de tipo referencia a Persona accediendo directamente a sus atributos (nombre, apellidos, telefono); mientras que en las l neas 17-19 accedemos a la misma informaci on haciendo uso de los m etodos denidos en la clase (getNombre(), getApellidos(), getTelefono()). Finalmente, para mostrar informaci on en forma de texto por consola utilizamos System.out.println("Texto"). Ejecutemos este primer programa para ver cual es el resultado, para ello haz click con el bot on derecho sobre el nombre de la clase Principal.java que tienes en la columna de la derecha en Eclipse (Package Explorer ) y en el men u emergente que te aparecer a selecciona Run as Java Application ; en la parte inferior de Eclipse se abrir a una nueva solapa (Console ) donde se mostrar a el resultado de la ejecuci on que debe ser: Muestra informaci on accediendo directamente a los campos. Nombre: Oscar Apellidos:Belmonte Tel efono: 1234 Muestra informaci on llamando a los m etodos de la clase. Nombre: Oscar Apellidos:Belmonte Tel efono: 1234 Como has comprobado, el trabajo de edici on en Eclipse es realmente sencillo y te ir as dando cuenta que este entorno de programaci on Java es muy potente.

32

CAP ITULO 2. CLASES

Pregunta En el ejemplo anterior estamos recuperando la informaci on almacenada en una instancia de la clase Persona de dos modos: accediendo directamente a sus atributos, o llamando a los m etodos de la clase. Qu e sentido tiene declarar m etodos de acceso a los atributos de una clase si puedo acceder directamente a ellos?.

2.2.4.

Sobrecarga de m etodos y constructores

Dos o m as m etodos pueden tener el mismo nombre siempre que su n umero de argumentos sea distinto. En caso de que los dos m etodos tengan el mismo n umero de argumentos, ser an distintos si al menos un tipo de sus argumentos es distinto. Por ejemplo en el siguiente Listado los dos m etodos unMetodo est an sobrecargados y son distintos.
1 2 3 4 5 6 7

p u b l i c v o i d unMetodo ( i n t e n t e r o ) { // D e f i n i c i o n d e l m e todo } p u b l i c v o i d unMetodo ( f l o a t r e a l ) { // D e f i n i c i o n d e l m e todo }

De modo an alogo, los constructores tambi en pueden estar sobrecargados, 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 etodos es que el tipo de retorno no sirve para distinguir dos m etodos. Si dos m etodos tienen el mismo n umero de argumentos y sus tipos son los mismos, no los podremos sobrecargar haciendo que el tipo de sus valores de retorno sean distintos. Denici on El nombre de un m etodo junto con su lista de argumentos forman la signatura del m etodo. El tipo del valor de retorno no forma parte de la signatura de un m etodo. En el siguiente Listado se muestra un error al intentar sobrecargar dos m etodos que se distinguen u nicamente por su tipo de retorno.
1 2 3 4 5 6 7 8 9

// ESTE LISTADO CONTIENE UN ERROR. // LOS METODOS NO SE PUEDEN SOBRECARGAR POR EL TIPO DE RETORNO. p u b l i c v o i d unMetodo ( ) { // D e f i n i c i o n d e l m e todo } p u b l i c i n t unMetodo ( ) { // D e f i n i c i o n d e l m e todo }

Los dos m etodos tienen el mismo nombre y ning un argumento, el primero de ellos no retorna nada void, y el segundo de ellos retorna un int. El compilador es

2.3. TIPOS DE DATOS EN JAVA. Tipo boolean char byte short int long oat double Tama no(bits) 1 16 8 16 32 64 32 64

33

Denici on true o false Car acter Unicode Entero en complemento a dos con signo Entero en complemento a dos con signo Entero en complemento a dos con signo Entero en complemento a dos con signo Real en punto otante seg un la norma IEEE 754 Real en punto otante seg un la norma IEEE 754

Tabla 2.1: Tipos de datos primitivos en Java y sus tama nos en memoria.

incapaz de distinguirlos y devuelve un error que indica que estamos intentando denir el mismo m etodo dos veces. Pero volvamos a Java y vemos qu e signica el t ermino tipo de dato referencia.

2.3.

Tipos de datos en Java.

En Java existen dos grandes grupos de tipos de datos, los tipos de datos primitivos y los tipos de datos referencia. Los tipos de datos primitivos sirven para representar tipos de datos tales como n umeros enteros, caracteres, n umeros reales, booleanos, etc etera. Se les llama primitivos porque nos permiten manejar elementos de informaci on b asicos como letras y n umeros. Una variable de tipo primitivo nos permite almacenar en ella un tipo primitivo como por ejemplo un valor num erico. Por otro lado, los tipos de datos referencia nos permiten indicar que vamos a trabajar con instancias de clases, no con tipos primitivos. Una variable de tipo referencia establece una conexi on hacia un objeto, y a trav es de esta conexi on podremos acceder a sus atributos y m etodos. Cuando hablamos de variables, es muy importante asimilar la diferencia entre variables de tipo primitivo y variables de tipo referencia. En una variable de tipo primitivo podemos almacenar valores de tipo primitivo (n umeros, caracteres); pero el las variables de tipo referencia no almacenamos valores son la puerta de entrada hacia los objetos. Son los objetos, las instancias de clases, las que almacenan informaci on y me permiten trabajar con ellos a trav es de llamadas a sus m etodos.

Concepto Las variables de tipo primitivo nos permiten almacenar valores de tipo primitivo como n umeros y caracteres. Las variables de tipo referencia no almacenan valores, sino que nos permiten acceder a los atributos y m etodos de los objetos. En Java, el tama no en memoria de los tipos de datos primitivos est a estandarizado. Los tipos de datos primitivos y sus tama nos son los que aparecen en la Tabla 2.1.

34

CAP ITULO 2. CLASES

Sintaxis Las 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 umero Como ya hemos visto, las referencias en Java son la puerta de entrada a los objetos, las referencias me permiten acceder a los atributos y m etodos de los objetos, el tipo de una referencia debe ser compatible con el tipo del objeto al que se reere. En el cap tulo 3 dedicado a la herencia veremos qu e quiere decir ((compatible)). Sintaxis Las variables de tipo referencia se declaran de este modo: tipoReferencia nombre [ = valor referencia inicial]; Ejemplo 1 : Persona persona; Ejemplo 2 : Persona persona = new Persona(" Oscar", "P erez", "123"); En m ultiples ocasiones, nos interesa trabajar con m as de un u nico valor de un determinado tipo, en vez de trabajar con una u nica Persona queremos trabajar con un grupo de personas. Veamos c omo podemos declarar conjuntos de elementos del mismo tipo en Java.

2.3.1.

Arrays de datos en Java.

Hasta el momento, hemos aprendido c omo declarar variables de tipos de datos primitivos y de tipos de datos referencia. Esto nos sirve para crear una u nica variable que contendr a bien un tipo de datos primitivo a una referencia a un objeto, pero a veces nos interesa poder manejar conjuntos de elementos del mismo tipo, por ejemplo, en alguna circunstancia nos puede interesar declarar una variable con la que poder acceder a un grupo de 10 enteros o 100 objetos de la clase Persona. En Java utilizaremos arrays de elementos cuando necesitemos manejar m as de un elemento del mismo tipo. Para declarar un array en Java utilizamos los corchetes seg un la siguiente sintaxis: Sintaxis Los arrays de tipos primitivos se declaran: Ejemplo 1 : int array[]; // Array declarado Ejemplo 2 : int arrayEnteros[] = new int[10]; // Array iniciado Los arrays de tipos referencia se declaran: Ejemplo 3 : Persona grupo[]; // Array declarado Ejemplo 4 : Persona grupo = new Persona[10]; // Array iniciado Aunque la sintaxis de la declaraci on de arrays de tipo primitivo y de tipo referencia es la misma, el resultado es radicalmente distinto en los dos casos.

2.3. TIPOS DE DATOS EN JAVA.

35

Analic emoslo. En el Ejemplo 2 del recuadro de sintaxis anterior se est a deniendo un array capaz de albergar 10 enteros (con ndice 0 para el primer elemento e ndice 9 para el u ltimo), dentro de cada una de las posiciones del array podemos almacenar un entero. En el caso del Ejemplo 4, estamos deniendo 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 un objeto de la clase Persona, s olo referencias a objetos de ese tipo. La diferencia entre arrays de tipo primitivo y tipo referencia es muy importante. 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 u nico que se ha creado 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 ejemplo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

package agenda ; public c l a s s A r r a y s { / @param a r g s / public s t a t i c void main ( S t r i n g [ ] a r g s ) { // TODO Autog e n e r a t e d method s t u b i n t a r r a y E n t e r o s [ ] = new i n t [ 1 0 ] ; 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 ] ; // 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 a 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 ] ) ; // 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 on [5] 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 on grupoPersonas [5]: " + g r u p o P e r s o n a s [ 5 ] . nombre ) ; } }

Listado 2.8: Diferencia entre arrays de tipos primitivos y arrays de tipos referencia Si creas una nueva clase con el c odigo del Listado 2.8 y lo ejecutas (recuerda: bot on derecho sobre el nombre de la clase en el Package Explorer, y luego Run as Java Applications ), obtendr as el siguiente error: Valor en arrayEnteros[5]: 0 Exception in thread "main" java.lang.NullPointerException at hola.Arrays.main(Arrays.java:15) En la posici on 5 del array de enteros tenemos un valor por defecto, pero la referencia que tenemos en la posici on 5 del array de tipo Persona es el valor por defecto null que en Java tiene el signicado de Referencia no asignada.

36 Sintaxis

CAP ITULO 2. CLASES

A los elementos de un array se accede mediante el operador []. Dentro de este operador indicamos la posici on del elemento a la que deseamos acceder. C omo podemos resolver el error anterior?. Simplemente asignando a la referencia en la posici on 5 del array grupoPersonas una referencia a un objeto que haya sido creado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

package agenda ; public c l a s s A r r a y s 2 { / @param a r g s / public s t a t i c void main ( S t r i n g [ ] a r g s ) { // TODO Autog e n e r a t e d method s t u b i n t a r r a y E n t e r o s [ ] = new i n t [ 1 0 ] ; 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 ] ; 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 " ) ; // 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 a 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 ] ) ; // 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 on [5] 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 on grupoPersonas [5]: " + g r u p o P e r s o n a s [ 5 ] . nombre ) ; } }

Si ejecutas el c odigo con la modicaci on obtendr as el siguiente resultado: Valor en arrayEnteros[5]: 0 Nombre en posici on grupoPersonas[5]: James En este u ltimo caso la referencia en la posici on 5 del array grupoPersonas s que hace referencia a un objeto, luego no hay problema al usarla para acceder a su atributo nombre. Ya sabemos c omo acceder a los elementos de un array, la pregunta que nos surge es C omo puedo recorrer todos los elementos de un array?. La respuesta es: ((Usando estructuras de control de repetici on))

2.4.

Estructuras de control.

Java es un lenguaje de programaci on estructurado, esto signica que Java proporciona estructuras de control para decidir el ujo de ejecuci on de nuestros programas. Existen dos grandes grupos de estructuras de control: Estructuras de control de repetici on: Nos permiten indicar si un determinado bloque de c odigo se debe ejecutar mas de una vez. Estructuras de control de selecci on: Nos permiten especicar mas de una direcci on de ujo dependiendo de alguna condici on.

2.4. ESTRUCTURAS DE CONTROL.

37

2.4.1.

Estructuras de control de repetici on.

En Java existen tres estructuras de control de repetici on: Bucle for. Bucle while. Bucle do...while. Las estructuras de repetici on sirven para repetir una determinada tarea mientras se cumpla cierta condici on. En el caso de un array nos sirven para recorrer los elementos almacenados en el array secuencialmente, para, por ejemplo, mostrar sus valores. Veamos como se usa cada una de estas estructuras de repetici on. 2.4.1.1. El bucle for

Si conocemos cual es el primer elementos y el u ltimo sobre los que queremos iterar el bucle for es la manera m as c omoda de recorrerlos todos. Su sintaxis es la siguiente:

Sintaxis La sintaxis del bucle for es: for(inicio; condici on: incremento) Ejemplo 1: for(int i = 0; i <10; i += 2) La variable ((i)) se declara en el bucle y s olo tiene existencia dentro del bucle, al salir del bucle desaparece la variable de control ((i)). Para el bucle for...each: for(Tipo variable: Colecci on) 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 on de parada es que el valor de la variable 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 versi on 5 de Java. En este caso utilizamos una variable que va recibiendo los valores de los elementos que hay dentro del conjunto de manera incremental, uno con cada iteraci on. El bucle for...each es especialmente u til cuando se itera sobre los elementos de una colecci on, tal y como veremos en el Cap tulo 8. Veamos un ejemplo con un poco m as de detalle:
1 2 3 4 5 6 7 8 9

package r e p e t i c i o n ; public c l a s s B u c l e F o r { public s t a t i c void main ( S t r i n g [ ] a r g s ) { // Declaramos e l a r r a y i n t a r r a y E n t e r o s [ ] = new i n t [ 5 ] ; // Almacenamos d a t o s en s u s e l e m e n t o s f o r ( i n t i = 0 ; i < 5 ; i ++)

38
10 11 12 13 14 15 16

CAP ITULO 2. CLASES


arrayEnteros [ i ] = i ; // 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 o n almacenada for ( int i : arrayEnteros ) 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 ] ) ;

} }

El resultado de la ejecuci on de este c odigo es el siguiente: arrayEnteros[0] arrayEnteros[1] arrayEnteros[2] arrayEnteros[3] arrayEnteros[4] = = = = = 0 1 2 3 4

El primer bubcle for itera sobre las posiciones del array almacenando los n umeros 0 a 4 en las posiciones 0 a 4 del array. El segundo bucle itera sobre las 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 on de parada se comprueba antes de cada iteraci on y, si la condici on se cumple, se ejecuta el bloque del bucle while. Sintaxis La sintaxis del bucle while es: while(condici on) { Bloque de c odigo } 2.4.1.3. El bucle do...while

En el caso del bucle do...while la condici on se comprueba despu es de haberse ejecutado al menos una vez el cuerpo del bucle. La condici on se comprueba al nal del bucle. Sintaxis La sintaxis del bucle do...while es: do { Bloque de c odigo } while(condici on); Estas tres estructuras de control de repetici on son intercambiables, se puede sustituir una por otra con peque nas modicaciones. Elegir una u otra depende de cada caso: si conocemos el intervalo sobre el que queremos iterar el bucle for es el mas comodo de utilizar; si la condici on de parada no involucra el valor de una posicion podemos utilizar el bucle while si necesitamos comprobar la condici on antes, o bien el bucle do...while si queremos ejecutar al menos una vez el bloque de c odigo que encierra el bucle.

2.4. ESTRUCTURAS DE CONTROL.

39

2.4.2.

Estructuras de control de selecci on.

Las estructuras de control de selecci on nos permiten especicar mas de un posible camino a seguir por el ujo de ejecuci on de nuestro programa. La direcci on nal que seguir a el ujo depender a de si se cumple o no cierta condici on. 2.4.2.1. Bifurcaciones con la sentencia if...else.

La sentencia if nos permite ejecutar un bloque de c odigo, o no, dependiendo de cierta condici on. La condici on debe evaluarse a un valor booleano, es decir, a true o false, como en el siguiente ejemplo de c odigo:
1 2 3

int entero ; i f ( e n t e r o % 2 == 0 ) System . o u t . p r i n t l n ( " E l n umero es par . " ) ; e l s e System . o u t . p r i n t l n ( " E l n umero es impar . " ) ;

Dentro del bloque de c odigo correspondiente al else podemos a nadir una nueva sentencia if, se dice entonces que las sentencias if est an encadenadas, como en el siguiente ejemplo:
1 2 3 4 5 6 7

i f ( primeraCondicion ) { \\ Bloque de c o digo } e l s e i f ( segundaCondicion ) { \\ Bloque de c o digo } else { \\ Bloque de c o digo }

Esto nos permite especicar m as de un posible camino a seguir por el ujo de ejecuci on de nuestro c odigo. 2.4.2.2. M ultiples caminos con la sentencia switch

Existe una construcci on del lenguaje que nos permite especicar m ultiples caminos a seguir por el ujo de ejecuci on de nuestro c odigo: la sentencia switch. En este caso el camino a seguir se selecciona bas andose en el valor de una expresi on que se evalua a un valor entero, como en el siguiente ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

i n t mes = 1 ; // C o r r e s p o n d e a l mes de Enero s w i t h ( mes ) { case 1 : System . o u t . p r i n t l n ( " E l m e s e s E n e r o . " ) ; break ; case 2 : 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 . " ) ; break ; case 3 : System . o u t . p r i n t l n ( " E l m e s e s M a r z o . " ) ; break ; default : 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 . " ) ; break ; }

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 odigo correspondiente al case hasta que se encuentra la sentencia break en cuyo momento se avandona el bloque de la sentencia switch. Exite una

40

CAP ITULO 2. CLASES

opci on Por defecto etiquetada como default que es opcional y cuyo c odigo se ejecutar a si la expresi on entera no coincide con ninguno de los case anteriores. Es importante hacer notar que una vez que se encuentra una coincidencia entre la expresi on entera y un case, se ejecuta su c odigo correspondiente hasta encontrar la sentencia break. Esto nos permite obviar esta sentencia si queremos que varios case distintos ejecuten el mismo segmento de c odigo, como en el siguiente ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

i n t mes = 1 ; // C o r r e s p o n d e a l s w i t c h ( mes ) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: System . o u t . p r i n t l n ( El mes break ; case 4: case 6: case 9: case 11: System . o u t . p r i n t l n ( El mes break : default : System . o u t . p r i n t l n ( El mes break ; }

mes de Enero

t i e n e 31 d as .) ;

t i e n e 30 d as .) ;

es Febrero . ) ;

En el ejemplo anterior los meses cuyo ordinal es 1, 3, 5, 7, 8, 10 o 12 tienen 31 d as, todos los case correspondientes, excepto el de valor 12, no incluye la 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 a si el ordinal del mes es 4, 6, 9 u 11, en todos los casos se ejecutar a la sentencia de la l nea 16.

2.5.

Modicadores de acceso.

Ahora ya estamos en situaci on de volver a la pregunta: Qu e sentido tiene declarar m etodos de acceso a los atributos de una clase si puedo acceder directamente a ellos? La repuesta es que, como regla general, nunca debemos hacer visibles los atributos de nuestras clases, s olo deben ser visibles desde el interior de las clases. Como resultado, para acceder a los valores de los atributos utilizaremos m etodos. Esta regla es una manera de expresar el concepto de Encapsulaci on, una de las piezas centrales de la programaci on orientada a objetos.

Concepto Las clases encapsulan atributos y m etodos de tal modo que s olo se hace visible una parte de esos atributos y m etodos, los estrictamente necesarios para que podamos trabajar con las instancias de esa clase. La respuesta a la pregunta anterior hace surgir una nueva: C omo restrinjo la visibilidad de los atributos de una clase?, la respuesta es: mediante los Modicadores de acceso.

2.5. MODIFICADORES DE ACCESO.

41

Denici on Los modicadores de acceso son palabras reservadas de Java mediante las cuales restringimos la visibilidad de los atributos y m etodos de una clase. En Java un modicador de acceso est a representado por una palabra reservada que me permite denir la visibilidad de un atributo o un m etodo de la clase. Los cuatro modicadores de acceso que podemos utilizar en Java son: private. protected. ((Vac o)) (no escribimos nada). public. De momento, y hasta que veamos el cap tulo de herencia, vamos a ver el signicado de los dos m as sencillos: private y public. Los modicadores de acceso se escriben antes del tipo del atributo o antes del tipo de retorno del m etodo. Veamos c omo quedar a nuestra clase Persona asignando la visibilidad adecuada a cada uno miembros:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

package agenda ; public c l a s s P e r s o n a { S t r i n g nombre ; String apellidos ; String telefono ; Persona ( ) { } 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 , t h i s . nombre = nombre ; this . a p e l l i d o s = a p e l l i d o s ; this . t e l e f o n o = t e l e f o n o ; } S t r i n g getNombre ( ) { return nombre ; } String getApellidos () { return a p e l l i d o s ; } String getTelefono () { return t e l e f o n o ; } } String telefono ) {

En este caso estamos restringiendo la visibilidad de los atributos de la clase Persona de modo que u nicamente son visibles desde el interior de la propia clase donde se han denido (modicador private). Por otro lado, estamos haciendo visibles los m etodos de la clase a cualquier otra clase que los quiera utilizar (modicador public).

42

CAP ITULO 2. CLASES Buenas pr acticas y convenciones

En general, se considera una buena pr actica declarar los atributos de una clase como privados (private) y si necesitamos acceder a ellos para leer sus valores o modicarlos utilizaremos los m etodos get o set. En caso de que el tipo del valor devuelto sea boolean se utilizar a is en vez de set, por ejemplo isNuevo() en vez de getNuevo() si el valor que se retorna es un boolean (true o false). Adem as de los modicadores que nos permiten denir la visibilidad de atributos y m etodos de una clase, en Java existen otros modicadores que tambi en se pueden aplicar sobre la denici on de atributos y m etodos: static y final.

2.6.

Modicadores static y final.

Un atributo de una clase se puede modicar 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 e quiere decir esto?, pues que no existe una copia de ese atributo en cada uno de los objetos de la clase, si no que existe una u nica 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 es del nombre de la clase. De igual modo, podemos modicar los m etodos de una clase con la palabra reserva static. A estos m etodos se les llama m etodos de la clase, y, al igual que con los atributos static, podemos usarlos a trav es del nombre de la clase, sin necesidad de crear ninguna instancia de la clase. Pero existe una restricci on, los m etodos est aticos de una clase s olo pueden acceder a atributos est aticos u otros m etodos est aticos de la clase, pero nunca a atributos o m etodos que no lo sean. Ves porqu e? Qu e ocurrir a si desde un m etodo est atico y usando el nombre de la clase intentases acceder a un atributo de instancia de la clase? En el siguiente ejemplo de c odigo hemos a nadido un contador para saber el n umero de instancias de la clase Persona que se han creado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

package t i p o s ; public c l a s s P e r s o n a implements C o n t a c t o { private S t r i n g nombre ; private S t r i n g a p e l l i d o s ; private S t r i n g t e l e f o n o ; private s t a t i c i n t n I n s t a n c i a s ; public P e r s o n a ( ) { super ( ) ; iniciaAtributos () ; } public s t a t i c i n t g e t N I n s t a n c i a s ( ) { return n I n s t a n c i a s ; }

F jate que el m etodo getNInstancias() que accede al atributo nInstancias es est atico. En el siguiente ejemplo de c odigo se est a utilizando este m etodo est atico a trav es del nombre de la clase y a trav es de una instancia concreta:

2.7. EL RECOLECTOR DE BASURA.

43

1 2 3 4 5 6 7 8 9 10 11

public f i n a l c l a s s P r i n c i p a l { private P r i n c i p a l ( ) { super ( ) ; } private void e j e c u t a ( ) { P e r s o n a unaPersona = new P e r s o n a ( ) ; // Accedemos a l m e todo a t r a v e s de l a c l a s e System . o u t . p r i n t l n ( " N u mero de p e r s o n a s c r e a d a s : " + Persona . getNInstancias () ) ; 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 " ) ; // Accedemos a l m e todo a t r a v e s de una i n s t a n c i a c o n c r e t a

Cuando un atributo de una clase los modicamos en su denici on con la 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 = " Oscar " ;

Una vez denido, este atributo no puede cambiar de valor, si lo intent asemos cambiar el compilador nos dar a un error. Muchas veces los modicadores static y final se utilizan en combinaci on para denir constantes, como en el siguiente ejemplo:
1 2 3 4

public c l a s s C o n s t a n t e s { public s t a t i c f i n a l double PI = 3 . 1 4 1 5 9 2 ; ... }

De este modo, la constante es accesible desde cualquier otra clase (al ser public) y podemos leerla a trav es del nombre de la clase de este modo Constantes.PI, pero si por descuido intentamos modicarla, el compilador de Java nos dar a un error. Regla de convenci on Los nombre de las constantes se escriben en may usculas. El modicador final tambi en se puede usar sobre un m etodo o sobre la clase. Veremos con detalle lo que esto signica 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 omo liberamos el espacio en memoria que est a ocupando? En Java no existe ning un operador especial para eliminar de la memoria las instancias que no vamos a seguir utilizando. Para liberar la memoria existe un mecanismo mucho m as potente, el Recolector de basura. Como ya sabes, el modo de acceder a los objetos en Java es mediante las variables 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 instancia ha perdido todas las referencias que apuntaban a ella. Si un objeto pierde

44

CAP ITULO 2. CLASES

todas la referencias que apuntan a el y las referencias son el u nico mecanismo que tenemos de acceder a los objetos, signica 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 omo podemos marcar un objeto para que sea borrado de memoria? Una t ecnica sencilla es eliminar todas las referencias que apuntan a el como en el siguiente ejemplo:
1 2 3 4 5

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 a 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 dos 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 a 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 a // 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 on.

En la secci on anterior hemos visto cual es el mecanismo que utiliza Java para ir liberando de la memoria los objetos que ya no son accesibles. Todos los objetos poseen un m etodo con la siguiente signatura protected void finalize() throws Throwable, en los cap tulos siguientes veremos con detalle el signicado 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 u ltimo m etodo de cualquier objeto que se llama antes de que el recolector de basura elimine la instancia de la memoria. Dicho de otro modo, el m etodo finalize es la u ltima oportunidad que tenemos como programadores para que nuestras instancias acaben limpiamente. Veamos qu e quiere decir esto con m as detalle. Sup on que has escrito un clase que abre un chero para lectura (veremos acceso a cheros 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 a esta instancia de la memoria y el resultado colateral ser a que el chero quedar a abierto. Para que esto no ocurra, en el m etodo finalize podemos escribir el c odigo que cierre los cheros que est an abiertos, ya que sabemos que este m etodo ser a llamado antes de eliminar la instancia de memoria. Pero no es tan inmediato, el problema que conlleva delegar al m etodo 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 a, pero no sabemos cuando. Y aunque podemos ((forzar)) la actuaci on del recolector de basura de este modo:
1 2

Runtime r = Runtime . getRuntime ( ) ; 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:

2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACION. Buenas pr acticas

45

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 documentaci on.

Todos los programadores son conscientes de la importancia de documentar su trabajo. Una tarea de documentaci on es incluir comentarios en el propio c odigo para que otros programadores puedan conocer en el momento de la lectura de c odigo los detalles de implementaci on. Para realizar tareas de documentaci on Java nos proporciona tres tipos de comentarios: 1. Comentarios de una u nica l nea. 2. Comentarios de m as de una l nea. 3. Comentarios de documentaci on. Los comentarios de una u nica l nea empiezan con y el texto del comentario restringe su extensi on a una u nica l nea. Los comentarios de m as de una l nea empiezan con /*, el texto del comentario puede ocupar cuantas l neas necesitamos, pero es necesario indicar que el comentario acaba insertando al nal */. En el siguiente ejemplo puedes ver c omo se usan ambos tipos de comentario:
1 2 3 4 5 6 7

public c l a s s P e r s o n a { // 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 nea private S t r i n g nombre ; / E s t e c o m e n t a r i o ocupa m as de una l nea de c o d i g o / private S t r i n g a p e l l i d o s ;

Pero, sin duda, los comentarios de documentaci on son una herramienta realmente potente en Java. Los comentarios de documentaci on se incluyen en el c odigo y nos sirven para, a partir de ellos, crear documentaci on de nuestro c odigo en formato html. En los comentarios de documentaci on podemos a nadir etiquetas que nos permiten enriquecer la documentaci on generada. Veamos c omo se introducen los comentarios de documentaci on y las etiquetas que tenemos disponibles. Un comentario de documentaci on siempre debe empezar por /**, nota que tras la barra se escriben dos asteriscos, y debe acabar por */, como los comentarios de m as de una l nea. Dentro de los comentarios de documentaci on podemos utilizar etiquetas para a nadir informaci on que enriquezca la documentaci on generada. Por ejemplo, podemos utilizar la etiqueta para indicar quien es el autor del c odigo de una 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

46
2 3 4 5 6 7 8 9

CAP ITULO 2. CLASES


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 o en una agenda de t e l efonos @author Oscar Belmonte Fern a ndez @version 1.0

public c l a s s P e r s o n a { private S t r i n g nombre ;

otros comentarios de documentaci on: @version Indicamos la versi on del c odigo. @param nombre Descripci on del par ametro. @return Signicado del valor de retorno. @deprecated Raz on de por qu e este m etodo est a obsoleto. @see #metodo() Referencia cruzada al m etodo. @exception Excepci on que se puede producir en el m etodo @throws Excepci on no gestionada Adem as, en los comentarios de documentaci on podemos incluir c odigo HTML. En el listado 2.9 tienes la clase Persona documentada.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

package p e r s o n a . c o m e n t a r i o s ; / 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 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 o en una agenda de t e l efonos @author Oscar Belmonte Fern a ndez @version 1.0 / public c l a s s P e r s o n a { private S t r i n g nombre ; private S t r i n g a p e l l i d o s ; private S t r i n g t e l e f o n o ; private s t a t i c i n t n I n s t a n c i a s = 0 ; / C ons t ru ct or por d e f e c t o / public P e r s o n a ( ) { n I n s t a n c i a s ++; } / C o n s t r u c t o r con p a r a metros . 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 i n m u t a b l e s , no e x i s t i r a n m e todos p a r a c a m o b i a r l o s @param nombre Nombre d e l nuevo c o n t a c t o @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 o @param t e l e f o n o T e l e f o n o d e l nuevo c o n t a c t o / 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 ) { t h i s . nombre = nombre ; this . a p e l l i d o s = a p e l l i d o s ; this . t e l e f o n o = t e l e f o n o ; n I n s t a n c i a s ++; } / D e v u e l v e e l n u mero de i n s t a n c i a s c r e a d a s @ re t ur n El n u mero de i n s t a n c i a s

2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACION.


41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

47

/ public s t a t i c i n t g e t N I n s t a n c i a s ( ) { return n I n s t a n c i a s ; } / D e v u e l v e e l nombre d e l c o n t a c t o @re t ur n Nombre d e l c o n t a c t o / public S t r i n g getNombre ( ) { return nombre ; } / Devuelve l o s a p e l l i d o s d e l c o n t a c o t @re t ur n A p e l l i d o s d e l c o n t a c t o / public S t r i n g g e t A p e l l i d o s ( ) { return a p e l l i d o s ; } / D e v u e l v e e l n u mero de t e l efono del contacto @re t ur n N umero de t e l efono del contacto / public S t r i n g g e t T e l e f o n o ( ) { return t e l e f o n o ; } / Cambia e l nombre d e l c o n t a c t o @param nombre El nuevo nombre d e l c o n t a c t o @ d e p r e c a t e d E s t e m e 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 @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 ) / public void setNombre ( S t r i n g nombre ) { t h i s . nombre = nombre ; } / Cambia l o s a p e l l i d o s d e l c o n t a c t o @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 o @ d e p r e c a t e d E s t e m e 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 @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 ) / 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 ) { this . a p e l l i d o s = a p e l l i d o s ; } / Cambia e l n u mero de t e l efono del contacto @param t e l e f o n o El nuevo n u mero de t e l efono / 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 ) { this . t e l e f o n o = t e l e f o n o ; } }

del contacto

Listado 2.9: C odigo fuente de la clase Persona con comentarios de documentaci on. El paquete de desarrollo Java nos proporcionan una herramienta para generar la documentaci on de nuestras clases en formato HTML a partir del c odigo. Esta herramienta se llama javadoc. La generaci on de c odigo se puede realizar desde consola de este modo: javadoc Persona.java /ruta/a/directorio Si no se especica la ruta la documentaci on se generar a en el directorio donde se encuentre el chero Persona.java.

48

CAP ITULO 2. CLASES

Figura 2.1: javadoc.

Comentarios de documentaci on generados con la herramienta

Desde Eclipse tambi en podemos generar la documentaci on de nuestras clases haciendo click con el bot on derecho del rat on sobre el chero de la clase y despu es seleccionamos Export Java Javadoc. Se generar a toda la documentaci on de nuestra clase, o todo el proyecto si lo hemos seleccionado en vez de una clase individual. En la gura 2.1 se muestra cual es el aspecto de la documentaci on generada cuando se utiliza un navegador web para su visualizaci on.

Ejercicios.
1. Escribe una clase que abstraiga la idea de Empresa. Una empresa puede tener u nicamente como atributos su nombre y un tel efono de contacto. 2. A nade comentarios de documentaci on a la clase Empresa y genera la documentaci on de esta clase. 3. Escribe un sencillo programa para gestionar los contactos de una agenda telef onica (clase Agenda). Las operaciones b asicas que la agenda telef onica es capaz de realizar son:

2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACION. a ) Insertar nuevas Personas en la agenda. b ) Listar todas las Personas de la agenda. c ) Buscar una Persona a partir de su nombre.

49

Lecturas recomendadas.
El excelente libro de Arnold y otros [2] es una referencia completa a la denici on de clases. Comenta de modo exhaustivo todos los detalles en la denici on de una clase, los distintos modicadores de acceso y su signicado. El modo de presentar los conceptos del lenguaje Java ne la serie de libros de la colecci on Head rst Java es muy interesante. En particular, la referencia [3] presenta de una manera muy visual los principales conceptos en la denici on de clases en Java. Finalmente, una lectura siempre recomendable sobre buenas pr acticas y escritura de c odigo limpia es el libro de Robert C. Martin de la referencia [10].

50

CAP ITULO 2. CLASES

Cap tulo 3

Herencia e Interfaces
Contenidos
3.1. Herencia. . . . . . . . . . . . . . . . . . . . . . 3.2. Extensi on de una clase. . . . . . . . . . . . . . 3.2.1. Sobrescribir atributos. . . . . . . . . . . . . . 3.2.2. Sobrescribir m etodos. . . . . . . . . . . . . . 3.2.3. La palabra reservada super. . . . . . . . . . . 3.2.4. El constructor por defecto y la clase Object. 3.2.5. El operador instanceof. . . . . . . . . . . . . 3.2.6. El modicador final. . . . . . . . . . . . . . 3.2.7. M etodos static. . . . . . . . . . . . . . . . . 3.3. Clases abstractas. . . . . . . . . . . . . . . . . 3.4. Interfaces. . . . . . . . . . . . . . . . . . . . . . 3.5. Enumeraciones. . . . . . . . . . . . . . . . . . . 3.6. Paquetes en Java. . . . . . . . . . . . . . . . . 3.7. Clases e interface anidados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 52 54 56 59 59 60 61 62 63 65 68 69 71

. . . . . . .

Introducci on
En el cap tulo 2 hemos visto c omo podemos codicar la abstracci on de una entidad del mundo real a c odigo Java. Hemos visto la sintaxis de Java para la creaci on de clases, atributos y m etodos, adem as de algunos de los modicadores que podemos utilizar sobre ellos. Tambi en hemos aprendido a crean instancias a partir de una clase con el operador new. La situaci on en la que nos encontramos ahora es que nuestras clases abstraen entidades del mundo real pero, Qu e ocurre si quiero a nadir m as funcionalidad a mis clases?, C omo puedo a nadir nuevos atributos a una clase ya existente?, C omo puedo ampliar el conjunto de m etodos que proporciona? En POO existe un mecanismo fundamental para a nadir funcionalidad a una clase el concepto es la Herencia, tambi en conocido como Extensi on o Derivaci on. La idea b asica de la Herencia es crear una nueva clase a partir de la denici on de otra clase ya existente. La Herencia nos permite construir una clase a nadien51

52

CAP ITULO 3. HERENCIA E INTERFACES

do u nicamente la nueva funcionalidad a otra clase ya existente. Y tambi en nos permite modicar el comportamiento de la clase original sobrescribiendo sus m etodos.

3.1.

Herencia.

En esta secci on vamos a ver c omo se puede ampliar el comportamiento de una clase a trav es de la herencia. Veremos tambi en el concepto, muy importante, de la vinculaci on din amica para encontrar qu e m etodo se ha de invocar al utilizar referencias a clases extendidas, as como el uso del operador instanceof. Finalmente veremos el signicado de los modicadores nal y abstract cuando se aplican a la denici on de un m etodo o una clase.

3.2.

Extensi on de una clase.

Lo primero que hay que destacar es que en Java s olo est a permitida la herencia simple, una nueva clase s olo puede extender a una u nica clase base. Dicho de otro modo, una clase hija no puede tener m as de una clase padre.

Caracter stica Java s olo admite herencia simple. Una clase no puede tener m as de una clase padre. Imaginemos que necesitamos ampliar nuestra clase Persona, para a nadirle nueva funcionalidad. Queremos que nuestra nueva clase contenga la provincia, la poblaci on de residencia y la edad 1 de cada uno de nuestros contactos. Para ello tenemos dos alternativas antag onicas: 1. Reescribir la clase desde cero. 2. Aprovechar al m aximo el c odigo existente. Si optamos por la primera opci on, no tenemos nada nuevo que aprender, con lo aprendido hasta ahora podemos resolverlo. Si optamos por la segunda opci on estaremos haciendo uso del mecanismo de Herencia. 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 nade m as funcionalidad a una clase ya existente. A la nueva clase se le llama clase hija o extendida, a la clase original se le llama clase padre o base. Para indicar que una clase extiende el comportamiento de otra utilizamos la palabra reservada extends. Supongamos que queremos ampliar la denici on de una Persona para que contenga datos de su lugar de residencia, como son la Provincia y la Poblaci on y tambi en la Edad, y llamemos a esta nueva clase Ciudadano. en Java lo hacemos de este modo:
1 Cuando veamos las clases para manipular fechas en el Cap tulo 8 veremos una mejor implementaci on para obtener la edad de una persona a partir de su fecha de nacimiento

DE UNA CLASE. 3.2. EXTENSION

53

1 2 3

public c l a s s Ciudadano extends P e r s o n a { // D e f i n i c i o n de l a nueva c l a s e e x t e n d i d a }

En la denici on de la nueva clase podemos incluir nuevos atributos y m etodos. En el siguiente c odigo de ejemplo, a la clase Ciudadano se le han a nadido los tres nuevos atributos antes mencionados y los getters y setters para estos nuevos atributos. Nuestra nueva clase Ciudadano posee tanto los nuevos m etodos denidos en ella como los m etodos denidos en su clase padre (con las restricciones de accesibilidad que veremos en la secci on 3.6. En el Listado 3.1 aparece la denici on completa de la nueva clase Ciudadano.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

package t i p o s ; public c l a s s Ciudadano extends P e r s o n a { private S t r i n g p o b l a c i o n ; private S t r i n g p r o v i n c i a ; private i n t edad ; public Ciudadano ( ) { super ( ) ; iniciaAtributos () ; } @Override protected void i n i c i a A t r i b u t o s setNombre ( " U n n o m b r e " ) ; edad = 0 ; } public S t r i n g g e t P o b l a c i o n ( ) { return p o b l a c i o n ; } 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 ) { this . poblacion = poblacion ; } public S t r i n g g e t P r o v i n c i a ( ) { return p r o v i n c i a ; } 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 ) { this . provincia = provincia ; } public i n t getEdad ( ) { return edad ; } public void setEdad ( i n t edad ) { t h i s . edad = edad ; } } () {

Listado 3.1: Denici on de la clase Ciudadano C omo hacemos uso de los m etodos de la clase, tanto de los denidos en la clase extendida como los denidos en la clase base?, sencillamente como lo est abamos haciendo hasta ahora: a trav es de las referencias, como en el siguiente ejemplo de c odigo:
1 2

Ciudadano c i u d a d a n o = new Ciudadano ( " J o s e " , " Garc a " , " 555 123 456 " , " Alcorc on " , " Madrid " , 40; 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 ( ) ) ;

54
3

CAP ITULO 3. HERENCIA E INTERFACES

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 etodo getNombre() denido en la clase padre, a partir de una referencia de la clase hija, mientras que en la l nea 3 hacemos uso del m etodo getEdad() denido en la clase hija. Podemos utilizar una referencia a la clase padre para acceder a los mismos m etodos? No, aunque es perfectamente v alido asignar una referencia de una clase hija a una referencia a la clase padre, a trav es de la referencia a la clase padre s olo tendremos acceso a los miembros declarados en ella. En particular, para el ejemplo de la clase padre Persona y su clase hija Ciudadano, el siguiente c odigo ejemplo contiene un error:
1 2 3 4

Ciudadano c i u d a d a n o = new Ciudadano ( ) ; 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 lido . 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 . p e r s o n a . getEdad ( ) ; // E r r o r ! ! ! , getEdad ( ) e s t a d e f i n i d o en Ciudadano .

Tambi en es un error asignar a una referencia de una clase hija una referencia a la clase padre, el siguiente c odigo de ejemplo contiene un error:
1 2

P e r s o n a p e r s o n a = new P e r s o n a ( ) ; 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 e ocurrir a si no existiese esta prohibici on, podr amos asignar a una referencia a Ciudadano una referencia de su clase padre Persona, y a trav es de la referencia a Ciudadano podr amos invocar a, por ejemplo, el m etodo getEdad(), pero, la clase Persona no posee el atributo int edad;, Qu e se deber a devolver en este caso?

3.2.1.

Sobrescribir atributos.

En algunas circunstancias, podemos vernos en la necesidad de denir un atributo en una clase hija con el mismo nombre que en su clase padre, como muestra el siguiente c odigo de ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12

// Est a e s l a c l a s e p a d r e public c l a s s D i s t a n c i a { float distancia ; public D i s t a n c i a ( ) { distancia = 0; } public D i s t a n c i a ( f l o a t d i s t a n c i a ) { this . d i s t a n c i a = d i s t a n c i a ; } // S i g u e l a d e f i n i c i o n de e s t a c l a s e .

DE UNA CLASE. 3.2. EXTENSION


13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

55

} // Esta e s l a c l a s e h i j a 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 { // 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 o double d i s t a n c i a ; public D i s t a n c i a D o b l e P r e c i s i o n ( ) { distancia = 0; } 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 ) { this . d i s t a n c i a = d i s t a n c i a ; } // S i g u e l a d e f i n i c i o n de e s t a c l a s e . }

En este caso se dice que el atributo distancia de la clase hija DistanciaDoblePrecision sobrescribe el atributo distancia de la clase padre Distancia. Cuando una clase hija sobrescribe alg un atributo de su clase padre, el atributo de la clase padre queda oculto , de modo que si aparece el nombre del atributo en la clase hija se utilizar a el atributo denido en esta clase y no el denido 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 el tendremos que hacer uso de otro mecanismo como veremos m as adelante en esta secci on. C omo accedemos al atributo distancia desde fuera de la clase? Ya lo sabemos, a trav es de referencias. De acuerdo, entonces, Qu e mostrar a el siguiente ejemplo?:
1 2 3 4

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 ) ; 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 ) ; 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 ) ; 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 a este c odigo es, sorpresa: El valor de distancia es: 100.0 El valor de distancia2 es: 0.0 Qu e ha ocurrido? Nada extra no, simplemente que al acceder al atributo a trav es de la referencia, se ha buscado este valor en la denici on de la clase correspondiente a la referencia, que en los dos casos es Distancia y el atributo que se est a iniciando en la l nea 3 del c odigo anterior es el de la clase hija DistanciaDoblePrecision pues el objeto que se crea es de la clase extendida. Comparemos con el resultado de la ejecuci on de este otro c odigo ejemplo:
1 2 3 4

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 ) ; 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 ) ; 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) ; 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 a este c odigo es: El valor de distancia es: 100.0 El valor de distanciaDoblePrecision es: 200.0

56

CAP ITULO 3. HERENCIA E INTERFACES

En este u ltimo ejemplo, lo u nico 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 etodos.

Para introducir el modo de sobrescribir m etodos imaginemos que hemos a nadido al c odigo de la clase Distancia un nuevo m etodo que nos permita incrementar la distancia actual:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public c l a s s D i s t a n c i a { float distancia ; public D i s t a n c i a ( ) { distancia = 0; } public D i s t a n c i a ( f l o a t d i s t a n c i a ) { this . d i s t a n c i a = d i s t a n c i a ; } 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 ) { d i s t a n c i a += i n c r e m e n t o ; } // S i g u e l a d e f i n i c i o n de e s t a c l a s e . }

Listado 3.2: Denici on de la clase Distancia Ahora queremos probar nuestra nueva funcionalidad con este ejemplo:
1 2 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 = 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 ) ; distancia . incrementaDistancia (100) ; 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 omo es posible? Estamos intentando incrementar la distancia inicial de 100 en otros 100, y parece que no lo conseguimos. Donde est a el problema?. Nuestro nuevo m etodo incrementaDistancia(float) est a denido en la clase padre, y este m etodo incrementa el valor del atributo que hay denido en ella, no en la clase hija. C omo podemos arreglarlo? La respuesta es sobrescribiendo el m etodo incrementaDistancia(float) en la clase hija de este modo:
1 2 3 4 5 6 7

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 { double d i s t a n c i a ; public D i s t a n c i a D o b l e P r e c i s i o n ( ) { distancia = 0; }

DE UNA CLASE. 3.2. EXTENSION


8 9 10 11 12 13 14 15 16 17

57

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 ) { this . d i s t a n c i a = d i s t a n c i a ; } @Override 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 ) { d i s t a n c i a += i n c r e m e n t o ; } // S i g u e l a d e f i n i c i o n de e s t a c l a s e . }

Listado 3.3: Denici on de la clase DistanciaDoblePrecisiona N otese el uso de la anotaci on @Override que indica al compilador de Java que se est a intentando sobrescribir un m etodo en la clase padre. Ahora s , el resultado obtenido es: El valor de distancia es: 200.0 Para que una clase hija sobrescriba un m etodo de su clase padre es necesario que ambos m etodos tengan la misma signatura y el mismo tipo de retorno, de lo contrario no se sobrescribe el m etodo, como en el siguiente ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

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 { double d i s t a n c i a ; public D i s t a n c i a D o b l e P r e c i s i o n ( ) { distancia = 0; } 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 ) { this . d i s t a n c i a = d i s t a n c i a ; } // 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 argumento // Se p r o d u c e un e r r o r @Override 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 ) { d i s t a n c i a += i n c r e m e n t o ; } // S i g u e l a d e f i n i c i o n de e s t a c l a s e . }

Aunque es posible que la clase hija sobrescriba un m etodo de la clase padre ampliando el modicador de acceso, por ejemplo, en el caso del Listado 3.2.2 posemos denir el m etodo incrementaDistancia como public void incrementaDistancia(double incremento), de modo que hemos ampliado el modicador de acceso desde acceso paquete hasta public. Lo que no est a permitido es que cuando se sobrescribe un m etodo de la clase padre, el hijo restrinja el modicador de acceso. Siguiendo con el ejemplo, intentar sobrescribir el m etodo incrementaDistancia como private void incrementaDistancia(double incremento), dar a un error. Gracias al uso de la anotaci on @Override2 obtenemos un error que nos informa que el nuevo m etodo no est a sobrescribiendo a ning un m etodo en su clase padre. Si eliminamos la anotaci on no obtenemos ning un error y lo que estaremos haciendo es deniendo un nuevo m etodo que toma como argumento una variable de tipo double.
2 Las

anotaciones fueron introducidas en la versi on 5 de Java

58

CAP ITULO 3. HERENCIA E INTERFACES

Buenas pr acticas Para asegurar que los m etodos en las clases hijas sobrescriben m etodos en la clase padre util cese la anotaci on @Override en el m etodo sobrescrito. Ya sabemos que a una referencia a una clase padre le podemos asignar una referencia a cualquier clase hija, de acuerdo, modiquemos nuestro c odigo de prueba del siguiente modo:
1 2 3

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 ) ; distancia . incrementaDistancia (100) ; 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 e es lo que obtenemos? Sorpresa de nuevo: El valor de distancia es: 0.0 C omo puede ser que obtengamos 0.0 si estamos creando un objeto de tipo DistanciaDoblePrecision con un valor inicial de 100 y despu es lo estamos incrementando en 100 unidades m as?. La respuesta, esta vez est a recogida en este concepto clave:

Concepto clave Cuando accedemos a los m etodos de un objeto a trav es de una referencia se selecciona el m etodo a partir del tipo del objeto y no de la referencia a trav es de la que se accede. Este concepto es muy importante en POO y a este mecanismo se le llama Vinculaci on din amica . Qu e est a ocurriendo entonces? Ocurre que distancia es una referencia 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 etodo incrementaDistancia(100) la vinculaci on din amica ejecuta el c odigo de DistanciaDoblePrecision no el de Distancia. Mientras que tal y como sabemos de la Secci on 3.2.1, si utilizamos atributos no se hace uso de la vinculaci on din amica y se accede al atributo correspondiente al tipo que indica la referencia no el objeto que hay por debajo de ella, por lo tanto si escribimos distancia.distancia estamos accediendo al atributo en Distancia pero el atributo que se increment o con distancia.incrementaDistancia(100) fue el que increment o la vinculaci on din amica, es decir, el de DistanciaDoblePrecision. N otese la diferencia fundamental con respecto al acceso a los atributos, donde el atributo al que se accede se determina por el tipo de la referencia y no del objeto. Tambi en es posible que una clase sobrescriba un m etodo ampliando el tipo del valor de retorno, es decir que si en la clase padre Distancia tenemos un m etodo como Distancia metodo() la clase hija puede sobrescribirlo con el m etodo DistanciaDoblePrecision metodo(), ya que se ha ampliado el tipo del valor de retorno

DE UNA CLASE. 3.2. EXTENSION

59

desde el original Distancia a DistanciaDoblePrecision, esta posibilidad fue introducida en la versi on 5 de Java. Pero cuidado, esto u ltimo no funciona con los tipos de retorno primitivos, si tenemos en la clase padre un m etodo denido como public float unMetodo() 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 etodos o atributos sobrescritos en la clase padre. Si escribimos en la clase hija simplemente el nombre del atributo o del m etodo estaremos haciendo uso de la denici on dada para ellos en la clase hija. C omo accedemos a los miembros sobrescritos desde la clase hija?. La respuesta es haciendo uso de la palabra reservada super. Denici on 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 odigo de los ejemplos de prueba, nunca hemos utilizado el constructor sin par ametros de las clases Distancia (v ease el listado 3.2) ni de la clase DistanciaDoblePrecision (v ease el listado 3.3), luego, podemos intentar eliminar estos dos constructores e iniciar el atributo distancia de ambas clases en el momento de la denici on, tal y como se muestra en el siguiente listado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

// D e f i n i c i o n de l a c l a s e public c l a s s D i s t a n c i a { float distancia = 0; // E l i m i n a d o e l constructor

D i s t a n c i a //

s i n para metros

public D i s t a n c i a ( f l o a t d i s t a n c i a ) { this . d i s t a n c i a = d i s t a n c i a ; } 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 ) { d i s t a n c i a += i n c r e m e n t o ; } // S i g u e l a d e f i n i c i o n de e s t a c l a s e . } // D e f i n i c i o 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 // 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 { double d i s t a n c i a = 0 ; // E l i m i n a d o e l constructor s i n para metros

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 ) { this . d i s t a n c i a = d i s t a n c i a ; } @Override 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 ) { d i s t a n c i a += i n c r e m e n t o ; } // S i g u e l a d e f i n i c i o n de e s t a c l a s e .

60

CAP ITULO 3. HERENCIA E INTERFACES

Pero inmediatamente obtendremos el siguiente error en el c odigo de la clase DistanciaDoblePrecision Implicit super constructor Distancia() is undened. Must explicitly invoke another constructor. Este error es debido a que, el constructor de la clase hija public DistanciaDoblePrecision(double distancia) est a intentando invocar impl citamente al constructor de la clase padre public Distancia() que no est a denido. Este es el mecanismo en la creaci on de objetos en Java cuando existe relaci on de herencia entre clases, desde los constructores de las clases hijas, si no se indica lo contrario, se intenta invocar al constructor sin par ametros de la clase padre, que por este motivo es llamado Constructor por 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 acticas Para evitar problemas en la ceraci on de objetos, es conveniente denir siempre el constructor por defecto en nuestras clases. El error anterior lo podemos corregir de dos modos, a nadiendo los constructores por defecto a cada una de las clases, o bien, llamando desde el constructor con par ametros de la clase hija al constructor con par ametros de la clase padre, para que no se llame por defecto el constructor sin par ametros, que no est a denido:
1 2 3 4

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 ) { super ( 0 ) ; // Llamamos a l c o n s t r u c t o r con p a r ametros d e l padre this . d i s t a n c i a = d i s t a n c i a ; }

Si optamos por la segunda soluci on, la llamada al constructor del padre es 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 rst statement in a constructor La pregunta que nos surge es A qu e constructor se llama desde el constructor por defecto de la clase Distancia que no est a extendiendo a ninguna otra 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 a en la ra z del arbol de jerarqu de clases en Java, y que si una clase expl citamente no extiende a ninguna otra, impl citamente est a extendiendo a la clase Object. Concepto clave La clase Object se encuentra en la ra z del arbol de jerarqu a de clases en Java. Cualquier otra clase, bien directamente o bien a trav es de herencia, es hija de la clase Object.

3.2.5.

El operador instanceof.

Ya sabemos que cuando llamamos a los m etodos de un objeto a trav es de una referencia, es el tipo del objeto (la clase a la que pertenece) el que determina qu e m etodo se ha de llamar. A este mecanismo lo hemos llamado Vinculaci on

DE UNA CLASE. 3.2. EXTENSION

61

din amica. No importa el tipo de la referencia a la que asignemos el objeto siempre que, evidentemente, el tipo de la referencia sea compatible con el tipo del objeto, en tiempo de ejecuci on el mecanismo de vinculaci on din amica determinar a cual es el m etodo que se ha de llamar si es que ese m etodo est a sobrescrito. La pregunta que ahora nos surge es: Si el u nico acceso que tenemos es a trav es de referencias y el tipo de la referencia no tiene por qu e coincidir con el tipo del objeto que tiene asignado, basta con que sean compatibles, C omo podemos conocer el verdadero tipo del objeto asignado a la referencia?. Para dar contestaci on a esta pregunta Java pone a nuestra disposici on un 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 2 3 4 5 6

P e r s o n a p e r s o n a = new P e r s o n a ( ) ; 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 } 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} Ciudadano c i u d a d a n o = new Ciudadano ( ) ; 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 } 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 actica:

Buenas pr acticas Intenta evitar el uso del operador instanceof en tu c odigo, utiliza polimorsmo para no hacer uso de este operator.

3.2.6.

El modicador final.

En el cap tulo 2 vimos el uso del modicador final aplicado a los atributos de una clase. El operador final tambi en se puede aplicar a los m etodos de una clase, de tal modo que si un m etodo se declara como final estamos indicando que ese m etodo no puede ser sobrescrito por ninguna clase hija. Con ello estamos garantizando que el trabajo que realiza el m etodo es siempre el mismo con 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 omo se puede violar el comportamiento al iniciar una instancia por parte de un hijo si el padre no protege el m etodo que inicia los atributos con el modicado final :
1 2 3 4 5 6 7 8

// C o digo de l a c l a s e p a d r e public c l a s s P e r s o n a { private S t r i n g nombre ; private S t r i n g a p e l l i d o s ; private S t r i n g t e l e f o n o ; private s t a t i c i n t n I n s t a n c i a s ; public P e r s o n a ( ) {

62
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

CAP ITULO 3. HERENCIA E INTERFACES

super ( ) ; iniciaAtributos () ; } protected void i n i c i a A t r i b u t o s ( ) { nombre = " " ; apellidos = "" ; telefono = "" ; nInstancias = 0; } // S i g u e l a d e f i n i c i o n de l a c l a s e // C o digo de l a c l a s e h i j a public Ciudadano ( ) { super ( ) ; // Aqu cambiamos e l comportamiento de i n i c i o de l o s iniciaAtributos () ; } @Override protected void i n i c i a A t r i b u t o s ( ) { setNombre ( " U n n o m b r e " ) ; } // S i g u e l a d e f i n i c i o n de l a c l a s e

atributos

Simplemente a nadiendo el modicador final al m etodo iniciaAtributos de la clase padre, si intentamos sobrescribir este m etodo en la clase hija obtendremos el siguiente error Cannot override the nal method from Persona advirti endonos que no podemos sobrescribir un m etodo declarado como final en la clase padre. Buenas pr acticas Los m etodos a los que se llama desde los constructores de una clase deben ser modicados como final para prevenir que alguna clase hija modique el comportamiento al crear instancias de la clase. Es muy recomendable seguir la anterior buena pr actica, piensa que ocurrir a si en el constructor de una clase padre que abre una conexi on a una base de datos, y una clase hija sobrescribiese las tareas de inicio, y la conexi on a la base de datos no se estableciese; toda la aplicaci on dejar a de funcionar. El modicador final tambi en se puede aplicar sobre una clase de este modo:
1 2

public f i n a l c l a s s P e r s o n a { // 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 a declarada como final y por lo tanto no se puede extender, es decir no podemos crear hijas de ella.

3.2.7.

M etodos static.

En el Cap tulo 2 vimos el signicado del modicador static cuando se aplica a m etodos. El modicador static indica que el m etodo pertenece a la clase y no a las instancias de la clase. Por otro lado hemos visto lo que signica la Vinculaci on din amica, determinar en tiempo de ejecuci on el m etodo que se debe llamar al invocarse desde una instancia cuando est a sobrescrito. F jate

3.3. CLASES ABSTRACTAS.

63

que el mecanismo de sobrescribir m etodos funciona en el ambito de los objetos, mientras que los m etodos static pertenecen al ambito de las clases. Es por esto que un m etodo static de una clase padre no se puede sobrescribir, los m etodos static de la clase padre son m etodos ocultos que no son visibles desde las clases hija. Si en una clase hija declaramos un m etodo con la misma signatura que un m etodo static en la clase padre, lo que estamos haciendo realmente es creando un nuevo m etodo en la clase hija sin ninguna relaci on con el mismo m etodo en la clase padre. Obviamente si intentamos usar la anotaci on @Override para indicar que queremos sobrescribir el m etodo obtendremos un error This instance method cannot override the static method from ...

3.3.

Clases abstractas.

Hasta este momento, siempre que hemos denido los m etodos de las clases que hemos creado, siempre hemos escrito c odigo en la denici on de los m etodos. A veces es u til simplemente declarar m etodos un una clase padre, sin dar ninguna implementaci on para ellos, y delegar la implementaci on a las clases hijas que la extiendan. Esta es una t ecnica muy potente que utiliza el concepto de Polimorsmo de la POO. De este modo estamos garantizando que todas las clases hijas de la misma clase padre tiene un m etodo con la misma signatura, aunque, obviamente, cada una de las clase hijas puede tener una implementaci on distinta para el m etodo polim orco. Si queremos indicar que no vamos a dar una implementeaci on para alg un m etodo declarado en la clase, debemos modicarlo con la palabra reservada abstract, con la restricci on de que si una clase tiene alg un m etodo abstract ella misma tambi en debe ser declarada como abstract. Tambi en podemos declarar una clase como abstract sin que ninguno de sus m etodos los sea. Si una clase es declarada como abstract, sobre ella tenemos la restricci on recogida en el siguiente concepto clave: Concepto clave No se pueden crear instancias de una clase declarada como abstract De no existir esta restricci on Qu e ocurrir a si se llamase a un m etodo abstract de un objeto? Qu e c odigo se ejecutar a? Evidentemente de poder ser as tendr amos un grave problema, ya que puede que no existiene ning un c odigo para ejecutar. Los m etodos abstract de la clase padre deben ser denidos en las clases hijas, en cuyo caso los m etodos en las clase hijas ya no ser an abstract y tampoco la propia clase hija. Ahora bien, puede existir alg un el caso en que una clase hija tampoco dena alg un m etodo abstract de la clase padre; en este caso la clase hija tambi en deber a ser declarada abstract y no podremos crear instancias de ella. Un ejemplo recurrente para mostrar el uso de las clases abstract es una aplicaci on que dibuje distintas guras geom etricas tales como c rculos, tri angulos y cuadrados. Podr amos declara el comportamiento com un de todas estas clases, por ejemplo el m etodo dibujate() en una clase padre llamada Figuras, y cada una de las clases hijas tuviese la implementaci on adecuada para dibujarse

64

CAP ITULO 3. HERENCIA E INTERFACES

dependiendo de su naturaleza. De modo muy esquem atico el c odigo de estas clases podr a ser algo como lo mostrado en el Listado 3.4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

public abstract c l a s s F i g u r a { public abstract void d i b u j a t e ( ) ; // S i g u e l a d e f i n i c i o n de l a c l a s e } public c l a s s T r i a n g u l o extends F i g u r a { public void d i b u j a t e ( ) { // C o digo p a r a d i b u j a r un t r i angulo } // S i g u e l a d e f i n i c i o n de l a c l a s e } public c l a s s Cuadrado extends F i g u r a { public void d i b u j a t e ( ) { // C o digo p a r a d i b u j a r un c u a d r a d o } // S i g u e l a d e f i n i c i o n de l a c l a s e } public c l a s s C i r c u l o extends F i g u r a { public void d i b u j a t e ( ) { // C o digo p a r a d i b u j a r un c rculo } // S i g u e l a d e f i n i c i o n de l a c l a s e }

Listado 3.4: Denici on de una clase abstract y algunas clases hijas. La potencia del c odigo del Listado anterior se hace evidente cuando recordamos 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 2 3 4 5 6

F i g u r a f i g u r a = new C i r c u l o ( ) ; f i g u r a . d i b u j a t e ( ) ; // D i b u j a r a un c rculo f i g u r a = new T r i a n g u l o ( ) ; 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 ngulo f i g u r a = new Cuadrado ( ) ; 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 on din amica en cada una de las llamadas al m etodo dibujate() determinar a el m etodo que se debe invocar.

Buenas pr acticas El dise no de tus aplicaciones debe estar orientado al interface no a la implementaci on. Qu e quiere decir esta buena pr actica? La idea es que debes concentrarte en crear buenas abstracciones, es decir debes intentar encontrar el comportamiento com un (declaraci on de los m etodos) a tus clases para que las puedas tratar de manera homog enea, con independencia de c omo se materializa ese comportamiento com un (implementaci on de los m etodos en cada clase). En el caso de la aplicaci on para dibujar guras geom etricas, el comportamiento com un es el

3.4. INTERFACES.

65

hecho de que todas las guras geom etricas puede dibujarse. La implementaci on concreta es el modo en que cada una de las clases hija se dibuja. El uso de esta buena pr actica en el Listado 3.5 es que las referencias deben ser siempre del tipo m as general posible (clase abstracta o interface como veremos en la siguiente secci on), y no de una clase concreta. Rescribamos el c odigo del u ltimo listado haciendo caso omiso de esta buena pr actica:
1 2 3 4 5 6

C i r c u l o f i g u r a = new C i r c u l o ( ) ; f i g u r a . d i b u j a t e ( ) ; // D i b u j a r a un c rculo 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 } figura . dibujate () ; 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 } figura . dibujate () ;

Como ves, no podemos aprovechar el comportamiento polim orco de nuestras guras ya que las referencias son de un tipo concreto y no de un tipo abstracto, la clase Figura. En una clase hija tambi en podemos declarar como abstract alg un m etodo denido 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 a denir el m etodo declarado abstract. Obviamente, si una clase hija declara como abstract un m etodo de la clase padre, aunque este no fuese abstract en la clase padre, la tendremos que declarar como abstract.

3.4.

Interfaces.

Los interface son una nueva construcci on del lenguaje Java que da un paso m as all a en las clases abstract. Puedes pensar que un interface es como una clase abstract en la que todos sus m etodos son abstract. Siguiendo con el ejemplo de las guras geom etricas de la Secci on 3.3 podemos denir nuestro primer interface como:
1 2 3

public i n t e r f a c e D i b u j a b l e { public void d i b u j a ( ) ; }

Como ves, estamos usando la palabra reservada interface para indicar que estamos deniendo 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 2 3 4 5 6 7 8 9 10 11 12

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 { @Override public void d i b u j a ( ) { // C o digo p a r a d i b u j a r un t r i angulo } // S i g u e l a d e f i n i c i o n de l a c l a s e } public c l a s s Cuadrado implements D i b u j a b l e { @Override public void d i b u j a ( ) { // C o digo p a r a d i b u j a r un c u a d r a d o

66
13 14 15 16 17 18 19 20 21 22 23

CAP ITULO 3. HERENCIA E INTERFACES

} // S i g u e l a }

definici o n de l a

clase

public c l a s s C i r c u l o implements D i b u j a b l e { @Override public void d i b u j a ( ) { // C o digo p a r a d i b u j a r un c rculo } // S i g u e l a d e f i n i c i o n de l a c l a s e }

F jate que para indicar que las clases est an implementando un m etodo declarado en un interface se anota el m etodo con @Override. Y ahora, de nuevo, aparece la magia del polimorsmo y la vinculaci on din amica: a una referencia de un tipo interface le podemos asignar cualquier objeto de una clase que implemente ese interface, de modo que el c odigo del siguiente listado es perfectamente v alido:
1 2 3 4 5 6

D i b u j a b l e f i g u r a = new C i r c u l o ( ) ; f i g u r a . d i b u j a ( ) ; // D i b u j a r a un c rculo f i g u r a = new T r i a n g u l o ( ) ; f i g u r a . d i b u j a ( ) ; // D i b u j a r a un t r i a ngulo f i g u r a = new Cuadrado ( ) ; f i g u r a . d i b u j a ( ) ; // D i b u j a r a un c u a d r a d o

F jate que hemos utilizado una referencia de un tipo lo m as amplio posible Dibujable y el comportamiento se materializa en la creaci on de las instancias de clases concretas: Circulo, Triangulo o Cuadrado. En la denici on de un interface podemos declarar cualquier n umero de m etodos y tambi en cualquier n umero de constantes como en el siguiente ejemplo:
1 2 3 4 5

public c l a s s i n t e r f a c e D i b u j a b l e { 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 ) ; 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 ) ; public void d i b u j a ( ) ; }

El anterior interface tiene contiene la declaraci on de dos constantes, una dene 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 umero de interfaces. Recuerda que en el caso de la extensi on de una clase Java s olo permite herencia simple. En el siguiente Listado se muestra un ejemplo de clase que implementa m as de un inteface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// D e c l a r a c i o n de un nuevo \ t e x t t t { i n t e r f a c e } 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 { public void e s c a l a ( i n t sx , i n t s y ) ; public void d e s p l a z a ( i n t dx , i n t dy ) ; } // D e c l a r a c i o n de l a c l a s e 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 { @Override public void d i b u j a ( ) { // Aqu la definici o n d e l m e todo } @Override public void e s c a l a ( i n t sx , i n t s y ) {

3.4. INTERFACES.
16 17 18 19 20 21 22 23

67
d e l m e todo

// Aqu la }

definici on

@Override public void d e s p l a z a ( i n t dx , i n t dy ) { // Aqu la definici o n d e l m e todo } }

Los interface al igual que las clases se pueden extender, y adem as un interface puede extender a m as de un interface, la herencia simple es una restricci on en el ambito de las clases, los interface no poseen esta restricci on. En el siguiente listado tienes un ejemplo:
1 2 3

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 { public void g i r a ( f l o a t a n g u l o ) ; }

Listado 3.6: Un interface que extiende a otros dos Con el uso de los interface la Buena pr actica de programar orientado al interface toma a un m as fuerza. Por cierto no confundas la palabra reservada interface con el concepto de interface, este concepto se reere a los m etodos 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 acticas En tus dise nos software, favorece la composici on frente a la herencia. Qu e signica esta buena pr actica? No debemos utilizar nunca la herencia? No, todo lo contrario, la herencia es una t ecnica de POO a objetos muy potente siempre que se la utilice bien. Un mal uso de la herencia es utilizarla para reaprovechar c odigo. La herencia signica que entre los conceptos que queremos abstraer existe una clara relaci on padre-hijo. No debemos utilizar la herencia para reaprovechar c odigo entre clases que no est an relacionadas l ogicamente a 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 on entre esos entes en el mundo real no existe y no debemos trasladarla de un modo articial a nuestro c odigo. La composici on, signica que una clase contiene como atributos instancias de otras clases. Este mecanismo de relaci on entre clases es m as exible que la herencia y por lo tanto menos sensible a los cambios que tengamos que hacer en nuestro c odigo, m as a un si como referencias a las clases utilizamos interfaces o clases abstractas. En denitiva, favorecer la composici on frente a la herencia signica usar la herencia s olo cuando est e justicado en nuestro dise no y no s olo por comodidad.

68

CAP ITULO 3. HERENCIA E INTERFACES

3.5.

Enumeraciones.

Las enumeraciones son una construcci on del lenguaje introducida en la versi on 5 de Java. Las enumeraciones nos sirven para denir listas enumeradas de elementos y algo m as, ya que en cierto modo son como clases, pueden tener constructores, m etodos y atributos. El primer elemento de la enumeraci on tiene la posici on 0. La u nica restricci on que tiene las enumeraciones sobre las clases es que las enumeraciones no se pueden extender ya que impl citamente toda enumeraci on est a extendiendo a la clase java.lang.Enum. Adem as, el ambito de los constructores debe ser private o de paquete. La clase Enum nos proporciona algunos m etodos u tiles que podemos utilizar en nuestra propias enumeraciones, como veremos a continuaci on. Podemos denir una enumeraci on de una manera tan sencilla como la mostrada en el siguiente Listado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

public enum Semana { LUNES( " P r i m e r d a de la semana . " ) , MARTES( " N i t e c a s e s n i t e e m b a r q u e s . " ) , MIERCOLES( " S i n c o m e n t a r i o s . " ) , JUEVES( " S i e m p r e e n m e d i o . " ) , VIERNES( " Ultimo d a de t r a b a j o . " ) , SABADO( " E m p i e z a e l f i n d e s e m a n a . " ) , DOMINGO( " M a ~ nana de nuevo a t r a b a j a r . " ) ; private S t r i n g c o m e n t a r i o ; // 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 . Semana ( S t r i n g c o m e n t a r i o ) { this . comentario = comentario ; } public S t r i n g g e t C o m e n t a r i o ( ) { return c o m e n t a r i o ; } }

Listado 3.7: Denici on de una enumeraci on para los d as de la semana La clase Enum nos proporciona el m etodo values() que devuelve un array de String con todos los nombre de los elementos de la enumeraci on. Cada uno de los elementos de la enumeraci on posee dos m etodos heredados de la clase Enum uno para conocer el nombre del elemento name(), y otro para conocer el ordinal del elemento dentro de la enumeraci on ordinal(), adem as, evidentemente, de los m etodos que nosotros mismo hayamos denido (el m etodo getComentario(), en el ejemplo del Listado 3.8. El siguiente Listado muestra un ejemplo de uso de la anterior enumeraci on:
1 2 3 4 5

f o r ( Semana d i a : Semana . v a l u e s ( ) ) { System . o u t . p r i n t l n ( d i a . name ( ) ) ; System . o u t . p r i n t l n ( d i a . o r d i n a l ( ) ) ; 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 ( ) ) ; }

Listado 3.8: Uso de una enumeraci on

3.6. PAQUETES EN JAVA.

69

3.6.

Paquetes en Java.

Los paquetes son una construcci on del lenguaje Java que nos permite agrupar clases que est an l ogicamente relacionadas en el mismo grupo o paquete. Para denotar que una clase pertenece a un determinado paquete, se indica en la denici on de la clase con la palabra reservada package seguida del nombre del paquete como en el siguiente ejemplo:
1 2 3 4 5

p a c k a g e agenda ; // E sta c l a s e

esta d e n t r o d e l p a q u e t e agenda

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 e // D e f i n i c i o n de l a c l a s e }

Regla de convenci on Los nombres de paquetes se escriben en min usculas. Si el nombre de un paquete est a compuesto por m as de una palabra, se separan mediante puntos. Un nombre v alido de paquete con m as de una palabra es: agenda.datos o agenda.io.teclado. El a mbito o visibilidad por defecto de una clase es el paquete, por ello, para denotar que la visibilidad de una clase est a restringida al paquete en la que est a denida no se utiliza ning un modicador de acceso. Como ya sabemos, la visibilidad tambi en se dene sobre los miembros de una clase de tal manera que un miembro puede se p ublico, privado, protegido o de paquete. Veamos con mas detalle como se restringe la visibilidad con cada uno de estos modicadores de acceso. El modicador de acceso private es el m as restrictivo de los cuatro, un miembro privado no es accesible por ninguna otra clase. Podemos utilizar este modicador de acceso para ocultar completamente miembros de una clase. Una clase nunca se puede denir como private. El modicador 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 denirse como de paquete. El modicador de acceso protected, se comporta exactamente igual que el modicador por defecto pero adem as permite que las clases hijas de la clase protected puedan usar sus miembros a trav es de la herencia aunque estas clases hijas pertenezcan a paquetes distintos. El modicador de acceso public asigna la mayor visibilidad a los miembros de una clase. Un miembro p ublico es accesible desde cualquier otra clase sin importar el paquete en el que est e denido, o si existe una relaci on padre-hija entre ellas. La Tabla 3.1 muestra todas las posibilidades de acceso entre miembros de clases. En la Figura 3.1 su muestra gr acamente las posibilidades seg un el modicador de acceso empleado. A partir de la versi on 5 de Java se introdujo el concepto de import static con la intenci on de facilitar la escritura de c odigo. La idea de los import static

70

CAP ITULO 3. HERENCIA E INTERFACES

Es accesible? Misma clase Clase/subclase del paquete Subclase otro paquete Clase otro paquete

private SI NO NO NO

paquete SI SI NO NO

protected SI SI SI 3 NO

public SI SI SI SI

Tabla 3.1: Modicadores de acceso y su visibilidad

Figura 3.1: Visibilidad de los miembros seg un el modicador de acceso utilizado.

3.7. CLASES E INTERFACE ANIDADOS

71

es incluir una clase o un paquete de clases y poder llamar a los miembros est aticos de las clases importadas sin necesidad de escribir el nombre de la clase, tal y como se muestra en el siguiente Listado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// D e f i n i c i o n de una c l a s e package p a q u e t e . s u b p a q u e t e ; public c l a s s C l a s e { public s t a t i c void metodo ( ) { // D e f i n i c i o n d e l m e todo } } // D e f i n i c i o n de o t r a c l a s e 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 ; public c l a s s ClaseQueUsaImports { public void otroMetodo ( ) { metodo ( ) ;

Los import static son un arma de doble lo, por un lado facilitan la codicaci on, pero por otro se pierde la perspectiva de la pertenencia de los miembros static a sus clases concretas. Hay que utilizarlos con precauci on. Un caso de uso comunmente extendido es en las pruebas unitarias, para incluir los miembros est aticos de los frameworks como JUnit .

3.7.

Clases e interface anidados

Hasta este momento hemos denido cada una de nuestras clases en un chero independiente con extensi on .java. No obstante, en un mismo chero podemos denir m as de una clase siempre que s olo una de ellas sea public y su nombre coincida con el del chero. El ambito o visibilidad del resto de clases debe ser el paquete (recuerda, visibilidad por defecto). Aunque lo aconsejable es que, con independencia del ambito, cada clase est e denida en un chero distinto, ya que esto favorece el mantenimiento del c odigo. Si embargo, dentro de la denici on de una clase podemos denir nuevas clases e interface como se muestra en el siguiente Listado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public c l a s s P e r s o n a { private S t r i n g nombre ; private S t r i n g a p e l l i d o s ; private D i r e c c i o n d i r e c c i o n ; private c l a s s D i r e c c i o n { private S t r i n g c a l l e ; private i n t numero ; private S t r i n g p u e r t a ; private S t r i n g p o b l a c i o n ; private S t r i n g p r o v i n c i a ; } public i n t e r f a c e L e e r D a t o s { public S t r i n g getNombre ( ) ; }

Listado 3.9: Uso de una enumeraci on A la clase Direccion as denida se le llama clase interna y, a efectos de programaci on es una nueva clase como cualquier otra. De igual modo interface LeerDatos es un interface como otro cualquiera.

72

CAP ITULO 3. HERENCIA E INTERFACES

Hay un caso particular de creaci on de clases internas en el que la nueva clase no recibe ning un nombre como se muestra en el siguiente Listado, continuaci on del anterior:
17 18 19 20 21 22 23

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 ( ) { private P e r s o n a p e r s o n a ; @Override public S t r i n g getNombre ( ) { return nombre ; }

Listado 3.10: Uso de una enumeraci on F jate que en la l nea 17 parece que se est a intentando instanciar un interface, cosa que como sabes no est a permitida. Lo que est a ocurriendo es que se est a creando e instanciando una nueva clase sin nombre, y por lo tanto an onima, que est a implementando el interface LeerDatos. Se est a creando e instanciando la clase interna an onima al mismo tiempo, este es el u nico momento en el que se puede instanciar una clase interna an onima, ya que por ser an onima no tienen nombre y por lo tanto no podemos denir sus constructores. Las clases internas an onimas son una construcci on muy potente del lenguaje. Veremos toda su potencia en el Cap tulo 11 dedicado a la creaci on de interfaces gr acos de usuario, y en el Cap tulo 14 dedicado a la programaci on concurrente con hilos.

Cuestiones.
1. Tiene sentido declarar los constructores de una clase como private? Se podr an crear instancias de una clase en la que todos sus constructores son private? Se podr a extender una clase en la que todos sus constructores son private?.

Ejercicios.
1. Modica tu implementaci on de la clase Agenda que escribiste en el ejercicio 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 on sobre la direcci on de residencia de la persona. 3. Amplia la clase Empresa para que contenga informaci on sobre la direcci on de la sede de la empresa. 4. Modica tu agenda para que sea capaz de trabajar con los nuevos tipos de datos denidos.

3.7. CLASES E INTERFACE ANIDADOS

73

Lecturas recomendadas.
El cap tulo 7 del libro de Sierra y Bates [3] expone gr acamente todos los conceptos relacionados con la herencia en Java. Para una exposici on m as detallada sobre clases e interface anidados una excelente referecia es el cap tulo 5 del libro de James Gosling [2]. Para un fundamentado razonamiento de porqu e favorecer la composici on sobre la herencia v ease el item 14 de la referencia [4].

74

CAP ITULO 3. HERENCIA E INTERFACES

Cap tulo 4

Control de versiones con Subversion


Contenidos
4.1. 4.2. 4.3. 4.4. Qu e es un sistema de control de versiones? Principales caracter sticas de Subversion . . Creaci on de un repositorio . . . . . . . . . . . Trabajo con repositorios . . . . . . . . . . . . 4.4.1. Obteniendo informaci on del repositorio . . . 4.5. Integraci on con Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . 76 . 76 . 77 . 78 . . 82 . 84

Introducci on
Este es el primer cap tulo dedicado a una herramienta utilizada en el desarrollo de proyectos inform aticos y no directamente al lenguaje de programaci on Java. Como ya se coment o en la introducci on, el objetivo de este libro es mostrar c omo desarrollar proyectos inform aticos con tecnolog a Java en un contexto de desarrollo lo m as cercano posible al que el programador va a encontrar en cualquier organizaci on de desarrollo de software. Lo que hace realmente valioso a un programador no es s olo que conozca lenguajes de programaci on, si no que conozca las herramientas de desarrollo de software m as utilizadas y que, desde un principio, se integre suavemente en un equipo de desarrollo. Y esta integraci on tiene mucho que ver con toda la experiencia que posea con el trabajo en grupo y las herramientas que lo favorecen. En este cap tulo se presenta la herramienta de control de versiones Subversi on, que, incluso trabajando individualmente, se hace imprescindible en todo proyecto para la gesti on de las diferentes versiones del proyecto a medida que este evoluciona. 75

76

CAP ITULO 4. SUBVERSION

4.1.

Qu e es un sistema de control de versiones?

El escenario m as usual en el desarrollo de software es un equipo formado por varios programadores, probablemente coordinado por un jefe de proyecto. Sea cual sea la metodolog a que se utilice en el desarrollo del proyecto, el c odigo va a estar sujeto a continuas modicaciones por parte de todos los programadores del equipo. En este escenario, no es raro encontrar que dos programadores han modicado el mismo fragmento de c odigo de modo que se llegue a conictos cuando se quiere unicar el c odigo del proyecto. Otra necesidad del equipo es garantizar que todos los programadores pueden disponer de la u ltima versi on del c odigo del proyecto. Un Sistema de Control de versiones es una herramienta software que, de manera autom atica, se encarga de facilitar la gesti on de las versiones del c odigo de un proyecto de manera centralizada.

4.2.

Principales caracter sticas de Subversion

Subversion es una herramienta centralizada de ayuda al control de versiones. Su uso no es exclusivo en el desarrollo de proyectos inform aticos, si no que puede utilizarse en cualquier proyecto que requiera de un sistema autom atico de control de versiones. El concepto central en Subversion es el Repsositorio . Por repositorio se entiende la u ltima versi on del proyecto que existe en el sistema de control de versiones. El paradigma que Suversion utiliza es Copia-Modicaci on-Fusi on (CopyModify-Merge en ingl es). En este paradigma, cada uno de los miembros del equipo, cuando empieza a trabajar en el proyecto, hace una copia local del contenido del repositorio; modica su copia local y nalmente fusiona sus modicaciones locales con el c odigo del repositorio, resolviendo los posibles conicto que hayan aparecido. Al nalizar esta fase, se dice que se ha creado una nueva versi on del proyecto en el repositorio. Una de las caracter stica principales de Subversion es que las actualizaciones en el repositorio son incrementales, s olo se actualizan los cheros que se han modicado con respecto a la versi on anterior. Otra caracter stica es relativa a la numeraci on de la versi on del repositorio, cada vez que se realiza una modicaci on en el repositorio, se actualiza la versi on de todos los cheros existentes en el repositorio y no u nicamente de los cheros que se han modicado. Por otro lado, se puede trabajar con Subversion de manera local sobre el propio sistema de cheros donde se realiza el desarrollo, o sobre un servidor en red. Y en este u ltimo caso, el servidor utilizado puede ser el propio servidor adhoc que viene incluido con la distribuci on de Subversion (svnserve), o como un m odulo de Apache. La elecci on del modo de trabajo con Subversion se ver a reejada en la URL que utilizaremos para acceder al repositorio. Dependiendo del protocolo 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 al repositorio en el sistema de cheros. En la segunda de las opciones se accede utilizando el servidor ad-hoc que viene incluido en la propia distribuci on de Subversion. En la tercera opci on se utiliza Subversion a trav es de un t unel ssh. La cuarta opci on permite el acceso a un repositorio a trav es de Apache y el

DE UN REPOSITORIO 4.3. CREACION le:/// svn:// svn+ssh:// http:// https:// El repositorio se encuentra en el disco local. El acceso al repositorio se realiza a trav es del servidor svnserve. El acceso al repositorio se realiza a trav es del servidor svnserve utilizando un t unel SSH El acceso al repositorio se realiza a trav es de Apache con el m odulo WebDAV. El acceso al repositorio ser realiza con encriptaci on SSL a trav es de Apache con el m odulo WebDAV.

77

Tabla 4.1: Tipos de acceso a los repositorios Subversion. m odulo WebDAV (del ingl es Web-based Distributed Authoring and Versioning ). Finalmente, en la u ltima opci on se accede al respositorio a trav es de un servidor Apache con encriptaci on ssl (del ingl es Secure Socket Layer ). Cada una de estas opciones tiene sus ventajas y desventajas. En las pr oximas secciones utilizaremos el protocolo svn:// para acceder a un repositorio a trav es del servidor svnserve. El trabajo con Subversion es independiente del protocolo utilizado.

4.3.

Creaci on de un repositorio

La creaci on de un nuevo repositorio se hace utilizando la herramienta svnadmin incluida en la distribuci on de Subversion. Supongamos que hemos creado el directorio ./Repositorio (directorio Repositorio en la ra z de nuestro directorio de usuario), en nuestro disco duro local. Para crear un repositorio Subversion en este directorio, en una consola escribir amos: ~$ svnadmin create ~./Repositorio Si examinamos el contenido del directorio veremos que se han creado los siguiente subdirectorios y cheros dentro del directorio ./Repositorio: drwxr-xr-x drwxr-xr-x -rw-r--r-drwxr-xr-x drwxr-sr-x -r--r--r-drwxr-xr-x drwxr-xr-x 8 32 1 5 16 1 11 4 oscar oscar oscar oscar oscar oscar oscar oscar staff staff staff staff staff staff staff staff 272 1088 229 170 544 2 374 136 23 23 23 23 23 23 23 23 may may may may may may may may 18:48 18:48 18:48 18:48 18:48 18:48 18:48 18:48 . .. README.txt conf db format hooks locks

El chero README.txt contiene un aviso sobre c omo debe ser usado este directorio, s olo a trav es de las herramientas que proporciona Subversion. El directorio hooks contiene scripts b asicos para el trabajo con Subversion. El directorio locks es utilizado por Subversion para los bloqueos del repositorio. El directorio db es el que emplea Subversion para registrar todos los cambios realizados en el contenido del repositorio, es el coraz on del repositorio. Finalmente, el directorio conf es donde se encuentran los cheros de conguraci on para el acceso al servidor de Subversion.

78

CAP ITULO 4. SUBVERSION

A efectos pr acticos, el directorio donde vamos a realizar tareas de conguraci on es conf. Su contenido es el siguiente: drwxr-xr-x drwxr-xr-x -rw-r--r--rw-r--r--rw-r--r-5 8 1 1 1 oscar oscar oscar oscar oscar staff staff staff staff staff 170 272 1080 309 2279 23 23 23 23 23 may may may may may 18:48 18:48 18:48 18:48 18:48 . .. authz passwd svnserve.conf

En el chero svnserve.conf indicamos las opciones de acceso al repositorio, en particular podemos restringir los permisos de lectura y escritura para cada uno de los usuarios a cada uno de los directorios que contiene nuestro repositorio a trav es de lo especicado en el chero authz, y los usuarios y claves de acceso al repositorio en el chero passwd. Como ejemplo u nicamente vamos a especicar los usuarios y sus claves en el chero passwd sin modicar el chero authz, lo que implica que todos los usuarios dados de alta en el chero passwd tendr an acceso total a todos los directorios y cheros del repositorio. Para activar la opci on de acceso a trav es de usuario autorizado hay que descomentar la l nea: password-db = passwd esta l nea indica el nombre del chero de pares usuario/clave para nuestro repositorio. Despu es de descomentar esta l nea debemos editar el chero passwd y a nadir los usuario y sus claves siguiendo el ejemplo que encontraremos en el: # harry = harryssecret # sally = sallyssecret oscar = clave_secreta Con esto ya tenemos activa la conguraci on m as b asica para nuestro repositorio, al que s olo puede acceder el usuario oscar con permisos de lectura y escritura para todos los directorios y cheros del repositorio. El siguiente paso antes de empezar a trabajar con nuestro repositorio es iniciar el servidor de Subversion del siguiente modo: ~$ sudo svnserve --daemon Es necesario tener permisos de administrador para iniciar el servidor de Subversion como un proceso daemon. A partir de ahora ya podemos empezar a trabajar con nuestro repositorio.

4.4.

Trabajo con repositorios

Supongamos que, por claridad, elegimos nombrar al directorio que va a mantener nuestra copia de trabajo como . /CopiaTrabajo (en Eclipse nuestra copia de trabajo ser a algo como . /workspace/NombreProyecto, no es necesario que los nombre del proyecto y del repositorio coincidan). El primer paso que debemos dar es importar el contenido del directorio CopiaTrabajo al repositorio Subversion, suponiendo que nos encontramos en el directorio CopiaTrabajo, de este modo:

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 on que aparece a continuaci on svn://localhost/home/oscar/Repositorio/trunk corresponde a la direcci on donde se encuentra el repositorio. Finalmente -m Import inicial del proyecto" es un mensaje descriptivo de lo que estamos haciendo para que, a posteriori, resulte c omodo encontrar una determinada versi on del proyecto. En este momento se solicitar a la clave del usuario que hemos activado en el chero de conguraci on passwd. Te habr as dado cuenta de que estamos a nadiendo el subdirectorio trunk al repositorio, esto forma parte de las buenas pr acticas de trabajo con Subversion.

Buenas pr acticas En nuestro directorios Subversion es recomendable crear los siguiente subdirectorios: trunk: Directorio donde se encuentra la versi on en desarrollo del proyecto. branches: Directorio donde se encuentran las posibles ramicaciones del proyecto. tags: Directorio donde se encuentran las versiones nales (releases). Para que los cheros y directorios en CopiaTrabajo se conviertan en una copia de trabajo real, necesitamos hacer el checkout del repositorio hacia nuestra copia de trabajo de este modo (f jate en el punto nal ((.)) para indicar que el 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 nuestra copia de trabajo. Para seguir la estructura t pica de un proyecto Eclipse creemos el directorio src para contener los fuentes del proyecto. Cualquier modicaci on en el directorio de trabajo la tenemos que hacer utilizando Subversion, luego, en la consola debemos escribir lo siguiente para crear un directorio que Subversion pueda sincronizar con el repositorio: ~/CopiaTrabajo$ svn mkdir src A src Subversion nos indicar a que se ha a nadido (A) el directorio src a la copia local. Ahora, dentro del directorio creado en el paso anterior creamos un chero de denici on de clase llamado Saludo.java, y le indicamos a Subverion que lo a nadimos a nuestra copia local: ~/CopiaTrabajo$ touch src/Saludo.java ~/CopiaTrabajo$ svn add Saludo.java A src/Saludo.java

80

CAP ITULO 4. SUBVERSION

En este momento el chero Saludo.java no se ha enviado al repositorio, s olo se ha marcado para que la pr oxima vez que se haga un commit este chero se a nada efectivamente al Repositorio. Lo siguiente que debemos hacer es subir al repositorio todos los cambios que hemos hecho en nuestra copia local, de este modo: ~/CopiaTrabajo$ svn commit -m "A~ nadiendo la clase Saludo al repositorio" Observa que es necesario a nadir un mensaje descriptivo de lo que estamos haciendo con la opci on -m Texto del mensaje, si lo olvidas, Subversion te lo pedir a. La respuesta que ver as en consola por parte de Subversion ser a parecida a esta: A~ nadiendo src A~ nadiendo src/Saludo.java Transmitiendo contenido de archivos . Commit de la revisi on 2. Donde se nos informa que la u ltima versi on disponible en el repositorio es la 2. A la u ltima versi on se le llama HEAD. Ahora realiza alg un peque no cambio en el chero Saludo.java, como a nadir un simple comentario y graba tu chero. Para conocer el estado de tu copia local con respecto a la u ltima versi on existente en el Repositorio puedes utilizar la instrucci on status con la opci on -v tecleando en consola: ~/CopiaTrabajo$ svn status -v 1 1 oscar 2 2 oscar M 2 2 oscar . src src/Saludo.java

Este texto nos informa que el chero Saludo.java se ha modicado (letra inicial M), y en el pr oximo commit se subir a la versi on local al repositorio, la opci on -v signica verbose, es decir queremos una descripci on detallada del estado de la copia local. Veamos el resultado de un commit escribiendo: ~/CopiaTrabajo$ svn commit -m "Una modificaci on en el fichero Saludo.java" Enviando src/Saludo.java Transmitiendo contenido de archivos . Commit de la revisi on 3. Este texto nos informa, de nuevo, que no ha habido ning un problema al subir la nueva copia local al Repsositorio. En un equipo de desarrollo cualquier otro programador puede realizar cambios sobre alg un chero en el Repositorio, o puede a nadir nuevos cheros y directorios al repositorio, para conocer en todo momento cual es el estado de nuestra copia local con respecto a la u ltima versi on existente en el Repositorio podemos escribir: ~/CopiaTrabajo$ svn status -u * 3 src/Saludo.java Estado respecto a la revisi on: 5

4.4. TRABAJO CON REPOSITORIOS

81

La opci on -u indica que se compare nuestra versi on local con respecto a la u ltima versi on en el Repositorio, si no la incluimos la comparaci on se realizar a entre la copia local y la u ltima versi on que nos descargamos desde el Repositorio que puede no ser la versi on HEAD del Repositorio. El * indica que el chero Saludo.java en mi copia de trabajo est a en la versi on 3 mientras que en el repositorio la u ltima versi on es la 5. Si queremos conocer cual es el estado local de nuestros chero con respecto de la u ltima actualizaci on escribimos: ~/CopiaTrabajo$ svn diff Index: src/Saludo.java ============================================== --- src/Saludo.java (revisi on: 3) +++ 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 signica que esa l nea se ha a nadido con respecto de la u ltima actualizaci on que hicimos (que no tiene porqu e coincidir con la u ltima versi on que existe en el repositorio). Si la l nea empieza con un signo ((-)) indica que esa l nea sea ha eliminado. Si ahora intentamos hacer un commit obtendremos el siguiente error: ~/CopiaTrabajo$ svn commit -m "Intentando un commit que fallar a" Enviando src/Saludo.java Transmitiendo contenido de archivos .svn: Fall o el commit (detalles a continuaci on): svn: El archivo /trunk/src/Saludo.java est a desactualizado Este error se debe a que nuestra copia local se encuentra en la versi on 3 y la u ltima versi on en el repositorio es la 5, luego es necesario que primero actualicemos nuestra copia local a la u ltima versi on en el repositorio, y en segundo lugar que enviemos los cambios. Para actualizar nuestra copia local a la u ltima versi on del Repositorio (HEAD ) escribimos: ~/CopiaTrabajo$ svn update G svn/Saludo.java Actualizado a la revisi on 5. Esta vez, la letra G al inicio de la l nea indica que Subversion ha sido capaz de mezclar (merge en ingles) la u ltima revisi on existente en el Repositorio con los cambios locales en nuestro chero y no ha encontrado conictos. Si dos programadores no modican las mismas l neas de c odigo, si no que las discrepancias aparecen el l neas de c odigo distintas, no aparecer a ning un conicto cuando Subversion intente mezclar el c odigo de nuestra copia local con el de la copia en el Repositorio. En este caso tenemos, en nuestra copia local, la u ltima versi on en el Repositorio m as nuestros cambios que se han a nadido sin conicto, ahora ya podemos hacer de nuevo un commit:

82

CAP ITULO 4. SUBVERSION

~/CopiaTrabajo$ svn commit -m "Ya actualizado subo mis cambios" Enviando src/Saludo.java Transmitiendo contenido de archivos . Commit de la revisi on 6. Ahora s que ha tenido exito el commit puesto que la versi on de nuestra copia local coincide con la u ltima versi on en el repositorio. En otras ocasiones, cuando una l nea ha sido modicada por dos o m as programadores, Subversion no sabr a c omo resolver el conicto por s solo, y en el momento de intentar hacer un update seremos informados de que existe un conicto, como en el caso siguiente: ~/CopiaTrabajo$ svn diff Index: src/Saludo.java ================================================ --- src/Saludo.java (revisi on: 7) +++ 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 conicto ya que nuestra copia local contiene la l nea del comentario que ha sido eliminada en el repositorio, de hecho, las l neas extra que aparecen en el c odigo se han a nadido realmente al chero Saludo.java. En este caso debemos resolver el conicto a mano, y una vez resuelto (por ejemplo, eliminando todas las l neas insertadas y manteniendo el comentario) se lo indicamos a Subversion del siguiente modo: ~/CopiaTrabajo$ svn resolved Saludo.java Se resolvi o el conflicto de src/Saludo.java De nuevo, podemos seguir trabajando con nuestro repositorio como hasta el momento o hasta que aparezca un nuevo conicto.

4.4.1.

Obteniendo informaci on del repositorio

Sin animo de ser exhaustivos con respecto a las posibilidad para obtener informaci on sobre un repositorio Subversi on, aqu mostramos algunas de las opciones para conocer el estado del repositorio y de nuestra copia local. Subversion nos proporciona instrucciones para conocer en cualquier momento informaci on sobre el repositorio. Con svn log obtenemos informaci on sobre los mensajes que fueron adjuntados con cada nuevo commit, tal y como se muestra a continuaci on:

4.4. TRABAJO CON REPOSITORIOS

83

-------------------------------------------------------------r2 | oscar | 2010-05-17 09:44:03 +0200 (lun, 17 may 2010) | 1 line C odigo del cap tulo Clases. -------------------------------------------------------------r1 | oscar | 2010-05-17 09:43:33 +0200 (lun, 17 may 2010) | 1 line Initial import. -------------------------------------------------------------Si estamos interesados en alguna revisi on en particular, podemos indicarlo con la opci on -r como en el siguiente ejemplo: caterva:LibroJava oscar$ svn log -r 10 -------------------------------------------------------------r10 | oscar | 2010-06-25 10:31:51 +0200 (vie, 25 jun 2010) | 1 line Para el Cap tulo de Entrada/Salida. -------------------------------------------------------------Para conocer el estado del repositorio podemos usar svn list. De nuevo, si estamos interesados en el estado del repositorio para una determinada revisi on podemos utilizar la opci on -r tal y como se muestra en el siguiente ejemplo: caterva:LibroJava oscar$ svn list -r 3 clases/ herencia/ Si lo que nos interesa es conocer el estado de las u ltimas modicaciones de nuestra copia local con respecto al repositorio, podemos utilizar la instrucci on svn status, con lo que obtendremos informaci on del modo siguiente: ? ! A C D M L Punto.java Nuevo.java Punto3D.java Principal.java PruebaPunto.java Ejemplo.java Hola.java

La letra may uscula de la primera columna, antes del nombre de cada chero, es un c odigo que nos indica el estado del chero o directorio con el siguiente signicado: En la tabla 4.2, el u ltimo c odigo indica que el chero ha quedado bloqueado. Esta situaci on puede ocurrir cuando, por ejemplo, al intentar hacer un commit la conexi on con el repositorio remoto se pierde y no se puede acabar el env o. Al realizar cambios en la copia local, Subversion va acumulando en una lista todas las tareas pendientes. En el momento en que se desea sincronizar la copia local con la remota, se bloquean los cheros que se van a sincronizar. Si por alguna raz on alguna de las tareas pendientes no se puede llevar a cabo, el resultado ser a que alg un chero puede quedar bloqueado.

84 ? ! A C D M L

CAP ITULO 4. SUBVERSION El chero no se est a a nadido al repositorio No existe una copia local de este chero El chero se ha marcado para a nadir al repositorio Existen conictos entre la copia local y el repositorio El chero se ha marcado para ser borrado del repositorio Hay modicaciones en la copia local del chero El chero est a bloqueado

Tabla 4.2: C odigos con informaci on sobre el estado de la copia local del chero sobre la copia remota. Para eliminar los bloqueos podemos utilizar la instrucci on svn cleanup, esta instrucci on comprueba el listado de tareas a realizar, y si queda alguna pendiente, intenta realizarla, al nal de lo cual, se eliminar a el bloqueo sobre los cheros. Finalmente, si lo que nos interesas conocer con detalle son los cambios producidos en los cheros entre la copia local y la existente en el repositorio podemos utilizar svn diff.

4.5.

Integraci on con Eclipse

Aunque el trabajo con Subversion desde l nea de instrucciones es bastante sencillo, resulta interesante no tener que abandonar Eclipse para trabajar con el Repositorio de nuestro proyecto. Como se coment o en la Secci on 1.4.1, se puede a nadir nueva funcionalidad a Eclipse instalando nuevos plug-ins, y precisamente existen excelentes plug-ins para trabajar con Subversion desde Eclipse. Uno de ellos es Subclipse, (http://subclipse.tigris.org/), aunque existe otros excelentes plug-ins como Subversive (http://www.eclipse.org/subversive/). Elegir entre uno u otro acaba siendo cuesti on de gustos ya que todos ellos son excelentes, la mejor idea es probar algunos de ellos y quedarnos con el que m as se adapte a nuestra forma de trabajo. La u ltima versi on del plug-in Subclipse puedes encontrarla en la direcci on http://subclipse.tigris.org. En la secci on Dowload and Install encontrar as la u ltima release y la URL desde donde instalar el plug-in. Para instalar un plug-in 1 selecciona la opci on Help Install new Software se abrir a una ventana con una secci on Work with donde debes introducir la URL del plug-in Subclipse 2 . Al pulsar Enter te aparecer an todos los plug-ins disponibles en esa direcci on, aunque algunos de ellos son opcionales, conviene instalarlos todos, as que marca todos ellos y pulsa el bot on Next. En la siguiente ventana se te mostrar an a modo de informaci on todos los paquetes que se instalar an. Pulsa de nuevo Next. En la siguiente ventana se te pide que aceptes los t erminos de la licencia, para ello selecciona la opci on I accept the terms and of the license agreements y pulsa el bot on Finish, ver as como todos los paquetes de clases necesarios para el nuevo plug-in se descargan hacia tu m aquina. Hacia el nal de la instalaci on se abrir a una ventana de advertencia indic andote que
instrucciones son v alidas para la versi on 3.5 (Galileo) y la 3.6 (Helios) de Eclipse el momento de escribir este libro la u ltima versi on de este plug-in es la 1.6 y su URL es http://subclipse.tigris.org/update_1.6.x
2 En 1 Estas

CON ECLIPSE 4.5. INTEGRACION

85

parte del software que est as instalando no est a rmado, por esta vez pulsa la tecla OK para seguir con la instalaci on. Finalmente se te pedir a que para acabar con la instalaci on est a recomendado reiniciar Eclipse, pulsa el bot on Yes y Eclipse se reiniciar a y ya podr as usar el plug-in reci en instalado. Ahora, dispondr as de una nueva Perspectiva que te permitir a visualizar tus repositorios Subversion. Para abrir esta nueva Perspectiva, selecciona la opci on de men u de Eclipse Window Show perspective Other.... Al hacerlo se abrir a una ventana, en ellaselecciona SVN Repository Exploring. Dentro de esa nueva Perspectiva puedes abrir una nueva vista con la opci on de men u Window Show View SVN Repositories. Finalmente, se abrir a una Vista con el t tulo SVN Repositories que inicialmente estar a vac a. En esta nueva vista es donde puedes a nadir conexiones a los repositorios Subversion que quieras. Como ejemplo, vamos a crear una conexi on al repositorio local que tenemos en svn://localhost/home/oscar/Repositorio/trunk (esta direcci on no tiene por qu e coincidir con la que hayas elegido t u), para ello, sobre la vista SVN Repositories pulsa el bot on derecho de tu rat on y en el men u emergente que aparecer a selecciona New Repository Location, se abrir a una ventana solicit andote la URL del repositorio al que te quieres conectar, introduce svn://localhost/home/oscar/Repositorio, f jate que no hemos introducido trunk al nal de la URL, pulsa Finish, ver as que la nueva URL te aparece en la vista SVN Repositories, despliega la URL y ver as bajo ella el directorio trunk y bajo el, el chero Saludo.java. Para hacer el chekout del repositorio selecciona trunk con el bot on derecho y en el men u contextual que te aparecer a selecciona Checkout... se abrir a una nueva ventana con la opci on seleccionada por defecto Check out as a project congured using New Project Wizard, pulsa Finish, y en la nueva ventana despliega la opci on Java y selecciona la opci on Java Project, a partir de aqu , el Wizard es el que ya conoces y utilizas cada vez que quieres crear un nuevo proyecto, as que simplemente introduce un nombre para el nuevo proyecto y pulsa Finish. Al nal de este proceso tendr as una copia local del proyecto en Eclipse sobre la que puedes trabajar tal y como trabajas sobre cualquier otro proyecto Eclipse, con la enorme ventaja de que todo el trabajo con el repositorio puedes hacerlo desde el propio Eclipse. Cada vez que queramos ver las discrepancias entre nuestra copia local y la existente en el repositorio, es decir la informaci on que en consola obten amos con svn diff, ahora la podemos obtener, de manera gr aca, pulsado el bot on derecho del rat on sobre el proyecto en Eclipse y eligiendo del men u contextual Team Synchronize with repository, pasaremos a la perspectiva Team Synchronization donde nos aparecer a toda la informaci on de sincronizaci on con el repositorio. La interpretaci on de los iconos es la que se muestra en la Tabla 4.3. Si existe conicto en alguno de nuestros cheros de c odigo podemos abrir una Vista donde se nos muestra, al mismo tiempo, el estado de nuestra copia local y el estado del chero en el repositorio, para que podamos resolver los conictos c omodamente. Una vez resueltos los conictos pulsamos el bot on derecho del rat on y elegimos Mark as Merged para indicar que hemos resuelto los conictos y que nuestra copia local est a lista para ser subida al repositorio (commit ), solo nos restar a hacer bot on derecho sobre el nombre del chero y seleccionar Commit..., se abrir a una ventana para introducir un comentario para el commit y pulsamos OK.

86 Flecha azul hacia la izquierda Flecha gris hacia la derecha Doble echa roja

CAP ITULO 4. SUBVERSION La versi on del repositorio es m as actual que la copia local. La versi on local es m as actual que la existente en el repositorio. Existe un conicto que Subversion no sabe resolver entre la copia local y la existente en el repositorio.

Tabla 4.3: Signicado de los iconos en la perspectiva Team Synchronization

Lecturas recomendadas.
La referencia obligada para conocer cualquier aspecto de Subversion es [7]. Existe una versi on gratuita que se puede descargar desde http:// subversion.apache.org/. Otra referencia m as compacta es el cap tulo 4 del la referencia [13], donde se detallan los puntos principales para empezar a trabajar con Subversion.

Cap tulo 5

Excepciones
Contenidos
5.1. Qu e es una excepci on? . . . . . . . . . . . . . 5.1.1. Tipos de excepciones . . . . . . . . . . . . . . 5.2. C omo se gestiona una excepci on . . . . . . . 5.3. Creaci on de excepciones propias . . . . . . . . . . . . . . . . 87 . . 88 . 88 . 91

Introducci on
Es evidente que lo deseable es que no se produzcan errores durante la ejecuci on de un programa. A todos nos provoca rechazo utilizar herramientas que fallan, ya sean herramientas software o cualquier otro tipo de herramientas. En el cap tulo 6 veremos la t ecnica de Test unitarios para comprobar nuestro software con objeto de eliminar el mayor n umero posible de errores durante la fase de desarrollo de nuestras aplicaciones. No obstante, durante la ejecuci on de nuestras aplicaciones existir an situaciones an omalas susceptibles de provocar un mal funcionamiento de nuestro software. Piensa por ejemplo en el caso en que un usuario intenta guardar un chero en un directorio protegido contra escritura. Existen lenguajes de programaci on, como C++, que proporcionan un mecanismo opcional de reacci on frente a estas situaciones an omalas, pero el programador no est a obligado a utilizarlo, es como se ha dicho, una opci on. En Java existe un mecanismo de reacci on ante situaciones an omalas muy parecido al de C++ con la gran diferencia de que el programador s que est a obligado a usarlo en aquellas situaciones susceptibles de provocar un error, lo que conduce a la producci on de c odigo m as robusto frente a este tipo de fallos en tiempo de ejecuci on.

5.1.

Qu e es una excepci on?

En Java, una excepci on es una situaci on an omala en tiempo de ejecuci on. Piensa en el ejemplo de la introducci on en el que un usuario intenta guarda un chero en un directorio protegido contra escritura. Piensa tambi en en el acceso a una 87

88

CAP ITULO 5. EXCEPCIONES

posici on fuera de un array est atico. Todo este tipo de situaciones se producen en tiempo de ejecuci on, y aunque podemos estar prevenidos contra ellas, no podemos evitar completamente que vayan a ocurrir.

5.1.1.

Tipos de excepciones

En Java existen tres grandes grupos de excepciones dependiendo de su naturaleza: 1. Excepciones de la propia m aquina virtual. Estas excepciones causadas por un mal funcionamiento de la propia m aquina virtual (S , la m aquina virtual Java tambi en es una pieza de software y como tal est a sujeta 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 on de nuestras aplicaciones puede ocurrir que nuestra aplicaci on se cierre y veamos un mensaje de error de la propia m aquina virtual. Pero quede el lector tranquilo, es extremadamente dif 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 omo intentar acceder a una posici on inexistente de un array est atico; o intentar hacer un casting incompatible sobre una variable de tipo referencia. El c odigo donde se puede dar este tipo de situaciones es tan com un que a nadir m as c odigo para gestionarlas sobrecargar a terriblemente la escritura 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 u ltimo tipo de excepciones est a formado por el resto de situaciones 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 on. En la Figura 5.1 la clase Exception es la clase padre de este grupo de excepciones.

5.2.

C omo se gestiona una excepci on

Java proporciona un mecanismo de gesti on de errores a trav es de los bloques try...catch...finally, tal y como se muestra en el siguiente Listado:
1 2 3 4 5 6 7 8 9

try { 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 S t r i n g l i n e a = f . l e e L i n e a ( ) ; // Q u i z a 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 lectura } catch ( FileNotFoundException e ) { // C o digo de r e c u p e r a c i o n del error 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 on del error } catch ( IOException e ) { // C o digo de r e c u p e r a c i o n del error } finally {

5.2. COMO SE GESTIONA UNA EXCEPCION

89

Figura 5.1: Parte del arbol de jerarqu a de las excepciones en Java.


10 11 12 13

// C o digo com un } // O t r a s l n e a s de c odigo System . o u t . p r i n t l n ( Aqu sigue la

ejecuci o n ) ;

Listado 5.1: Ejemplo bloque try{...} catch{...} finally{...} En el ejemplo anterior, el m etodo de la l nea 2 est a intentando abrir un chero, y una posible situaci on an omala es que el chero no exista (FileNotFoundException) lo que puede provocar un error. En la l nea 3 se est a intentando leer una l nea desde el chero que s est a abierto, lo que tambi en puede provocar alg un tipo de error de entrada/salida. Antes de pasar a ver la t ecnica para gestionar estas situaciones an omalas, 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 on sobre el error que se produjo. Recuerda, todo en Java es un objeto y en particular los errores en tiempo de ejecuci on son instancias de clases que representan errores. La t ecnica para gestionar esta situaci on es: 1. Encerrar en un bloque try{...} el o los m etodos susceptibles de provocar 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 denir un bloque finally{...} que se ejecutar a con independencia de que se genere alguna de las excepciones gestionadas o no. Es decir si existe el bloque finally{...} su c odigo siempre se ejecutara se produzca o no una excepci on. En el listado 5.1, si en la l nea 2 se produce un error porque no se encontrase el chero que se desea abrir, la siguiente l nea de c odigo 3 no se ejecutar a, la

90

CAP ITULO 5. EXCEPCIONES

ejecuci on pasar a directamente al bloque catch(FileNotFoundException e), y tras su ejecuci on al bloque finally de la l nea 10, ya que tenemos denido uno. Despu es de la gesti on del error, la ejecuci on seguir a en la l nea 13. Si tanto el c odigo en el bloque finally{...} como el c odigo posterior a la gesti on de la excepci on se ejecuta (c odigo en la l nea 13), Qu e sentido tiene incluir el bloque finally{...}?. Antes de contestar a esta pregunta veamos c omo podemos obviar la gesti on de una excepci on en el momento en que esta se produce y delegar su gesti on en el m etodo que invoc o al actual en la pila de llamadas. Existen casos es los que nos interesa delegar la gesti on de la excepci on, la excepci on ocurre en la denici on de un determinado m etodo pero no queremos a nadir el c odigo de gesti on de la excepci on en la denici on de ese m etodo, C omo indicamos que un m etodo no va a gestionar una determinada excepci on?, con el uso de la palabra reservada throws como en el siguiente listado:
1 2 3

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 { // Aqu la definici o n d e l m e todo }

En este caso estamos delegando la gesti on de la excepci on al m etodo que llam o a este otro, que es quien delega la gesti on de la excepci on. La palabra reservada throws tambi en se utiliza para lanzar excepciones propias tal y como vamos a ver en la Secci on 5.3. Ahora ya podemos contestar a la pregunta sobre la utilidad del bloque finally{...} observando el ejemplo del Listado 5.2. Si se produjese la excepci on IOException en la l nea 4 durante el proceso de lectura, se abandonar a la ejecuci on del m etodo delegando la gesti on de estas excepciones al m etodo que invoc o a este (ejecuta()). La l nea 6 nunca se ejecutar a y el chero quedar a abierto, su referencia perdida al salir del m etodo y no podr amos cerrar el chero.
1 2 3 4 5 6 7

// E s t e m e 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 on FileNotFoundException y IOException 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 { 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 " ) ; int c a r a c t e r = f r . read ( ) ; 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 ) ; fr . close () ; }

Listado 5.2: Tanto la excepci on FileNotFoundException como IOException se delegan En el ejemplo del Listado 5.3, tanto si se produce una excepci on, como si no se produce, el bloque finally{...} siempre se ejecutar a y el chero se cerrar a en cualquier caso, se produzca o no una excepci on.
1 2 3 4 5 6 7 8

// E s t e m e 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 on FileNotFoundException y IOException 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 { FileReader f r = null ; int c a r a c t e r = 0 ; try { f r = new F i l e R e a d e r ( " f i c h e r o . t x t " ) ; c a r a c t e r = f r . read ( ) ; } finally {

DE EXCEPCIONES PROPIAS 5.3. CREACION


9 10 11 12

91

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 ) ; i f ( f r != n u l l ) f r . c l o s e ( ) ; } }

Listado 5.3: En este caso el chero 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 as espec co (FileNotFoundException) al m as general (IOException). Piensa qu e ocurrir a si se intercambiase el orden, la clase padre aparecer a en el primer bloque catch{...} de modo que tanto si se produjera una excepci on de tipo IOException como si fuera de tipo FileNotFoundException ambas provocar a la ejecuci on del bloque catch{IOException} ya que este bloque atrapa referencias de la clase padre y cualquier clase hija. Recuerda, las excepciones tambi en son instancias de objetos, y por lo tanto sobre ellas es v alido todo lo que aprendimos sobre herencia en el cap tulo 3.

5.3.

Creaci on de excepciones propias

En el desarrollo de nuestras propias aplicaciones resulta interesante poder lanzar excepciones propias ante situaciones inesperadas. Java proporciona un mecanismo para denir nuevas excepciones y lanzarlas en los casos en los que se produzcan situaciones an omalas. El mecanismo para denir y lazar excepciones propias es el siguiente: 1. Denir la nueva clase que representa a nuestra excepci on. 2. Lanzar la excepci on en las situaciones an omalas. 3. Gestionar la excepci on como cualquier otra. Al utilizar este mecanismo estamos creando excepciones que son tratadas del mismo modo que las excepciones predenidas en Java. Veamos cada uno de estos pasos con un ejemplo. Supongamos que queremos generar una excepci on si se solicita una posici on no v alida dentro de nuestra aplicaci on de la Agenda. Lo primero que debemos hacer es denir la clase que representa a nuestra nueva excepci on. El detalle que debemos tener en cuenta es que nuestra excepci on debe ser hija de la clase Exception, tal y como se muestra en el Listado 5.4:
1 2 3 4 5

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

Listado 5.4: Denici on de una excepci on propia F jate que el constructor por defecto llama a super del padre con un String que es una descripci on del error que ha ocurrido. Este String se podr a recuperar en el bloque catch{...} correspondiente como veremos m as adelante. El siguiente paso es lanzar la excepci on en caso de producirse una situaci on an omala, como en el Listado 5.5 :

92

CAP ITULO 5. EXCEPCIONES

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

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 { private f i n a l double CERO ABSOLUTO = 2 7 3 . 1 5 ; public C o n v e r s o r T e m p e r a t u r a s ( ) { super ( ) ; } 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 { 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 ( ) ; return 9 . 0 / 5 . 0 c e l s i u s + 3 2 . 0 ; } 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 { 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 ( ) ; return 4 . 0 / 5 . 0 c e l s i u s ; } }

Listado 5.5: Denici on de una excepci on propia Cabe resaltar un par de cosas del Listado 5.5, la primera es que el m etodo desde el que se lanza la excepci on indica que va a hacerlo con el uso de la palabra reservada throws seguida del nombre de la excepci on. La segunda es que para lanzar la excepci on utilizamos la palabra reservada throw y creamos una nueva instancia de la excepci on con new, recuerda que una excepci on al n y al cabo no es m as que una instancia de una clase. Nuestra excepci on se gestiona como cualquier otra ya denida en el paquete est andar de Java, mediante el bloque try{...} catch{...} finally{...}, tal y como se muestra en el Listado 5.6. En la l nea 7 de este listado se muestra c omo recuperar el texto descriptivo de la excepci on que proporcionamos en la denici on de la clase TemperaturaNoValidaException. Un m etodo u til para recuperar toda la traza de ejecuci on de nuestro programa hasta el momento en el que se produzco la excepci on es printStackTrace() denido en la clase Throwable que es la clase de la que heredan todas las excepciones en Java.
1 2 3 4 5 6 7 8 9

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 ) { try { fharenheit = conversor . celsiusAFharenheit ( c e l s i u 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 ) ; 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 ) ; } 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 ) { System . o u t . p r i n t l n ( e . g e t M e s s a g e ( ) ) ; } }

Listado 5.6: Denici on de una excepci on propia

Lecturas recomendadas
El cap tulo 8 de la referencia [2] presenta todos los detalles de la gesti on de excepciones y c omo crear excepciones propias.

Cap tulo 6

Pruebas unitarias con JUnit


Contenidos
6.1. Qu e son las pruebas unitarias? . . . . . . . . . . . 94 6.1.1. Principios FIRST para el dise no de pruebas unitarias 94 6.2. Pruebas unitarias con JUnit . . . . . . . . . . . . . 95 6.2.1. Creaci on de clases de prueba . . . . . . . . . . . . . 95 6.2.2. La anotaci on @Test . . . . . . . . . . . . . . . . . . 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 a de datos de entrada . . . . . . . 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 . . . . . . . . . . 103

Introducci on
Llegados a este punto ya somos capaces de escribir aplicaciones Java utilizando los principios de la POO. Sabemos c omo denir clases, como utilizar la herencia o la composici on para ampliar el comportamiento de nuestras clases. Incluso somos capaces de controlar las situaciones an omalas que pueden darse durante la ejecuci on de nuestras aplicaciones a trav es de la gesti on de excepciones. El siguiente paso que usualmente se suele dar es comprobar la validez del c odigo realizando pruebas. A veces, estas pruebas son caseras, probamos unos cuantos valores de entrada en nuestra aplicaci on, valores de los que conocemos cual es la salida esperada, y conamos que en el resto de casos nuestro c odigo est e libre de errores. Y en este momento es cuando la conanza se convierte en enga no, nuestro c odigo est a plagado de errores que no hemos sido capaces de detectar y que tarde o temprano saldr an a la luz sumi endonos en la oscuridad, paradojas de la vida. La primera idea que debemos jar es que hacer pruebas de c odigo no debe ser una opci on, es un requerimiento, por defecto, en todo desarrollo de proyectos inform aticos. 93

94

CAP ITULO 6. PRUEBAS UNITARIAS CON JUNIT

La segunda idea que debemos jar es que las pruebas de c odigo no deben ser manuales, si no automatizadas. Si son manuales, por aburrimiento o falta de tiempo acabaremos por no hacerlas. Las pruebas automatizadas forman parte del c odigo del proyecto, son tan importantes como el c odigo que se est a probando y por lo tanto debemos dedicarles el mismo empe no que al desarrollo del c odigo de nuestra aplicaci on. En este cap tulo no se va a mostrar c omo dise nar buenas pruebas de c odigo, y lo que es m as importante, no se va a mostrar c omo escribir c odigo que se pueda probar f acilmente, en la secci on de referencias de este cap tulo se dan algunos t tulos que son de lectura obligada a todo desarrollador que quiera poner en pr actica la prueba de c odigo en sus proyectos. En este cap tulo se va a mostrar c omo utilizar una herramienta para comprobar c odigo. Esta excelente herramienta es JUnit.

6.1.

Qu e son las pruebas unitarias?

Las pruebas unitarias se realizan sobre una clase, para probar su comportamiento de modo aislado, independientemente del resto de clases de la aplicaci on. Este requisito a veces no se cumple, piensa en el c odigo de una clase que accede a una base de datos, y que la prueba de la clase se base en el resultado que se recupera de la base de datos, resulta imposible comprobar esta clase de modo aislado, aunque existen t ecnicas (como los Mock Objects ) que minimizan estas dependencias.

6.1.1.

Principios FIRST para el dise no de pruebas unitarias

Cuando se dise nan pruebas unitarias es importante seguir los principios FIRST. Cada una de las letras de esta palabra inglesa est a relacionada con un concepto. Ve amoslos: Fast : La ejecuci on del c odigo de pruebas debe ser r apida. Si las pruebas consumen 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 olo hay dos posibles resultados de una prueba: ((La prueba pas o con exito)) o ((La prueba fall o)). Timely : Las pruebas han de escribirse en el momento de escribir el c odigo, y no al nal de toda la fase de desarrollo 1
1 La metodolog a de desarrollo Test Driven Development (TDD) lleva este principio al inicio del proceso de desarrollo de tal modo que las pruebas de c odigo se escriben antes que el propio c odigo que se intenta probar.

6.2. PRUEBAS UNITARIAS CON JUNIT

95

6.2.

Pruebas unitarias con JUnit

JUnit es una herramienta para realizar pruebas unitarias automatizadas. JUnit est a integrada en Eclipse, no es necesario descargarse ning un paquete ni instalar un nuevo plug-in para utilizarla. Eclipse facilita la creaci on de pruebas unitarias. Para mostrar con un ejemplo c omo se escriben pruebas unitarias de c odigo con JUnit vamos a utilizar las clases ConversorTemperaturas y TemperaturaNoValidaException que vimos en el cap tulo anterior.

6.2.1.

Creaci on de clases de prueba

Para crear una clase de prueba en Eclipse seleccionamos File New Other..., en la ventana de di alogo que se abrir a seleccionamos Java JUnit JUnit Test Case, se abrir a una nueva ventana de di alogo, en la parte superior 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 veremos que se nos ofrece la posibilidad de seleccionar los m etodos que queremos probar, en nuestro caso vamos a seleccionar celsiusAFharenheit(double) y celsiusAReamur(double) y pulsamos Finish. La clase de prueba que se crear a autom aticamente ser a la que se muestra en el Listado 6.1. Cabe destacar varios puntos de este Listado: 1. F jate en el uso del import static de la l nea 3, es u til para no tener que incluir el nombre de la clase Assert cuando utilizamos alguno de sus m etodos est aticos, como fail. Un import static me permite utilizar todos los m etodos static de una clase sin necesidad de anteponer al m etodo el nombre de la clase 2 . 2. Observa que se han creado dos m etodos de prueba, una para cada m etodo que seleccionamos sobre la clase a probar, y que la signatura de ambos es public void nombreDelMetodo(). Los m etodos de prueba deben ser p ublicos no retornar ning un valor y su lista de argumentos debe estar vac a. 3. F jate que sobre cada uno de los m etodos de prueba aparece la anotaci on @Test que indica al compilador que es un m etodo de prueba. 4. Por defecto, cada uno de los m etodos de prueba tiene una llamada a fail("Mesaje con descripci on.".

1 2 3 4 5 6 7

package t e s t ; import s t a t i c o r g . j u n i t . A s s e r t . ; import o r g . j u n i t . T e s t ; 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 {

static import se introdujeron en la versi on 5 de Java, y aunque son c omodos de utilizar, el uso de JUnit es un caso, pueden provocar confusi on en el programador, ya que al no aparecer el nombre de la clase tendremos la duda de si el m etodo pertenece a la clase actual o a un clase de la que se ha hecho un static import. En general, el uso de los static import est a desaconsejado.

2 Los

96
8 9 10 11 12 13 14 15 16 17 18 19

CAP ITULO 6. PRUEBAS UNITARIAS CON JUNIT

@Test 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 ( ) { f a i l ( " Not yet implemented " ) ; } @Test 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 ( ) { f a i l ( " Not yet implemented " ) ; } }

Listado 6.1: C odigo generado autom aticamente por Eclipse para una clase de prueba.

6.2.2.

La anotaci on @Test

Como ya se ha dicho, la anotaci on @Test sirve para indicar que un determinado m etodo es un m etodo de prueba. Vamos a escribir el primer c odigo de prueba tal y como se muestra en el Listado 6.2. F jate que tambi en hemos a nadido throws TemperaturaNoValidaException para indicar que no queremos gestionar esta posible excepci on en el c odigo del m etodo de prueba.
1 2 3 4 5

@Test 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 { 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 ( ) ; as ser tEq ual s (32 , conversor . celsiusAFharenheit (0) , 0) ; }

Listado 6.2: Un m etodo de prueba. Lo primero que hacemos en el m etodo de prueba es crear una instancia de la clase ConversorTemperaturas, y despu es utilizar el m etodo assertEquals( valorEsperado, valorObtenido, error). Este m etodo comprueba que la diferencia entre el 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 a aseverando que la diferencia entre el valor que devuelve el m etodo celsiusAFharenheit(0) y el valor 32 es cero. Escribamos el c odigo para la segunda de las pruebas tal y como se muestra en el Listado 6.3.
1 2 3 4 5

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

Listado 6.3: Un segundo m etodo de prueba. De modo an alogo al primer m etodo de prueba, en este caso estamos aseverando que la diferencia entre el valor que devuelve la llamada al m etodo celsiusAReamur(0) y el valor 0 es cero. Para ejecutar las pruebas desde Eclipse pulsa el bot on derecho del rat on sobre la clase de prueba y en el men u emergente selecciona la opci on Run As JUnit Test, ver as que se abre una nueva vista con el resultado de la ejecuci on de las pruebas, que en nuestro caso es Runs: 2/2 Errors: 0 Failures: 0 que nos

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 pruebas unitarias con JUnit?. Un fallo es una aseveraci on que no se cumple, un error es una excepci on durante la ejecuci on del c odigo. Generemos un fallo de modo articial 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 informar a que el valor esperado de la prueba era 0 mientras que el valor obtenido es 32.0. A nadamos otro m etodo de prueba que genere un error, para ver la diferencia con un fallo, tal y como se muestra en el Listado 6.4.
1 2 3 4

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

Listado 6.4: Un m etodo de prueba que genera un error. Al ejecutar de nuevo las pruebas esta vez obtendremos la excepci on La temperatura no puede ser menor que -273o C. Y si lo que queremos es precisamente comprobar que se lanza la excepci on?, es decir, Y si nuestra prueba pasa precisamente si se genera la excepci on? Para ello basta con a nadir el atributo expected=TemperaturaNoValidaException.class a la anotaci on @Test quedando de este modo @Test(expected=TemperaturaNoValidaException.class). Si ejecutamos de nuevo las pruebas veremos que todas ellas pasan. Otra t ecnica que no utiliza el atributo expected de la anotaci on @Test para comprobar que se produce una excepci on es la mostrada en el Listado 6.5. Esta vez el m etodo est a etiquetado u nicamente con @Test, y detr as de la l nea de c odigo que esperamos que produzca la excepci on escribimos fail("Para temperaturas por encima de -273 la prueba debe pasar."). Si la excepci on se produce al ejecutarse la l nea 5, la ejecuci on de la prueba continuar a en el bloque catch (TemperaturaNoValidaException e) y la prueba pasar a, que es lo que esperamos. Si no se produce la excepci on en la l nea 5, se ejecutar a la sentencia fail(...) y la prueba no pasar a, cosa que ser a indicativa de que algo ha ido mal ya lo que intentamos probar es que la excepci on s que se produce.
1 2 3 4 5 6 7 8 9

@Test 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 ( ) { 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 ( ) ; try { 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) ; 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 . " ) ; } 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 ) { } }

Listado 6.5: Un m etodo de prueba que genera un error. Este segundo m etodo de prueba de excepciones es el recomendado, ya que es m as f acil interpretar qu e es lo que se est a intentando probar.

98

CAP ITULO 6. PRUEBAS UNITARIAS CON JUNIT

6.2.3.

Las anotaciones @Before y @After

Si revisas el c odigo de los tres m etodos de prueba anteriores ver as que lo primero que hacemos es crear una instancia de la clase ConversorTemperaturas. JUnit nos proporciona un mecanismo para extraer el c odigo que se repite en todos los m etodos, y que debe ejecutarse antes de cualquiera de ellos, a trav es de las anotaciones @Before y @After. Si anotamos un m etodo con @Before su c odigo ser a ejecutado antes de cada uno de los m etodos de prueba, si tenemos tres m etodos de prueba ser a ejecutado antes de cada uno de los tres m etodos. Por su lado, si anotamos un m etodo con @After ser a ejecutado despu es de la ejecuci on de cada uno de los m etodos de prueba. Por lo tanto, podemos usar la anotaci on @Before para iniciar todas las infraestructuras necesarias a la ejecuci on de las pruebas y la anotaci on @After para limpiar estas infraestructuras. En nuestro caso, la clase de prueba quedar a tal y como se muestra en el Listado 6.6.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

import s t a t i c o r g . j u n i t . A s s e r t . ; import o r g . j u n i t . A f t e r ; import o r g . j u n i t . B e f o r e ; import o r g . j u n i t . T e s t ; 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 ; 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 ; 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 { 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 ; @Before 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 ( ) { 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 ( ) ; } @After 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 ( ) { conversor = null ; } @Test 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 { as ser tEq ual s (32 , conversor . celsiusAFharenheit (0) , 0) ; } @Test 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 { 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) ; } @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 ) 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 { 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) ; } }

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 as de un m etodo anotado con alguna de estas anotaciones. Todos los m etodos que est en anotados con @Before se ejecutar an antes de cada uno de los m etodos de prueba y todos los m etodos que est en anotados con @After se ejecutar an despu es de cada uno de los m etodos de

6.2. PRUEBAS UNITARIAS CON JUNIT prueba.

99

6.2.4.

Las anotaciones @BeforeClass y @AfterClass

Podemos mejorar un poco m as nuestra clase de prueba con el uso de dos nuevas etiquetas @BeforeClass y @AfterClass. F jate que la clase que estamos probando ConversorTemperaturas no tiene estado, y por lo tanto el resultado de las llamadas a sus m etodos es independiente del orden en el que se hagan, por lo 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 etodo de una clase de prueba con @BeforeClass ese m etodo se ejecutar a una u nica vez antes de la ejecuci on de cualquier m etodo de prueba. Por otro lado, si anotamos un m etodo de una clase de prueba con @AfterClass el m etodo ser a ejecutado una u nica vez despu es de haberse ejecutado todos los m etodos de prueba, tal y como se muestra en el Listado 6.7.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

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 ; import o r g . j u n i t . A f t e r C l a s s ; import o r g . j u n i t . B e f o r e C l a s s ; import o r g . j u n i t . T e s t ; 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 ; 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 ; 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 { 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 ; @BeforeClass 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 ( ) { 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 ( ) ; } @AfterClass 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 ( ) { conversor = null ; } @Test 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 { as ser tEq uals (32 , conversor . celsiusAFharenheit (0) , 0) ; } @Test 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 { 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) ; } @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 ) 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 { 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) ; } }

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 etodos anotados con @BeforeClass y @AfterClass deben ser ambos static y por lo tanto, los atributos a los que acceden tambi en deben ser static, tal y como vimos en 2.6.

100

CAP ITULO 6. PRUEBAS UNITARIAS CON JUNIT

6.2.5.

Pruebas con bater a de datos de entrada

Cada uno de los m etodos de prueba de los ejemplos anteriores utiliza un tr o de 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 o de valores es tedioso crear un m etodo s olo para el. JUnit proporciona un mecanismo para probar bater as de valores en vez de u nicamente tr os aislados. Lo primero que debemos hacer es anotar la clase de prueba con @RunWith(Parameterized.class) indicando que va a ser utilizada para realizar bater as de pruebas. La clase de prueba ha de declarar un atributo por cada uno de los par ametros de la prueba, y un constructor con tantos argumentos como par ametros en cada prueba. Finalmente necesitamos denir un m etodo que devuelva la colecci on de datos a probar anotado con @Parameters . De este modo, cada uno de los m etodos de prueba ser a llamado para cada una de las 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

import s t a t i c o r g . j u n i t . A s s e r t . ; import j a v a . u t i l . A r r a y s ; import j a v a . u t i l . C o l l e c t i o n ; import import import import import import org . org . org . org . org . org . junit junit junit junit junit junit . AfterClass ; . BeforeClass ; . Test ; . r u n n e r . RunWith ; . runners . Parameterized ; . runners . Parameterized . Parameters ;

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 ; 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 ; @RunWith ( P a r a m e t e r i z e d . c l a s s ) 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 { private double c e l s i u s ; private double f h a r e n h e i t ; private double reamur ; private double e r r o r ; 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 ; 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 ) { this . c e l s i u s = c e l s i u s ; this . fharenheit = fharenheit ; t h i s . reamur = reamur ; this . e r r o r = e r r o r ; } @Parameters 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 ( ) { return A r r a y s . a s L i s t ( new O b j e c t [ ] [ ] { { 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 , {15 , 5 9 . 0 , 1 2 . 0 , 0 . 0 } , {30 , 8 6 . 0 , 2 4 . 0 , 0 . 0 } , {50 , 122.0 , 4 0 . 0 , 0 . 0 } , {90 , 194.0 , 7 2 . 0 , 0.0} }) ; } @BeforeClass public s t a t i c void i n i c i a C o n v e r s o r ( ) { 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 ( ) ; } @AfterClass

error}

6.2. PRUEBAS UNITARIAS CON JUNIT


48 49 50 51 52 53 54 55 56 57 58 59 60 61

101

public s t a t i c void e l i m i n a C o n v e r s o r ( ) { conversor = null ; } @Test 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 { assertEquals ( fharenheit , conversor . celsiusAFharenheit ( c e l s i u s ) , error ) ; } @Test 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 { 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 ) ; } }

Listado 6.8: Ejemplo de denici on de una clase que realiza una bater a de pruebas. De modo resumido, estos son los pasos para denir una clase que ejecuta bater as de pruebas: 1. Anotar la clase de prueba con @RunWith(Parameterized.class). 2. Declarar un atributo en la clase por cada par ametro de prueba. 3. Denir un constructor con tantos argumentos como par ametros de prueba. 4. Denir un m etodo que devuelva una colecci on con todas las tuplas de prueba, y anotarlo con Parameters. Internamente y de modo esquem atico, lo que JUnit hace en el caso de las bater as de pruebas es crear una instancia de la clase de prueba a partir del constructor con tantos argumentos como par ametros en cada una de las tuplas de prueba, y seguidamente llama a cada uno de los m etodos de prueba.

6.2.6.

Ejecutar varias clases de prueba. Test Suites

Lo com un, como hemos visto, es tener varias clases de prueba ya que a veces no tiene sentido una u nica clase donde se realicen todas las pruebas. Piensa por ejemplo en las pruebas parametrizadas, quiz as tengas alg un caso de prueba sobre el que no tenga sentido realizar pruebas parametrizadas, como por ejemplo comprobar que se produce una excepci on. Por lo tanto, si tenemos varias clases de prueba, C omo podemos ejecutar 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 as que agrupaciones de clases de prueba que se ejecutan una tras otra. B asicamente lo que hacemos es anotar una clase para que JUnit la reconozca como una suite de pruebas y con otra anotaci on a nadimos a esta clase todas las clases de prueba que nos interese ejecutar, tal y como se muestra en el Listado 6.9.
1 2 3 4

import o r g . j u n i t . r u n n e r . RunWith ; import o r g . j u n i t . r u n n e r s . S u i t e ; 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 ;

102
5 6 7 8 9 10 11 12 13

CAP ITULO 6. PRUEBAS UNITARIAS CON JUNIT

@RunWith ( S u i t e . c l a s s ) @SuiteClasses ({ TestConversorTemperaturas . class , TestConversorTemperaturas2 . class , TestConversorTemperaturas3 . class , TestConversorTemperaturas4 . class }) public c l a s s A l l T e s t s { }

Listado 6.9: Ejemplo de una suite de pruebas Test Suite. De este modo bien podemos ejecutar una u nica 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 odigo que han cubierto las pruebas, Ha quedado alg un fragmento de c odigo que no se ha ejecutado ni una sola vez para todas las pruebas? C omo podemos saberlo?. Lo ideal es que nuestras pruebas cubran el 100 % del c odigo de la clase que estamos probando. Pero no caigas en el enga no de pensar que por cubrir con pruebas el 100 % del c odigo est as cubriendo todos los casos posibles de la ejecuci on de ese c odigo, ni mucho menos. Puedes cubrir el 100 % de la ejecuci on del c odigo con casos triviales que nunca fallar an y no sacar an a la luz los posibles errores de tu c odigo. Para que veas con claridad la diferencia entre cobertura de c odigo y pruebas exhaustivas el Listado 6.10 te muestra un m etodo a probar y un par de m etodos de prueba. Los m etodos de prueba cubren el 100 % del c odigo del m etodo que se est a probando pero, Qu e pasa si alguna de las referencias que se pasan al m etodo de prueba es null? Evidentemente el m etodo que se est a probando contiene un error que no ser a descubierto por las pruebas aunque estas est en cubriendo el 100 % del c odigo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

// M e todo que s e va a p r o b a r 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 ) { i f ( p r i m e r a . edad > segunda . edad ) r e t u r n 1; i f ( p r i m e r a . edad < segunda . edad ) r e t u r n 1 ; e l s e return 0; } // T r e s m e todos de prueba @Test p u b l i c void masViejoElPrimero ( ) { P e r s o n a p r i m e r a = new P e r s o n a ( ) ; p r i m e r a . setEdad ( 1 0 0 ) ; P e r s o n a segunda = new P e r s o n a ( ) ; segunda . setEdad ( 5 0 ) ; a s s e r t E q u a l s ( 1 , quienEsMayor ( p r i m e r a , segunda ) , 0 ) ; } @Test p u b l i c v o i d m as V i e jo E l Se g u nd o ( ) { P e r s o n a p r i m e r a = new P e r s o n a ( ) ; p r i m e r a . setEdad ( 5 0 ) ; P e r s o n a segunda = new P e r s o n a ( ) ; segunda . setEdad ( 1 0 0 ) ;

6.3. COBERTURA DE LAS PRUEBAS


24 25 26 27 28 29 30 31 32 33 34

103

a s s e r t E q u a l s ( 1 , quienEsMayor ( p r i m e r a , segunda ) , 0 ) ; } @Test p u b l i c v o i d mismaEdad ( ) { P e r s o n a p r i m e r a = new P e r s o n a ( ) ; p r i m e r a . setEdad ( 5 0 ) ; P e r s o n a segunda = new P e r s o n a ( ) ; segunda . setEdad ( 5 0 ) ; a s s e r t E q u a l s ( 0 , quienEsMayor ( p r i m e r a , segunda ) , 0 ) ; }

Listado 6.10: Los tres m etodos cubren el 100 % del c odigo del m etodo que se est a probando, pero este m etodo contiene un error ya que no se comprueba que las referencias sean distintas de null Existe modelos te oricos que dan las pautas a seguir para garantizar que las pruebas son exhaustivas, de modo que se contemplan todos los posibles casos de fallo de nuestro c odigo. La referencia [5] presenta de modo exhaustivo las pruebas de c odigo que se han de realizar para garantizar que se cubre el 100 % de los posibles caminos de ejecuci on.

6.3.1.

EclEmma y su plug-in para Eclipse

Afortunadamente existen excelentes herramientas que miden, de modo autom atico, la cobertura de nuestras pruebas unitarias. Una de esas herramientas, aunque no la u nica es Ecl-Emma de la que existe un plug-in para Eclipse y cuya p agina web es http://www.eclemma.org/. Para instalar este plug-in basta seguir los mismo pasos que se mostraron en la Secci on 4.5, pero siendo la direcci on del plug-in la que se indica en la p agina web de la herramienta. Una vez instalado este plugin te aparecer a un nuevo bot on en Eclipse a la izquierda del bot on ejecutar. Si pulsas este bot on cuando est a seleccionada una clase de prueba, se abrir a una nueva vista de nombre Coverage en la que se te mostrar a todos los resultados de la cobertura de la prueba, y lo que es de gran ayuda, de cada una de las l neas de c odigo de la clase que se est a probando se colorear a su fondo en verde, si la l nea ha sido cubierta completamente por la prueba; amarilla, si ha sido cubierta s olo parcialmente; o roja, si no ha sido cubierta. En la vista Coverage se muestra para cada clase probada, una tabla con el porcentaje de l neas de c odigo cubiertas por la prueba. Sin duda, la herramienta Ecl-Emma y su plugin para Eclipse son excelentes herramientas que contribuyen a aumentar la calidad de nuestro c odigo.

Lecturas recomendadas.
Un excelente libro, de autores espa noles, donde se trata de un modo completo las pruebas de software es la referencia [5]. De lectura obligada si se quiere poner en pr actica las pruebas de software. Otra excelente referencia de autores espa noles es [6]. En la primera parte de este libro se expone con claridad los principios del Dise no de Software Dirigido por Pruebas. En la segunda parte del libro se aplica lo expuesto en la primera parte al desarrollo de una aplicaci on de ejemplo. Aunque los

104

CAP ITULO 6. PRUEBAS UNITARIAS CON JUNIT lenguajes de programaci on que muestran los ejemplos con .Net y Python, la aplicaci on a Java con JUnit es directa. El cap tulo 10 de [13] presenta las c omo realizar pruebas unitarias con JUnit, y en el cap tulo 12 muestra como trabajar con Cobertura para analizar el grado de cobertura de nuestras pruebas. Cobertura es otra excelente herramienta para el an alisis de cobertura de las pruebas unitarias. Otro excelente t tulo que deber a gurar en todos las estanter as de un buen desarrollador es Clean code de Robert C. Martin, tambi en conocido como uncle Bob. En el cap tulo 9 de esta referencia [10] est a dedicado a las buenas pr acticas en el dise no de test unitarios de prueba.

Cap tulo 7

Entrada y Salida
Contenidos
7.1. 7.2. 7.3. 7.4. 7.5. Flujos (Streams ) . . . . . . . . . . . . . . . . . . Flujos de bytes . . . . . . . . . . . . . . . . . . . Flujos de caracteres . . . . . . . . . . . . . . . . Conexi on entre ujos de bytes y de caracteres El sistema de cheros y ujos a cheros . . . . 7.5.1. El sistema de cheros . . . . . . . . . . . . . . 7.5.2. Flujos a cheros . . . . . . . . . . . . . . . . . 7.5.2.1. Flujos de bytes a cheros . . . . . . . 7.5.2.2. Flujos de caracteres a cheros . . . . 7.6. Serializaci on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 107 108 109 110 110 110 110 111 112

. . . .

Introducci on
Ya conocemos como denir clases que contienen datos y m etodos que nos permiten trabajar con las instancias de estas clases. En muchos casos, una vez que hemos trabajado con nuestros datos nos interesa almacenarlos de manera permanente, de tal modo que sea posible recuperar nuestros datos m as tarde para seguir trabajando sobre ellos. Todo lenguaje de programaci on proporciona una serie de mecanismos para realizar operaciones de entrada y salida de datos. Decimos que los datos son de entrada cuando llegan a nuestra aplicaci on desde una fuente de datos, y que son de salida cuando nuestra aplicaci on env a datos a alg un sumidero de datos. El lenguaje de programaci on Java nos proporciona un paquete, con una gran cantidad de clases, para poder realizar entrada/salida en nuestras aplicaciones. Ver as, que las operaciones de entrada/salida son susceptibles de lanzar gran cantidad de excepciones que vamos a tener que gestionar tal y como vimos en el cap tulo 5. La potencia de la aproximaci on de Java a las operaciones de entrada/salida es que Java utiliza un concepto transversal con independencia del dispositivo sobre el que se trabaja. Independientemente de si la salida es hacia un chero, 105

106

CAP ITULO 7. ENTRADA Y SALIDA

Figura 7.1: El concepto de ujo representado gr acamente. un Socket o una conexi on de Internet, el mecanismo de entrada/salida es el mismo: el uso de Flujos (Streams).

7.1.

Flujos (Streams )

Los ujos en Java son los canales por donde transita la informaci on. Los ujos pueden ser de entrada, de salida, o tener ambas direcciones. Utilizaremos ujos de entrada cuando a nuestras aplicaciones lleguen datos, es decir, cuando queramos leer datos que nos llegan desde alguna fuente de datos. Por el contrario, utilizaremos ujos de salida cuando nuestras aplicaciones quieran enviar datos a alg un sumidero de datos. La potencia de los ujos est a relacionada con su independencia de los dispositivos de entrada/salida a los que se est en conectando. Desde el punto de vista de nuestro c odigo, no importa que el dispositivo de salida sea una consola en pantalla, un Socket, o un chero en nuestro disco duro, el mecanismo de salida siempre es el mismo. Por otro lado, no importa que el dispositivo de entrada sea el teclado, una conexi on a una URL, o un chero en nuestro disco duro, el mecanismo de entrada siempre es el mismo. Las operaciones de entrada/salida en Java siempre se realizan a trav es de ujos que son independientes de las fuentes o sumideros de datos. En la Figura 7.1 se muestra gr acamente el concepto de ujo. En Java existen dos grandes categor as de ujos, cada una de ellas con sus propias clases para realizar operaciones de entrada salida: los ujos de bytes y los ujos de caracteres. Utilizaremos unos u otros para realizar operaciones de entrada/salida dependiendo de la naturaleza de los datos que recibamos desde una fuente de datos o enviemos a un sumidero de datos. En las siguientes secciones se va a presentar una gran cantidad de nuevas clases, lo que implica que vas a ver muchos nombres de clase nuevos. Al principio puede parecer abrumador, pero presta atenci on al nombre de las clases y ver as que es muy signicativo, veremos los detalles en la nominaci on de las clases en las siguientes secciones. Por otro lado, tambi en ver as que existe simetr a entre los nombres de las clases que realizan operaciones de lectura y las que realizan

7.2. FLUJOS DE BYTES

107

Figura 7.2: Parte de la jerarqu a de clases para ujos de bytes. operaciones de escritura. Y nalmente, tambi en ver as que existe simetr a en el nombre de las clases que correspondes a ujos de bytes y los nombres de las clases que corresponden a ujos de caracteres. Esta doble simetr a y el criterio para nominar a las clases te resultar a de gran ayuda para reconocer cual es el cometido de una clase simplemente a trav es de su nombre.

7.2.

Flujos de bytes

Los ujos de bytes nos permiten leer bytes desde una fuente de datos o escribir bytes hacia un sumidero de datos, es decir nos permiten la lectura/escritura de datos binarios. Las clases que nos permiten leer/escribir sobre ujos de bytes existen en Java desde las primeras versiones del lenguaje, y por ello, dispositivos de entrada como el teclado, o dispositivos de salida como una consola en pantalla son ambos ujos de bytes, aunque lo que nalmente se lee o escriba a ellos sean caracteres. Existe simetr a en el modo de nombrar a las clases que realizan operaciones de lectura sobre ujos de bytes y las que realizan operaciones de escritura. Si la operaci on es de lectura, el nombre de la clase contendr a la palabra Input, si el ujo es de escritura, la clase contendr a la palabra Output. Todas las clases que realizan operaciones de lectura de bytes extienden a la clase abstract InputStrem, por su lado, todas las clases que realizan operaciones de escritura de bytes extienden a la clase abstract OutputStrem. F jate que ambas clases son abstract y por lo tanto no se pueden instanciar directamente. En la Figura 7.2 se muestra algunas clases de la jerarqu a de ujos de bytes. La clase DataInputStream permite abrir un chero para leer tipos de datos primitivos, as por ejemplo, esta clase proporciona el m etodo float readFloat()

108

CAP ITULO 7. ENTRADA Y SALIDA

Figura 7.3: Parte de la jerarqu a de clases para ujos de caracteres.

que devuelve un n umero real de precisi on simple le do desde un ujo, y boolean readBoolean() que devuelve un booleano le do desde un ujo de bytes. De modo an alogo, la clase DataOutputStream permite abrir un ujo para escribir en el tipos de datos primitivos, y en este caso contamos con m etodos como void writeFloat(float f) para escribir en un ujo de byte un n umero real de precisi on sencilla y final void writeBoolean(boolean b) para escribir datos booleanos sobre un ujo de bytes. Las clases BufferedInputStream y BufferedOutputStream efect uan lectura y escritura de bytes desde un ujo utilizando una memoria intermedia (buer ) con el objeto de acelerar el proceso.

7.3.

Flujos de caracteres

La Figura 7.3 muestra una peque na parte de la jerarqu a de clases existente en Java para los ujos de caracteres. F jate en la analog a con la Figura 7.2. De nuevo, tenemos un par de clases abstractas, abstract Reader en el caso de lectura de ujos de caracteres y abstract Writer en el caso de escritura. De nuevo, y tambi en de modo an alogo a los ujos de bytes, disponemos de dos subclases que proporcionan una memoria intermedia para mejorar el rendimiento del proceso de lectura/escritura con ujos de caracteres. Estas clases son BufferedReader que nos proporciona un ujo de lectura de caracteres con buer, y BufferedWriter que nos proporciona un ujo de escritura de caracteres con buer. En el caso de la clase BufferedReader, esta clase cuenta con el m etodo String readLine() que nos permite leer cadenas de caracteres.

ENTRE FLUJOS DE BYTES Y DE CARACTERES 7.4. CONEXION

109

7.4.

Conexi on entre ujos de bytes y de caracteres

Como acabas de ver en las secciones anteriores, para un determinado tipo de ujo existe varias clases denidas en el arbol de jerarqu a, de modo que las clases hijas van a nadiendo nueva funcionalidad sobre la que proporciona la clase padre. Tomando como ejemplo los ujos de entrada de caracteres, la clase Reader proporciona m etodos para leer caracter a caracter desde alguna fuente de datos, y su clase hija BufferedReader a nade un buer intermedio en la lectura de modo que podemos leer l neas de caracteres (String) desde la fuente de datos. Por otro lado los constructores de BufferedReader son: BueredReader(Reader in) BueredReader(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 ujos en Java, obtener de alg un modo ujos de tipo b asico e ir construyendo sobre ellos nuevas instancias hasta llegar a un ujo que nos proporcione la funcionalidad que necesitamos. Por otro lado, el primer tipo de ujos que se introdujo en Java desde la versi on 1.0 fueron los ujos de bytes, y desde la versi on 1.1 aparecieron los ujos de caracteres. No es por lo tanto de extra nar que tanto el teclado, como la pantalla/consola sean ujos de bytes. Con todo esto, la pregunta que nos surge es C omo se conectan los ujos de caracteres con los ujos 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 on entre ambos tipos de ujos: InputStreamReader toma una instancia de InputStream, ujo de entrada de bytes, y sobre el que crear un ujo de lectura de caracteres. OutputStreamWriter toma una instancia de OutputStream, ujo de salida de bytes, y sobre el que crea un ujo de escritura de caracteres. El Listado 7.1 muestra un ejemplo de uso de esta t ecnica para leer cadenas de caracteres desde teclado:
1 2 3 4

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 n 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 s 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 a 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 ecnica para leer caracteres desde teclado En la l nea 1 del Listado 7.1 simplemente denimos la referencia is hacia el teclado, que es el ujo de entrada de byte desde el que queremos leer. En la l nea 2, convertimos el ujo de entrada bytes a un ujo de entrada de caracteres con la ayuda de la clase InputStreamReader, en este momento ya podr amos leer caracteres desde el teclado, pero es m as eciente utilizar una memoria intermedia.

110

CAP ITULO 7. ENTRADA Y SALIDA

En la l nea 3 estamos creando una instancia de la clase BufferedReader sobre el ujo de entrada de caracteres (InputStreamReader), para poder nalmente leer cadenas de caracteres con la ayuda del m etodo String readLine(), tal y como se muestra en la l nea 4 1 .

7.5.

El sistema de cheros y ujos a cheros

Un caso particular de fuente y sumidero de datos son los cheros. Desde nuestros programas podemos leer los datos contenidos en un chero, sean estos datos de tipo binarios o caracteres, y podemos escribir datos a un chero, sea estos datos de tipo binarios o caracteres. Como el acceso para realizar entrada/salida es independiente del dispositivo, lo que necesitaremos en este caso es alg un medio para acceder al sistema de cheros. Para ello Java nos proporciona la clase File. Esta clase nos da acceso al sistema de cheros y sobre esta clase podemos construir ujos de entrada/salida par tipos de datos tanto binarios como caracteres.

7.5.1.

El sistema de cheros

La clase File nos da acceso al sistema de cheros, con independencia del sistema operativo sobre el que se ejecute. Gracias a esta clase, podemos obtener informaci on tal como la lista de cheros o directorios bajo un directorio dado, o comprobar si el camino con el que se construye la instancia de File hace referencia a un chero o a un directorio.

7.5.2.

Flujos a cheros

Los ujos a cheros nos permiten acceder a la informaci on contenida en los cheros de nuestro sistema con el n de leer desde ellos o escribir informaci on hacia ellos. De nuevo, podemos distinguir dos tipos de ujos a cheros dependiendo de si la informaci on contenida en ellos es de tipo binario o caracteres. 7.5.2.1. Flujos de bytes a cheros

La clase FileInputStream nos permite crear un ujo de lectura hacia un chero para leer desde el datos de tipo binario. Podemos instanciar esta clase a partir de una referencia a File o bien a partir de un String que represente el camino hasta el chero. De modo an alogo, la clase FileOutputStream nos permite crear un ujo de escritura hacia un chero para escribir en el datos de tipo binario. Podemos instanciar esta clase tambi en a partir de una referencia a File o bien a partir de un String que represente el camino hasta el chero. En el momento de la creaci on del ujo podemos indicar si queremos conservar el posible contenido del chero en el momento de la creaci on del ujo a trav es de un argumento de tipo booleano en el constructor.
1 En el Cap tulo 8 se mostrar a un clase de utilidad Scanner que facilita enormemente la lectura de datos desde teclado, y en general desde cualquier ujo de entrada. No obstante lo que aqu se ha mostrado es un ejemplo del mecanismo general de conversi on entre ujos de caracteres y ujos de bytes

7.5. EL SISTEMA DE FICHEROS Y FLUJOS A FICHEROS 7.5.2.2. Flujos de caracteres a cheros

111

La clase FileReader nos permite crear un ujo de lectura hacia un chero, para leer desde el datos de tipo caracter. De modo an alogo al caso de 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 chero. Finalmente, la clase FileWriter nos permite crear un ujo de escritura de caracteres hacia un chero para escribir en el datos de tipo caracter. De modo an alogo a la clase FileOutputStream, podemos instanciar esta clase a partir de una referencia File, de un String que indique el camino al chero, e indicar en el momento de la creaci on si el chero conserva o no el posible contenido. Como ejemplo del manejo de cheros, el Listado 7.2 muestra c omo abrir un ujo a un chero de caracteres para leer desde el l nea a l nea su contenido y mostrarlo por consola.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

import import import import import

java java java java java

. . . . .

io io io io io

. BufferedReader ; . File ; . FileNotFoundException ; . FileReader ; . IOException ;

public c l a s s L e c t u r a F l u j o T e x t o { public L e c t u r a F l u j o T e x t o ( ) { super ( ) ; } private void e j e c u t a ( S t r i n g camino ) { F i l e f i c h e r o = new F i l e ( camino ) ; FileReader flujoLectura ; BufferedReader f l u j o B u f f e r = null ; try { try { 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 ) ; 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 ) ; String linea ; 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 ) { System . o u t . p r i n t l n ( l i n e a ) ; } } finally { i f ( f l u j o B u f f e r != n u l l ) flujoBuffer . close () ; } } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ] ) ; } }

Listado 7.2: Lectura desde un ujo de texto hacia un chero Como curiosidad del Listado 7.2, f jate que hay un bloque try{...} finally{...} en las l neas 17-27 que est a incluido dentro de otro bloque try{...}. El uso de estos bucles anidados facilita la lectura de c odigo, y su ejecuci on es la siguiente: si se produce alguna excepci on durante el trabajo con el chero (l neas de c odigo 18-23), se ejecutar a el bloque finally{...} con lo que se cerrar a el chero, y la excepci on se propagar a al bloque try{...} externo, que es quien tiene los bloques catch{...} para atrapar a cada una de

112

CAP ITULO 7. ENTRADA Y SALIDA

las excepciones posibles. Este modo de codicaci on es ampliamente utilizado y conviene que lo incorpores como t ecnica de escritura de tu propio c odigo.

7.6.

Serializaci on

La Serializaci on es el mecanismo por el cual Java es capaz de convertir un objeto en una secuencia de bytes. De este modo, podemos crear un ujo a partir de la secuencia de bytes que representa al objeto para escribirlo en un chero o enviarlo a trav es de un Socket por ejemplo. El concepto de serializaci on es extremadamente potente, si somos capaces de obtener una secuencia de bytes del objeto y sobre ella crear un ujo, podemos enviar el objeto a cualquier dispositivo que lo acepte. Y de modo an alogo, podr amos conectarnos a una fuente de datos a trav es de un ujo de entrada y obtener objetos desde ella. De hecho, esta t ecnica es tan potente que es la pieza sobre la que descansan otras tecnolog as Java como la invocaci on remota de m etodo (Remote Method Innvocation - RMI ), y gran parte de las tecnolog as Java en su edici on Enterprise 2 . En esta secci on vamos a presentar la serializaci on y c omo utilizarla para almacenar un objeto en un chero, y en el Cap tulo 15 veremos c omo utilizar la serializaci on para enviar y recibir objetos a trav es de un Socket. Para indicar que un objeto es Serializable debe implementar la interface Serializable. Esta interface no declara ning un m etodo, es u nicamente una marca sem antica para que Java sepa que en alg un momento podemos querer serializar el objeto. Cuando el objeto serializable se convierta en una secuencia de bytes, esta secuencia, adem as de incluir el valor de los atributos de la instancia incluye m as informaci on, como la clase de la instancia y un n umero de serie de la clase, para poder hacer un control de versiones de la clase. Por convenci on, este n umero de control se declara como private static final long serialVersionUID. El valor de este atributo lo podemos jar nosotros mismos, pero eso no garantiza que otro programador pueda utilizar el mismo n umero de serie para una clase completamente distinta. Java nos proporciona una herramienta, integrada en el JDK, que genera n umeros de versi on a partir de la clase compilada, esta herramienta es serialver, su uso es serialver [-classpath] nombreClase y un ejemplo de uso es: $ serialver -classpath . serializacion.Persona serializacion.Persona: static final long serialVersionUID = 7360368493593648647L; En este caso la herramientaserialver ha generado el n umero de serie 7360368493593648647L para la clase Persona. Y es muy improbable que serialver genere el mismo n umero de versi on para cualquier otra clase, lo que nos garantiza que este n umero de versi on es u nico e identica a la clase Persona.
2 Java 2 Enterprise Edition est a enfocada al desarrollo de aplicaciones de servidor. Son aplicaciones que se no se ejecutan en las m aquinas de un cliente, si no en un servidor remoto. Usualmente, el ciente accede al servidor a trav es de un navegador web, aunque no es la u nica opci on posible

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 atributo serialVersionUID, Eclipse nos mostrar a un aviso. Si corregimos el aviso que nos da Eclipse seleccionando la opci on Add generated serial verion ID, se a nadir a el n umero de serie obtenido con serialver. El Listado 7.3 muestra la clase Persona que implementa la interface Serializable y a la que se le ha a nadido el n umero de versi on generado con serialver.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

package s e r i a l i z a c i o n ; import j a v a . i o . S e r i a l i z a b l e ; 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 { 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 ; S t r i n g nombre ; String apellidos ; String telefono ; Persona ( ) { } 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 , t h i s . nombre = nombre ; this . a p e l l i d o s = a p e l l i d o s ; this . t e l e f o n o = t e l e f o n o ; } S t r i n g getNombre ( ) { return nombre ; } String getApellidos () { return a p e l l i d o s ; } String getTelefono () { return t e l e f o n o ; } @Override public S t r i n g t o S t r i n g ( ) { 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 = " + nombre + " , telefono =" + telefono + "]" ; } } String telefono ) {

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 omo almacenar la secuencia de bytes obtenida en un chero para su recuperaci on posterior.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

package s e r i a l i z a c i o n ; import import import import import import java java java java java java . . . . . . io io io io io io . FileNotFoundException ; . FileOutputStream ; . IOException ; . ObjectOutput ; . ObjectOutputStream ; . OutputStream ;

public c l a s s E s c r i t o r P e r s o n a { public E s c r i t o r P e r s o n a ( ) { super ( ) ; } private void e j e c u t a ( S t r i n g fichero ) {

114
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

CAP ITULO 7. ENTRADA Y SALIDA

OutputStream s t r e a m E s c r i t u r a ; 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 ; try { try { 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 ) ; 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 ) ; 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 " ) ) ; } finally { 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 ( ) ; } } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ] ) ; } }

Listado 7.4: Serializaci on de la clase Persona hacia un chero de bytes Si ejecutamos el programa Java anterior, introduciendo por l nea de instrucciones como nombre de chero persona.ser 3 se crear a un chero de tipo 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 omo leer un objeto serializado almacenado en un chero de tipo binario.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

package s e r i a l i z a c i o n ; import import import import import import java java java java java java . . . . . . io io io io io io . FileInputStream ; . FileNotFoundException ; . IOException ; . InputStream ; . ObjectInput ; . ObjectInputStream ;

public c l a s s L e c t o r P e r s o n a { public L e c t o r P e r s o n a ( ) { super ( ) ; } private void e j e c u t a ( S t r i n g f i c h e r o ) { InputStream streamLectura ; ObjectInput streamLecturaPersona = null ; try { try { 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 ) ; 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 ) ; Persona persona = ( Persona ) streamLecturaPersona . readObject ( ) ; System . o u t . p r i n t l n ( p e r s o n a ) ; } finally { 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 ( ) ; } } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } catch ( C l a s s N o t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; }
3 Por convenci on, para los cheros que contienen datos de objetos serializados se utiliza la extensi on .ser

7.6. SERIALIZACION
34 35 36 37 38 39 40

115

} public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ] ) ; } }

Listado 7.5: Des-serializaci on de la clase Persona desde un chero de bytes F jate c omo, de nuevo, se han utilizado bloques try{...} anidados para facilitar la gesti on de excepciones. En el cap tulo 15 se mostrar a otro ejemplo de serializaci on de objetos pero esta vez se enviar a la secuencia de bytes que representa al objeto a trav es de los ujos que proporciona un Socket, con lo que podremos escribir un servidor capaz de enviarnos, a trav es de una red de comunicaci on de datos, objetos serializados y podremos recuperarlos en un cliente.

Ejercicios
1. Amplia la funcionalidad de la aplicaci on de la agenda, para que sea posible almacenar los datos de los contactos en un chero de texto para su posterior recuperaci on. Escribe tanto el c odigo de escritura como de lectura. 2. Sigue ampliando la funcionalidad de tu aplicaci on de la agenda para que sea posible serializarla a un chero de tipo binario para su posterior recuperaci on. Escribe tanto el c odigo de escritura como el de lectura.

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 en todo el Cap tulo 14 al estudio del mecanismo de entrada/salida en Java. En particular las secciones dedicadas a la serializaci on de objetos son muy interesantes.

116

CAP ITULO 7. ENTRADA Y SALIDA

Cap tulo 8

Algunas clases de utilidad del paquete est andar


Contenidos
8.1. La clase Scanner . . . . . . . . . . . . . . . . . 8.2. Trabajo con cadenas de caracteres . . . . . . 8.2.1. La clase String . . . . . . . . . . . . . . . . . 8.2.2. Las clases StringBuffer y StringBuilder . . 8.3. Clases recubridoras . . . . . . . . . . . . . . . 8.4. Colecciones . . . . . . . . . . . . . . . . . . . . 8.5. Trabajo con fechas . . . . . . . . . . . . . . . . 8.5.1. La clase Date . . . . . . . . . . . . . . . . . . 8.5.2. Las clases Calendar y GregorianCalendar . . 8.6. Matem aticas . . . . . . . . . . . . . . . . . . . 8.6.1. La clase Math . . . . . . . . . . . . . . . . . . 8.6.2. La clase Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 120 120 121 122 124 128 128 129 129 129 130

. .

. . . .

Introducci on
La edici on est andar de Java proporciona una ampl sima colecci on de clases ya denidas. Estas clases ya denidas son de gran utilidad, a medida que como programadores vamos conociendo nuevas clases de esta colecci on nuestra productividad aumenta. En este Cap tulo presentamos un peque no grupo de clases de gran utilidad, aunque existen otras muchas tan u tiles como las aqu presentadas. Obviamente, por cuesti on de espacio, s olo presentamos las que creemos m as interesantes llegados a este punto. 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 ujos de bytes. Esto se debe a que en la versi on 1.0 de Java no exist an ujos de caracteres. Para guardar la compatibilidad hacia atr as, tanto el teclado como la consola siguen siendo ujos de caracteres en las versiones actuales de Java. Como nalmente lo que nos interesa leer desde el teclado son caracteres, tenemos que aplicar la t ecnica del recubrimiento que vimos en el Cap tulo 7 para conseguir leer cadenas de caracteres. El uso de la clase de utilidad Scanner nos oculta los ujos y nos permite leer directamente de ujos, en particular desde el teclado. La clase Scanner est a presente en Java desde la versi on 5. Esta clase permite 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 es de un ujo. Supongamos que tenemos un chero de texto que almacena datos de una agenda de tel efonos con el formato Persona: Nombre: Apellidos: Telefono, un ejemplo de contenido del chero es2 : Persona: Persona: Persona: Persona: Persona: LUISA: GARCIA MORENO: 313372295 ROSARIO: GONZALEZ ESTEBAN: 560248322 MANUEL: SANZ GARCIA: 571365702 FRANCISCO: VAZQUEZ FERRER: 690109651 VICTOR: MU~ NOZ LOPEZ: 500661266

El Listado 8.1 muestra un ejemplo de uso de la clase Scanner para leer l nea a l nea de este chero. El m etodo hasNext() nos sirve para comprobar si hay m as elementos a devolver.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

package u t i l i d a d ; import j a v a . i o . F i l e ; 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 ; import j a v a . u t i l . S c a n n e r ; 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 { 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 " ; private U t i l i d a d S c a n n e r ( ) { super ( ) ; } private void e j e c u t a ( ) { try { 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) ) ; while ( s c a n n e r . hasNext ( ) ) 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 ( ) ) ; } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { new U t i l i d a d S c a n n e r ( ) . e j e c u t a ( ) ; }

m as informaci on sobre qu e son expresiones regulares el lector puede consultar http: //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 on http://www.ine.es

1 Para

8.1. LA CLASE SCANNER


26 27

119

Listado 8.1: Lectura de l neas de texto desde un chero con el uso de la clase Scanner La clase Scanner tiene m etodos para poder leer tipos de datos primitivos tal y como se muestra en el Listado 8.2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

package u t i l i d a d ; import j a v a . i o . F i l e ; 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 ; import j a v a . u t i l . S c a n n e r ; 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 { 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 " ; private U t i l i d a d S c a n n e r 2 ( ) { super ( ) ; } private void e j e c u t a ( ) { try { 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) ) ; while ( s c a n n e r . hasNext ( ) ) { analizaLinea ( scanner . nextLine () ) ; } } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; } } 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 ) { 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 ) ; scanner . useDelimiter ( ": " ) ; S t r i n g p e r s o n a , nombre , a p e l l i d o s ; int t e l e f o n o ; persona = scanner . next ( ) ; nombre = s c a n n e r . n e x t ( ) ; a p e l l i d o s = scanner . next ( ) ; t e l e f o n o = scanner . nextInt () ; 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 ) ; } public s t a t i c void main ( S t r i n g a r g s [ ] ) { new U t i l i d a d S c a n n e r 2 ( ) . e j e c u t a ( ) ; } }

Listado 8.2: Lectura de l neas de texto desde un chero con el uso de la clase Scanner El resultado de la ejecuci on de este c odigo es el siguiente: 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 ujo de bytes que representa al teclado System.in, con la clase Scanner podremos leer tipos de datos primitivos, tal y como muestra el Listado 8.3.

120CAP ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR

1 2 3 4 5 6

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 ) ; 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 : " ) ; int entero = l e c t o r T e c l a d o . nextInt ( ) ; 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 : " ) ; float r e a l = lectorTeclado . nextFloat () ; 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 ujo de entrada, no s olo 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 as ecientes para trabajar con 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 on de objetos. A efectos pr acticos la inmutabilidad de la clase String signica 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 on de dos cadenas crea una nueva cadena. 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 as y el tercero contiene Hola, como est as.

8.2.1.

La clase String

La clase String representa una cadena de caracteres inmutable, es decir, una vez creada no se puede modicar la secuencia de caracteres. Por la tanto es u til utilizar esta clase cuando no vamos a hacer manipulaciones continuadas sobre la cadena que representa. El u nico operador sobrecargado en Java es el operador + cuando se aplica sobre cadenas con el signicado 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 ineciencia al utilizar el operador + sobre String.
1 2 3

S t r i n g primera = " Hola " ; S t r i n g segunda = " m u n d o . " 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 etodo public boolean equals(Object o), que compara el String actual con la representaci on como String del objeto que se le pasa como argumento. El

8.2. TRABAJO CON CADENAS DE CARACTERES

121

m etodo equals(Object o) distingue entre may usculas y min usculas, si queremos comparar dos cadenas con independencia del uso de may usculas/min usculas utilizaremos el m etodo public boolean equalsIgnoreCase(String s). Para averiguar el n umero de caracteres de una cadena utilizamos el m etodo public int length(). Si queremos convertir todas las letras de una cadena a min usculas utilizamos el m etodo public String toLowerCase(), y el m etodo public String toUpperCase en caso de que la queramos en may usculas. La clase String tambi en posee el m etodo sobrecargado static String valueOf(boolean/char/int/long/float/double) para convertir tipos de datos primitivos a su representaci on como cadenas de caracteres. Un m etodo interesante que nos permite trocear una cadena de caracteres a partir de una subcadena contenida en ellas es String split(String s), donde el argumento es una expresi on regular. El Listado 8.6 muestra un ejemplo de uso del m etodo split, f jate que estamos dividiendo la cadena original buscando el patr on representado por otra cadena, ", ".
1 2 3 4

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 ." ; String trozos [ ] = i n i c i a l . s p l i t (", ") ; for ( String trozo : t r o z o s ) System . o u t . p r i n t l n ( t r o z o ) ;

Listado 8.6: Uso del m etodo split para trocear una cadena. El resultado de la ejecuci on de este c odigo es: Esta cadena contiene comas por la que quiero trocear. Para poder dar formato a cadenas al estilo de C, la clase String nos proporciona el m etodo public static String format(String cadena, Object... 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 etodo format de la clase String El resultado de la ejecuci on del Listado 8.7: El valor de PI es: 3,14 Si se necesitan formatos m as sosticados, la clase Formatter es de gran ayuda.

8.2.2.

Las clases StringBuffer y StringBuilder

La clase StringBuffer tambi en representa una cadena de caracteres como la clase String pero esta vez la cadena que representa puede cambiar. 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 modica la original para representar la cadena concatenada nal. Para ello la clase StringBuffer posee el m etodo sobrecargado StringBuffer

122CAP ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR append(boolean/int/long/float/double/String/StringBuffer) que a nade la representaci on como String del argumento a la cadena actual. La clase StringBuffer posee otros m etodos interesantes de manipulaci on. Por ejemplo, el m etodo int indexOf(String s) devuelve la posici on de la primera ocurrencia de la cadena s dentro de la cadena original. El m etodo sobrecargado StringBuffer insert(int offset, boolean/char/int/long/float/double/String) inserta la representaci on del segundo argumento en la cadena original a partir del oset indicado en el primer argumento. El Listado 8.8 muestra un ejemplo de uso de estos m etodos.
1 2 3

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 . " ) ; sb . i n s e r t ( sb . i n d e x O f ( " . " ) , " J a v a " ) ; System . o u t . p r i n t l n ( sb ) ;

Listado 8.8: Uso de los m etodos indexOf y insert de la clase StringBuffer Los m etodos que manipulan la representaci on de la cadena dentro de la clase StringBuffer est an sincronizados, luego se pueden utilizar en aplicaciones en las que varios hilos est an accediendo a la misma referencia de la clase StringBuffer. Veremos el uso de hilos y lo que signica que un m etodo est e sincronizado en el Cap tulo 14. Por su parte, la clase StringBuilder funciona exactamente igual que la clase StringBuffer, de hecho los m etodos que proporciona la clase StringBuilder son exactamente los mismo que la clase StringBuffer, pero esta vez ninguno de ellos est a sincronizado por razones de eciencia3 .

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 proporciona clases que recubren los tipos de datos primitivos para poder trabajar con ellos a trav es de referencias, es decir, como con cualquier otro objeto. Esto es especialmente u til al trabajar con colecciones, tal y como veremos en la Secci on 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 on como cadena de texto de los datos primitivos.
1 2 3 4

Integer Integer Boolean Boolean

e n t e r o = new I n t e g e r ( 1 5 ) ; e n t e r o S t r i n g = new I n t e g e r ( " 1 0 " ) ; 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 ) ; 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 on de clases recubridoras. Para recuperar, como tipos primitivos, los valores que almacena una clase recubridora, estas proporcionan m etodos tal y como muestra el Listado 8.10
3 Como veremos en el Cap tulo 14, el acceso a m etodos sincronizados tiene un sobrecoste temporal debido al uso de cerrojos.

8.3. CLASES RECUBRIDORAS Tipo primitivo void boolean char byte short int long oat double Clase recubridora java.lang.Void java.lang.Boolean java.lang.Character java.lang.Byte java.lang.Short java.lang.Integer java.lang.Long java.lang.Float java.lang.Double

123

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 2

int ente roPrimit ivo = entero . intValue ( ) ; 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 on de los datos como tipos primitivos a partir de las clases recubridoras. Sin embargo, en la mayor a de los casos es de gran utilidad hacer uso del mecanismo de Autoboxing introducido en la versi on 5 de Java. Este mecanismo convierte, de modo autom atico y transparente para el usuario, tipos primitivos a recubridores en una asignaci on siempre que el tipo primitivo y la clase recubridora sean compatibles, tal y como muestra el Listado 8.11.
1 2 3 4

Integer entero = 15; int ente roPrimit ivo = entero ; 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 ; 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 on entre tipos de datos primitivos y sus correspondientes clases recubridoras. El mecanismo de Autoboxing es especialmente u til cuando se utilizan las clases colecci on, tal y como veremos en la Secci on 8.4.
1 2

int e n t e r o = I n t e g e r . p a r s e I n t ( " 10 " ) ; 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 etodos para obtener tipos primitivos a partir de cadenas de caracteres. Un grupo de m etodos especialmente u til cuando se procesan datos de entrada de tipo texto, como por ejemplo los par ametros de llamada a nuestra aplicaci on, y los queremos convertir a tipos primitivos son los que muestra el Listado 8.12, de modo an alogo cada clase recubridora, tiene un m etodo para realizar la correspondiente conversi on.

124CAP ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR

Figura 8.1: Interfaces b asicos presentes en el Java Collections Framework. Interface Set< E > SortedSet< E > List< E > Queue< E > Map< E > SortedMap< E > Implementaci on HashSet< E > TreeSet< E > ArrayList< E >, LinkendList< E >, Vector< E > LinkendList< E > HasMap< E >, HashTable< E > TreeMap< E >

Tabla 8.2: Varias clases colecci on y los interfaces que implementan.

8.4.

Colecciones

Las clases que forman parte del grupo de clases colecci on son aquellas que nos sirven para almacenar referencias a objetos, e implementan estructuras de datos tales como listas, tablas de dispersi on, conjuntos, etc etera. El conjunto de interfaces, clases y algoritmos que nos permiten trabajar con estructuras de datos se agrupan bajo el Java Collections Framework. La Figura 8.4 muestra los interfaces b asicos denidos en el Java Collections Framework. La parte de la derecha de esta gura muestra los interfaces que representan la funcionalidad de las clase que implementan colecciones de referencias como ((conjuntos)); mientras que en la parte derecha de la gura se muestran los interfaces que representan la funcionalidad de las clases que implementen colecciones donde cada elemento de la colecci on representa una pareja clave/valor. Las clases que representan colecciones de referencias pueden implementar uno o m as de estos interfaces. La Tabla 8.2 muestra algunas clases colecci on y los interfaces que implementan. F jate que en la Tabla 8.2 hay una novedad, los s mbolos < E > a continuaci on de las interfaces o las clases, esta sintaxis signica que la interface o la clase almacena un tipo Gen erico. En el momento de creaci on de la clase debemos especicar cual es el tipo de los elementos que va a contener la clase. Veremos con detalle c omo utilizar gen ericos en nuestra propias clases en el Cap tulo 9. En

8.4. COLECCIONES

125

este cap tulo s olo veremos como trabajar con ellos en el momento de creaci on de las clases y las ventajas que supone el uso de gen ericos, que fueron introducidos en la versi on 5 de Java. Aunque, es posible instanciar las clases colecci on sin especicar el tipo de los elementos que contendr a. En este caso, el compilador s olo mostrar a un aviso con el siguiente texto ArrayList is a raw type. References to 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 on. En particular, la clase ArrayList< E > representa una secuencia indexada de elementos, cada uno de ellos ocupa una posici on dentro de la estructura, y se puede acceder a un elementos dentro de la estructura a trav es de su ndice. Un ejemplo de c omo usar las clases colecci on se muestra en los siguientes listados: el Listado 8.13 muestra la interface Figura que declara un m etodo para que las clases que la implementen denan la funcionalidad del c alculo del area de una gura. El Listado 8.14 dene la clase Circulo de cuyas instancias podemos calcular el area. An alogamente, el Listado 8.15 dene la clase Rectangulo de cuyas instancias tambi en podemos calcular el area. Finalmente, el Listado 8.16 dene la clase TrianguloRectangulo de cuyas instancias tambi en podemos calcular el area. El Listado 8.17 muestra un ejemplo de c omo utilizar la clase ArrayList< F igura > para contener referencias a clases que implementen la interface Figura, a ella podemos a nadir c rculos, cuadrados y tri angulos, y los podemos recuperar utilizando un bucle for...each.
1 2 3 4 5 6

package c o l e c c i o n e s . f i g u r a s ; public i n t e r f a c e F i g u r a { public s t a t i c f i n a l double PI = 3 . 1 4 1 5 9 2 ; public double g e t A r e a ( ) ; }

Listado 8.13: Interface que declara la funcionalidad del c alculo del area de una gura geom etrica.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

package c o l e c c i o n e s . f i g u r a s ; public c l a s s C i r c u l o implements F i g u r a { private double r a d i o ; public C i r c u l o ( ) { super ( ) ; } public C i r c u l o ( double r a d i o ) { this . radio = radio ; } @Override public double g e t A r e a ( ) { return PI r a d i o r a d i o ; } @Override public S t r i n g t o S t r i n g ( ) { 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 ( ) ; b u i l d e r . append ( " C i r c u l o [ r a d i o = " ) ; b u i l d e r . append ( r a d i o ) ; b u i l d e r . append ( " ] " ) ; b u i l d e r . append ( " A r e a = " ) ; b u i l d e r . append ( g e t A r e a ( ) ) ; return b u i l d e r . t o S t r i n g ( ) ; }

126CAP ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR


29

Listado 8.14: Esta clase representa un c rculo del que se puede calcular su area.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

package c o l e c c i o n e s . f i g u r a s ; public c l a s s R e c t a n g u l o implements F i g u r a { private double b a s e ; private double a l t u r a ; public R e c t a n g u l o ( ) { super ( ) ; } public R e c t a n g u l o ( double base , double a l t u r a ) { super ( ) ; this . base = base ; this . altura = altura ; } @Override public double g e t A r e a ( ) { return b a s e a l t u r a ; } @Override public S t r i n g t o S t r i n g ( ) { 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 ( ) ; b u i l d e r . append ( " R e c t a n g u l o [ a l t u r a = " ) ; b u i l d e r . append ( a l t u r a ) ; b u i l d e r . append ( " , b a s e = " ) ; b u i l d e r . append ( b a s e ) ; b u i l d e r . append ( " ] " ) ; b u i l d e r . append ( " A r e a = " ) ; b u i l d e r . append ( g e t A r e a ( ) ) ; return b u i l d e r . t o S t r i n g ( ) ; } }

Listado 8.15: Esta clase representa un rect angulo del que se puede calcular su area.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

package c o l e c c i o n e s . f i g u r a s ; 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 { private double b a s e ; private double a l t u r a ; public T r i a n g u l o R e c t a n g u l o ( ) { super ( ) ; } 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 ) { super ( ) ; this . base = base ; this . altura = altura ; } @Override public double g e t A r e a ( ) { return b a s e a l t u r a / 2 ; } @Override public S t r i n g t o S t r i n g ( ) { 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 ( ) ; 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 = " ) ; b u i l d e r . append ( a l t u r a ) ; b u i l d e r . append ( " , b a s e = " ) ;

8.4. COLECCIONES
28 29 30 31 32 33 34

127

builder builder builder builder return } }

. append ( b a s e ) ; . append ( " ] " ) ; . append ( " A r e a = " ) ; . append ( g e t A r e a ( ) ) ; builder . toString () ;

Listado 8.16: Esta clase representa un tri angulo rect angulo del que se puede calcular su area.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

package c o l e c c i o n e s . f i g u r a s ; import j a v a . u t i l . A r r a y L i s t ; public f i n a l c l a s s P r i n c i p a l { public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 > () ; f i g u r a s . add ( new C i r c u l o ( 1 ) ) ; f i g u r a s . add ( new R e c t a n g u l o ( 1 , 2 ) ) ; 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 ) ) ; for ( Figura f i g u r a : f i g u r a s ) System . o u t . p r i n t l n ( f i g u r a ) ; } }

Listado 8.17: Ejemplo de uso de la clase ArrayList< F igura >. La ejecuci on de esta peque na aplicaci on muestra el siguiente resultado: 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 asemos a nadir una instancia de una clase que no implementa el interface Figura, obtendr amos un error en tiempo de compilaci on. Si no indic asemos el tipo de los datos que maneja la colecci on en el momento de la creaci on del ArrayList, el compilador no hubiese detectado el error, y se producir a en tiempo de compilaci on al extraer el elemento err oneo y modelarlo a la interface com un Figura, tal y como muestra el Listado 8.18 y el resultado de su ejecuci on.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

package c o l e c c i o n e s . f i g u r a s ; import j a v a . u t i l . A r r a y L i s t ; public c l a s s P r i n c i p a l 2 { public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) ; f i g u r a s . add ( new C i r c u l o ( 1 ) ) ; f i g u r a s . add ( new R e c t a n g u l o ( 1 , 2 ) ) ; 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 ) ) ; f i g u r a s . add ( new I n t e g e r ( 1 ) ) ; for ( Object f i g u r a : f i g u r a s ) System . o u t . p r i n t l n ( ( F i g u r a ) f i g u r a ) ; } }

Listado 8.18: Ejemplo de uso de la clase ArrayList sin especicar el tipo de los elementos de la colecci on. Se producir a un error en tiempo de ejecuci on.

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 etodos u tiles de la clase ArrayList< E > (que comparte con el resto de clases que implementan la interface List) son: E get(int posicion), devuelve el elementos en la posici on indicada; void clear() elimina todos los elementos; boolean contains(Object o), de vuelve true si el elementos est a en la lista y false en caso contrario; boolean isEmpty(), devuelve true si no el ArrayList< E > no contiene ning un elemento y false en caso contrario; int size(), devuelve el n umero de elementos.

8.5.

Trabajo con fechas

El paquete de clases de utilidad de Java nos proporciona un conjunto de clases para trabajar con fechas y especicar 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 on de milisegundos. Para obtener el instante de tiempo actual, simplemente creamos una instancia de esta clase a partir de su constructor por defecto. La informaci on se 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 etodos de esta clase est an obsoletos, en particular, todos aquellos m etodos relacionados con el trabajo con fechas. Para trabajar con fechas, por ejemplo para saber qu e d a de la semana ser a mi pr oximo cumplea nos, se utiliza la clase GregorianCalendar tal y como veremos en la Secci on 8.5.2. Un sencillo ejemplo de uso de la clase Date se muestra en el Listado 8.19, lo que mostrar a por consola: Ahora: Fri Jul 02 10:19:40 CEST 2010 donde se est a utilizando un formato anglosaj on para mostrar la fecha.
1

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 denir el formato con el que queremos mostrar las fechas. Para una descripci on de los s mbolos que se pueden utilizar al especicar el formato, se puede consultar la documentaci on de esta clase en esta direcci on http://java.sun.com/javase/6/docs/api/java/ text/SimpleDateFormat.html, aqu s olo mostraremos el ejemplo del Listado 8.20, donde se est a utilizando EEEE para mostrar el d a de la semana, dd para mostrar el ordinal del d a dentro del mes, MMMM para mostrar el nombre

8.6. MATEMATICAS

129

del mes completo, yyyy para mostrar el a no, hh para mostrar la hora en formato 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 2

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 ) " ) ; 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 denir 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 as, meses, a no, horas, minutos, 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 alculo de fechas en el calendario gregoriano. Esta clase nos permite, por ejemplo, saber qu e d a de la semana ser a dentro de 40 d as, tal y como muestra el Listado 8.21.
1 2 3 4 5

SimpleDateFormat s d f = new SimpleDateFormat ( " E E E E d d d e M M M M d e yyyy " ) ; 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 ( ) ; 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 ( ) ) ) ; c a l e n d a r i o . add ( C a l e n d a r . DAY OF YEAR, 4 0 ) ; System . o u t . p r i n t l n ( " D e n t r o d e 4 0 d as : " + s d f . format ( 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 as a la fecha actual para conocer la nueva fecha Todas las constantes para indicar el d a, mes, etc etera est an denidas en la clase Calendar.

8.6.

Matem aticas

En el paquete est andar de Java encontramos algunas clases para hacer c alculos matem aticos, algunas de ellas son la clase Math y la clase Random.

8.6.1.

La clase Math

La clase Math nos proporciona algunas de las funciones matem aticas trigonom etricas, logar tmicas y otras de utilidad. Todos los m etodos de esta clase 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 aticas que proporciona la clase Math.

130CAP ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR

1 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 s e n o d e % 2 . 2 f e s % 1 . 3 f " , Math . PI / 4 , Math . s i n ( Math . PI / 4 ) ) ) ; 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 de 2 es %1.4 f " , Math . s q r t ( 2 ) ) ) ; 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 on del Listado 8.22 es el siguiente: 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 umeros o valores booleanos. La secuencia de n umeros es reproducible si al crear la instancia de Random utilizamos la misma semilla. Los valores de las secuencias son equiprobables, excepto si utilizamos el m etodo double nextGaussian(), en cuyo caso la secuencia generada sigue una distribuci on aleatoria de media 0 y desviaci on est andar 1. El Listado 8.23 muestra algunos ejemplos de uso de los m etodos que proporciona esta clase.
1 2 3 4 5 6 7 8

Random e q u i p r o b a b l e s = new Random ( ) ; 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 umeros entre 0 y 100. " ) ; f o r ( i n t i = 0 ; i < 1 0 ; i ++) 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 ) + " ; " ) ; 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 umeros ." ) ; f o r ( i n t i = 0 ; i < 1 0 ; i ++) 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 as que aunque esta clase es abstracta, posee el m etodo public static Calendar getInstance() que devuelve una referencia a Calendar perfectamente funcional y con la que podemos trabajar con fechas como si de la clase GregorianCalendar se tratase. C omo es esto posible siendo la clase Calendar abstracta?.

Ejercicios.
1. Escribe un programa que calcule la cuota mensual de una hipoteca por el sistema franc es, dada por la siguiente f ormula: mensualidad = capital n 1 1 os) (1 + n)(12an

8.6. MATEMATICAS donde n = interes . 1200

131

Lecturas recomendadas.
El Cap tulo 17 de la referencia [2] muestra nuevas clases de utilidad.

132CAP ITULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTANDAR

Cap tulo 9

Programaci on con gen ericos


Contenidos
9.1. 9.2. 9.3. 9.4. Qu e son los tipos de datos gen ericos? . . . . . . . 133 M etodos gen ericos . . . . . . . . . . . . . . . . . . . 134 Clases gen ericas . . . . . . . . . . . . . . . . . . . . 135 Ampliaci on del tipo gen erico . . . . . . . . . . . . 138 9.4.1. Tipos gen ericos con l mite superior . . . . . . . . . . 139 9.4.2. Comodines . . . . . . . . . . . . . . . . . . . . . . . 139 9.5. Borrado de tipo y compatibilidad con c odigo heredado . . . . . . . . . . . . . . . . . . . . . . . . . . 141

Introducci on
En este cap tulo se presenta la programaci on con gen ericos, que fue introducida en Java en la versi on 5. Los gen ericos nos permiten denir clases que trabajar an con instancias de objetos de los que no especicamos su tipo en el momento de la denici on de la clase. El tipo de las referencias que la clase manejar a se especica en el momento de crear las instancias de la clase gen erica. El concepto de programaci on con gen ericos es muy potente, como has visto en la secci on 8.4 dedicada a las clases colecci on. Las clases colecci on son contenedores de referencias de las que se especica su tipo en el momento de crear la instancia de la clase colecci on. Como vimos, una ventaja del uso de gen ericos es asegurar que las referencias con las que trabaja la clase gen erica son de tipo compatible con el especicado en el momento de la instanciaci on de la clase gen erica, de lo contrario, obtendremos un error en tiempo de compilaci on, error que pasar a desapercibido sin el uso de tipos gen ericos.

9.1.

Qu e son los tipos de datos gen ericos?

Sin detenernos en detalles, en la secci on 8.4 vimos c omo utilizar las clases colecci on, y vimos que estas clases pueden trabajar con cualquier tipo de datos, basta con indicarlo en el momento de instanciar la clase colecci on. Vimos la 133

134

CON GENERICOS CAP ITULO 9. PROGRAMACION

gran ventaja que esto supon a al no trabajar con referencias a la clase Object que deb amos modelar al tipo adecuado al extraer los elementos de la colecci on. Al trabajar sin gen ericos podemos cometer errores (introducir una referencia de tipo incompatible con el resto de elementos en la colecci on), de los que no nos daremos cuenta en tiempo de compilaci on, causando graves problemas durante la ejecuci on de nuestras aplicaciones. El uso de gen ericos hace posible la detecci on de estos errores de incompatibilidad de tipos durante la fase de compilaci on haciendo que nuestro c odigo sea m as robusto. Un tipo de datos gen erico es un tipo de datos que no se especica, u nicamente se indica que se utilizar a alg un tipo de dato pero no se indica el tipo concreto hasta que no se utiliza. Los tipos de datos gen ericos se pueden utilizar en la denici on de un m etodo, o en la denici on de una clase.

9.2.

M etodos gen ericos

Un m etodo denido en una clase puede trabajar con un tipo gen erico aunque la clase no lo haga. El Listado 9.1 muestra un ejemplo de un m etodo que trabaja con un tipo gen erico. F jate en la declaraci on del m etodo private <T>void muestraNombreClase(T t), la <T> indica que se va a utilizar un tipo gen erico, de modo que la lista de argumentos (T t) se interpreta como una referencia de tipo gen erico T. El modo de uso es una simple llamada al m etodo como por ejemplo metodoGenerico.muestraNombreClase(new Float(1));, en este caso, el tipo gen erico <T> se sustituye por el tipo particular Float, de modo que el m etodo se reescribe como private void muestraNombreClase(Float t).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

package g e n e r i c o s ; import t i p o s . P e r s o n a ; public f i n a l c l a s s MetodoGenerico { private MetodoGenerico ( ) { super ( ) ; } private <T> void muestraNombreClase (T t ) { 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 ( ) ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { MetodoGenerico m e t o d o G e n e r i c o = new MetodoGenerico ( ) ; m e t o d o G e n e r i c o . muestraNombreClase ( new F l o a t ( 1 ) ) ; m e t o d o G e n e r i c o . muestraNombreClase ( new P e r s o n a ( ) ) ; } }

Listado 9.1: Denici on y uso de un m etodo gen erico.

Sintaxis de Java Para indicar que un m etodo trabaja con tipos gen ericos se escribe < T > entre el modicador de acceso y el tipo de dato de retorno del m etodo. Lo que ocurre en el momento de la compilaci on en el ejemplo anterior, es que se sustituye el s mbolo de tipo gen erico <T> por el tipo concreto (Float) en

9.3. CLASES GENERICAS

135

el ejemplo del Listado 9.1. Si el m etodo trabaja con un par de tipos gen ericos que pueden ser diferentes se indica como private <T, U> void metodo(T t, U u). Convenci on de codicaci on Se usa la letra <T> para indicar el primer tipo gen erico, si es necesario indicar m as tipos gen ericos se toman las letras may usculas que siguen a T, por ejemplo <T, U>. La convenci on utiliza las siguientes letras para indicar tipos gen ericos: E, Indica Elemento, como en el caso de las clase Colecci on. 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 ericas

Supongamos que queremos denir 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 en sensores de viento que miden la intensidad y direcci on del viento, por lo que no podemos decidir en el momento de la denici on de la clase Sensor la naturaleza del dato que representa la medida. C omo lo podemos resolver? Evidentemente, utilizando gen ericos. La idea es dejar sin especicar el tipo de datos que representa la medida tal y como se muestra en el Listado 9.2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

package s e n s o r e s ; public abstract c l a s s S e n s o r <T> { protected T medida ; private S t r i n g d e s c r i p c i o n ; public T g e t M e d i c i o n ( ) { return medida ; } 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 ) { this . d esc ri pc ion = d esc ri pci on ; } public S t r i n g g e t D e s c r i p c i o n ( ) { return d e s c r i p c i o n ; } }

Listado 9.2: La clase Sensor no especica 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 especicar como un n umero real, elegimos la clase Float para representar

136

CON GENERICOS CAP ITULO 9. PROGRAMACION

la medida de temperatura de modo que nuestra clase SensorTemperatura la podemos denir tal y como muestra el Listado 9.3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

package s e n s o r e s ; import j a v a . u t i l . Random ; 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 > { private s t a t i c f i n a l f l o a t TEMPERATURA MAXIMA = 4 5 ; private Random t e m p e r a t u r a = new Random ( ) ; public S e n s o r T e m p e r a t u r a ( ) { super ( ) ; 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 ( ) ) ; setDescripcion () ; } public f i n a l void s e t D e s c r i p c i o n ( ) { 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 " ) ; } }

Listado 9.3: La clase SensorTemperatura dene un sensor capaz de registrar temperaturas representadas como Float Para que se vea con mayor claridad el uso de gen ericos, vamos a completar el ejemplo deniendo un nuevo sensor, esta vez un sensor de velocidad del viento capaz de tomar datos de la intensidad y direcci on del viento. Esta vez ning un tipo de dato primitivo nos sirve para representar la medida de la velocidad del viento, ya que este tiene intensidad y direcci on, as que denimos una nueva clase que represente la medida de la velocidad del viento, tal y como muestra el Listado 9.4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

package s e n s o r e s ; public c l a s s V e l o c i d a d V i e n t o { private f l o a t i n t e n s i d a d ; private D i r e c c i o n d i r e c c i o n ; public c l a s s D i r e c c i o n { f l o a t vx ; f l o a t vy ; public D i r e c c i o n ( f l o a t vx , t h i s . vx = vx ; t h i s . vy = vy ; } f l o a t vy ) {

public S t r i n g t o S t r i n g ( ) { return " [ " + vx + " , " + vy + " ] " ; } } public V e l o c i d a d V i e n t o ( ) { super ( ) ; } 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 , this . intensidad = intensidad ; d i r e c c i o n = new D i r e c c i o n ( vx , vy ) ; } public double g e t I n t e n s i d a d ( ) { return i n t e n s i d a d ; } public D i r e c c i o n g e t D i r e c c i o n ( ) { f l o a t vx , f l o a t vy ) {

9.3. CLASES GENERICAS


35 36 37 38 39 40 41 42

137

return d i r e c c i o n ; } @Override public S t r i n g t o S t r i n g ( ) { 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 on : " + direccion ; } }

Listado 9.4: La clase VelocidadVieto dene una clase que almacena la intensidad y la direcci on de una medida del viento. Con esta nueva clase, la denici on de un sensor de velocidad del viento es la mostrada en el Listado 9.5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

package s e n s o r e s ; import j a v a . u t i l . Random ; 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 ; 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 > { private s t a t i c f i n a l f l o a t VELOCIDAD MAXIMA = 1 0 0 ; private Random v i e n t o = new Random ( ) ; public S e n s o r V e l o c i d a d V i e n t o ( ) { super ( ) ; setMedida ( ) ; setDescripcion () ; } public f i n a l void s e t D e s c r i p c i o n ( ) { 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 on del viento . " ) ; } private f i n a l void s e t M e d i d a ( ) { 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 ) ; f l o a t s e n o = ( f l o a t ) ( Math . s i n ( a n g u l o ) ) ; 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 ) ) ; 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 ) ; } }

Listado 9.5: La clase SensorVelocidadVieto dene 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 modica durante la ejecuci on de la aplicaci on. Cuando veamos en el Cap tulo 14 c omo trabajar con Hilos en Java, reescribiremos el c odigo de esta aplicaci on para que sea m as realista. Finalmente, un ejemplo del uso de estas clase se muestra en el Listado 9.6.
1 2 3 4 5 6 7 8 9 10 11 12 13

package p r i n c i p a l ; 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 ; 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 ; public f i n a l c l a s s P r i n c i p a l { private P r i n c i p a l ( ) { super ( ) ; } private void e j e c u t a ( ) { 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 ( ) ; 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 ( ) ) ;

138
14 15 16 17 18 19 20 21 22

CON GENERICOS CAP ITULO 9. PROGRAMACION

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 ( ) ; 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 ( ) ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) ; principal . ejecuta () ; } }

Listado 9.6: Este listado muestra c omo usar los sensores los tipos de sensores anteriormente denidos. Un ejemplo del resultado de la ejecuci on del c odigo del Listado 9.6 es: Temperatura: 1.4107025 Viento: Intensidad: 40.831844 Direcci on: [0.7000265, -0.7141168] Siguiendo el mismo esquema, basta denir el tipo de dato que mide el sensor, podemos crear cualquier tipo de sensor extendiendo a la clase Sensor, y autom aticamente ser a capaz de devolvernos el dato que mide.

9.4.

Ampliaci on del tipo gen erico

Para seguir profundizando en las posibilidades de los tipos gen ericos, retomemos el ejemplo de la Secci on 8.4 de las guras: c rculos, tri angulos rect angulos y rect angulos de las que podemos calcular su area. Sabemos que la clase ArrayList<E> es un contenedor que trabaja con gen ericos, luego podemos crear un ArrayList<Figura> para almacenar en el cualquier tipo de gura que se pueda dibujar, tal y como muestra el Listado 9.7.
1 2 3 4 5 6

// Una l i s t a de f i g u r a s . 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 > () ; // C rculos , tri angulos recta ngulos y recta n g u l o s s on f i g u r a s . f i g u r a s . add ( new C i r c u l o ( ) ) ; 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 ( ) ) ; 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 guras. Ahora creamos una lista s olo para almacenar c rculos, y una vez creada, ya que la clase Circulo implementa el interface Figura, intentamos asignar a la lista de guras figuras la lista que s olo almacena c rculos circulos, tal y como muestra el Listado 9.8.
7 8 9 10 11

// Ahora un A r r a y L i s t s o l o de c rculos 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 > () ; c i r c u l o s . add ( new C i r c u l o ( 3 ) ) ; c i r c u l o s . add ( new C i r c u l o ( 5 ) ) ; // 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 odigo 11 hay un error, no podemos asignar a un ArrayList<Figuras> un ArrayList<Circulo>, aunque la clase Circulo implemente el interface Figura, la conversi on de tipos no es posible. Si pudi esemos hacer la asignaci on, podr amos a nadir a la lista de c rculos cualquier otra gura a trav es de la referencia figuras.

DEL TIPO GENERICO 9.4. AMPLIACION

139

Si intentamos escribir un m etodo capaz de mostrar el area de todas las guras de una lista tal y como muestra el Listado 9.9, no tendremos ning un error cuando mostremos las areas de las guras de la referencia figuras, pero obtendremos 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 ArrayList<Figura> es diferente al tipo ArrayList<Circulo>.
20 21 22 23 24

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 ) { for ( Figura elemento : l i s t a ) { 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 ( ) ) ; } }

Listado 9.9: Un m etodo que recibe una lista de elementos de tipo interface Figura. C omo podemos resolver esta encrucijada? C omo podemos indicar que queremos una lista de cualquier cosa que implemente la Figura para poder asignar a una lista de guras unas veces listas de s olo c rculos y otras veces listas de s olo rect angulos? Java nos da una soluci on para este caso, los tipos gen ericos con l mite superior.

9.4.1.

Tipos gen ericos con l mite superior

El Listado 9.10 muestra un m etodo en el que se da un l mite superior al tipo de datos gen erico. <E extends Figura> indica que los elementos que se pueden almacenar en la colecci on pueden ser de cualquier subtipo de Figura. F jate que aunque Figura es una interface se utiliza la palabra reservada extends y no implements. De este modo, podemos llamar a este m etodo con cualquier ArrayList<T> siempre que sus elementos implementen la interface Figura.
22 23 24 25 26

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 ) { for ( Figura elemento : l i s t a ) { 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 ( ) ) ; } }

Listado 9.10: Un m etodo que recibe una lista gen erica de elementos que implementan la interface Figura. De este modo podemos restringir los tipos concretos de las clases gen ericas. En el Listado 9.10 estamos restringiendo el tipo concreto de la clase gen erica a alg un tipo que extienda, o implemente, el tipo Figura.

9.4.2.

Comodines

La l nea 13 del Listado 9.11 presenta una nueva construcci on del lenguaje: ArrayList<? extends Figura>figurasGeneral, donde ? es un comod n. Esta construcci on signica un ArrayList de cualquier tipo que implemente (o extienda) a Figura, es decir, a la referencia figurasGeneral s que le podemos asignar cualquier otro ArrayList de tipos concretos si esos tipos implementan

140

CON GENERICOS CAP ITULO 9. PROGRAMACION

(o extienden) el interface Figura. De nuevo, f jate que aunque, como en este caso, las clases nales implementan un interfaz, el ArrayList utiliza la palabra reservada extends, dicho de otro modo se utiliza siempre extends con el signicado de subtipo sea este por extensi on (extends) o por implementaci on (implements) de una interface.
12 13 14 15 16 17

// Una l i s t a con l mite superior 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 ; 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 > () ; r e c t a n g u l o s . add ( new R e c t a n g u l o ( ) ) ; 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 ) ) ; figurasGeneral = rectangulos ;

Listado 9.11: Con el uso de comodines podemos denir listas de tipos que extiendan la interace Figura Ahora s , tanto en la l nea 13 como 17 podemos hacer la asignaci on. Pero debemos pagar un precio por esta nueva posibilidad, y este es que no podemos a nadir elementos al ArrayList a trav es de la referencia figurasGeneral. No podemos escribir algo como gurasGeneral.add(new Circulo(). Es importante no olvidar esta restricci on. Para qu e nos sirve entonces esta posibilidad? Aunque no podamos a nadir nuevos elementos a esta lista, s que podemos trabajar con los elementos que hay en ella, de modo que podemos reescribir el m etodo del Listado 9.9 para mostrar el area de todas las guras contenidas en la lista, con independencia del tipo de elementos con el que se deni o la lista tal y como muestra el Listado 9.12.
28 29 30 31 32

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 ) { for ( Figura elemento : l i s t a ) { 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 ( ) ) ; } }

Listado 9.12: Un m etodo que recibe una lista gen erica de elementos que implementan la interface Figura utilizando comodines. El m etodo private void muestraAreas3(ArrayList<? extends Figura>lista), es capaz de mostrar el area de los elementos de cualquier lista de guras, y no hemos utilizado el l mite superior en la restricci on de tipo <E extends Figura> como en el caso del Listado 9.10. De modo an alogo, podemos indicar que una clase gen erica trabaja con referencias de cualquier clase padre de una clase dada, por si esto nos pudiera interesar. En este caso la sintaxis de la construcci on se muestra en el Listado 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.

9.5. BORRADO DE TIPO Y COMPATIBILIDAD CON CODIGO HEREDADO141

9.5.

Borrado de tipo y compatibilidad con c odigo heredado

Como has podido ver, la programaci on con gen ericos es muy potente, de hecho, todas las clases del Java Collection Framework usan tipos gen ericos. Sin embargo, existe un problema de incompatibilidad con c odigo heredado anterior a la versi on 5 de Java en las que no existen tipos gen ericos. C omo se resuelve? de uno modo transparente para el programador, el compilador de Java sustituye los tipos gen ericos por verdaderos tipos cuando se determinan estos en tiempo de compilaci on, es decir, si el m etodo del Listado 9.10 en el c odigo se llama una vez con un ArrayList<Circulo> y otra vez con un ArrayList<Recttangulo>, se crean dos versiones de este m etodo con cada uno de estos dos tipos concretos. A esta t ecnica se la llama Borrado de tipo.

Ejercicios.
1. Crea otros tipos de sensores, por ejemplo un sensor de presi on que mida la presi on atmosf erica, y un sensor de color que mida colores. En este u ltimo caso, elige el espacio de colores RGB. 2. Crea una clase gen erica Garage que permita almacenar en ellas cualquier tipo de veh culos Coches, Furgonetas y Motos por ejemplo, es una ubicaci on especicada por un n umero real (como la plaza de garage) o el DNI del usuario especicado como un String.

Lecturas recomendadas.
En esta direcci on http://java.sun.com/docs/books/tutorial/java/ generics/index.html puedes encontrar la referencia b asica de Sun sobre el uso de gen ericos en Java. En esta direcci on http://java.sun.com/docs/books/tutorial/extra/ generics/index.html puedes encontrar otra interesante y detallada referencia, de Gilad Bracha, a la programaci on con gen ericos en Java.

142

CON GENERICOS CAP ITULO 9. PROGRAMACION

Cap tulo 10

Construcci on de proyectos con Ant


Contenidos
10.1. Qu e es Ant . . . . . . . . . . . . . . . . . . . . 10.2. Denici on del proyecto . . . . . . . . . . . . . 10.2.1. Objetivos . . . . . . . . . . . . . . . . . . . . 10.2.2. Tareas . . . . . . . . . . . . . . . . . . . . . . 10.3. Compilar el c odigo fuente de un proyecto . . 10.4. Propiedades . . . . . . . . . . . . . . . . . . . . 10.5. Estructuras path-like . . . . . . . . . . . . . . 10.6. Ejecuci on de las Pruebas Unitarias . . . . . . 10.7. Generaci on de la documentaci on . . . . . . . 10.8. Empaquetado de la aplicaci on . . . . . . . . . 10.9. Ejecuci on y limpieza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 . 144 . . 145 . . 145 . 146 . 146 . 147 . 148 . 150 . 151 . 151

Introducci on
Hasta ahora, hemos utilizado un entorno de desarrollo integrado, como Eclipse, para realizar todas las tareas relativas a nuestro proyecto. Sabemos c omo compilar nuestro c odigo, c omo generar la documentaci on con javadoc, c omo ejecutar nuestro c odigo, c omo empaquetar nuestro c odigo y c omo realizar pruebas sobre nuestro c odigo con JUnit. Todas estas tareas han necesitado nuestra intervenci on, para ejecutar el c odigo de nuestra aplicaci on hemos tenido que pulsar el bot on correspondiente en Eclipse, y del mismo modo, hemos tenido que actuar sobre el IDE para ejecutar las 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 on entre unos y otros acaba siendo cuesti on de gustos y adaptaci on m as
1 Neatbeans

se puede descargar para su instalaci on desde http://netbeans.org/

143

144

DE PROYECTOS CON ANT CAP ITULO 10. CONSTRUCCION

que de la funcionalidad que los entornos proporcionan, ya que todos ellos proporcionan una funcionalidad parecida, al menos, suciente para lo que nosotros necesitamos en este momento. En cada uno de estos entorno de desarrollo, el modo de ejecutar una aplicaci on, o el modo de lanzar la herramienta de documentaci on cambia. Incluso el modo en que el entorno de desarrollo organiza las carpetas del proyecto puede cambiar, un entorno puede usar el nombre src y otro source. Estos peque nos cambios hacen necesaria la intervenci on del desarrollador para migrar proyectos de un entorno a otro. Ser a interesante que, con independencia del entorno de desarrollo, o incluso si no utiliz asemos ning un entorno de desarrollo, fuese posible realizar las tareas comunes sobre un proyecto Java de modo est andar. La herramienta de construcci on de proyectos Ant nos proporciona precisamente eso, un modo de trabajar con nuestros proyectos con independencia del entorno de desarrollo elegido, o incluso poder trabajar directamente sobre nuestro proyecto desde consola.

10.1.

Qu e es Ant

Ant es una herramienta de construcci on de proyectos. Pero su utilidad no se detiene ah , con Ant podemos hacer mucho m as que simplemente compilar el c odigo de nuestro proyecto, podemos realizar, de modo autom atico, otras muchas tareas como la ejecuci on de prueba sobre nuestro c odigo, la generaci on de informes a partir de los resultados de las pruebas, la generaci on de la documentaci on de nuestro proyecto y un largo, larg simo etc etera. Ant es extensible, de modo que incluso podemos denir nuestras propias tareas, ya que Ant est a escrito en Java. Ant es un proyecto de la Fundaci on Apache que puedes encontrar en esta direcci on http://ant.apache.org, donde tambi en se encuentran los sencillos detalles de instalaci on, basta con descomprimir el chero que se puede descargar desde la p agina web de la Fundaci on Apache y establecer las correspondientes variables de entorno.

10.2.

Denici on del proyecto

La denici on de un proyecto Ant siempre se hace en un chero llamado build.xml. Como ejemplo de uso de Ant, vamos a utilizar el proyecto de conversi on de temperaturas presentado en el Cap tulo 5 y las pruebas unitarias sobre este proyecto presentadas en el Cap tulo 6. Dentro del proyecto de conversi on de temperaturas crea un chero y ll amalo build.xml, el chero de descripci on del proyecto es un chero xml. Si este est andar tecnol ogico no te es familiar, interesa que antes de seguir adelante conozca esta tecnolog a. En esta direcci on http://www.w3.org/standards/xml/ core encontrar as una introducci on a esta tecnolog a proporcionada por el World Wide Web Consortium, el consorcio que se encarga de los est andares Web2
2 Este organismo es el encargado del proceso de estandarizaci on de las tecnolog as Web, su p agina web est a repleta de informaci on con respecto a estos est andares tecnol ogicos. Al igual que la p agina web de Sun sobre Java es la referencia b asica en la web sobre el lenguaje de programaci on Java, la p agina web del W3C es la referencia b asica para los est andares web.

DEL PROYECTO 10.2. DEFINICION

145

La etiqueta ra z bajo la cual se dene todo el proyecto es <project> que tiene un atributo obligatorio name con el que especicamos el nombre del proyecto. El Listado 10.1 muestra la denici on b asica de un proyecto.
< 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 "> ... 3 </ p r o j e c t >
1 2

Listado 10.1: Denici on de un proyecto Ant. Si la sintaxis xml no te es familiar, f jate que la etiqueta de apertura <project> est a cerrada con la etiqueta </project>3 . Un proyecto Ant est a formado, por uno o m as objetivos, y cada uno de estos objetivos puede estar formado por una o m as tareas. Cada tarea se realizar an en el orden en el que fue especicada dentro del objetivo. Veamos c omo se dene un objetivo en Ant.

10.2.1.

Objetivos

Para denir 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.
< 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 "> < t a r g e t name=" e m p e z a n d o "> 3 ... 4 </ t a r g e t> 5 </ p r o j e c t >
1 2

Listado 10.2: Denici on de un objetivo Ant. Cada uno de los objetivos contiene una o m as tareas que se ejecutar an en el orden en que fueron denidas cuando ejecutemos el correspondiente objetivo. Veamos c omo se especican las tareas pertenecientes a un objetivo.

10.2.2.

Tareas

En Ant existe una enorme cantidad de tareas predenidas, por ejemplo, existe una tarea para compilar el c odigo fuente de nuestra aplicaci on, y otra tarea para crear un chero jar a partir del c odigo compilado de nuestra aplicaci on. A lo largo de este cap tulo iremos describiendo las tareas m as utilizadas. En este momento, y como ejemplo, vamos a utilizar una tarea muy sencilla cuya funci on es simplemente mostrar un mensaje de texto en consola, tal y como muestra el Listado 10.3.
< 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 "> < t a r g e t name=" e m p e z a n d o "> 3 < e c h o>Empezando con Ant . </ e c h o> 4 </ t a r g e t> 5 </ p r o j e c t >
1 2

Listado 10.3: La tarea <echo> muestra un mensaje de texto por consola.


3 En un chero xml bien formado toda etiqueta abierta debe estar cerrada con su correspondiente etiqueta.

146

DE PROYECTOS CON ANT CAP ITULO 10. CONSTRUCCION

Ejecutar un objetivo desde l nea de instrucciones es muy sencillo, basta situarse en el directorio donde se encuentre el chero build.xml y teclear ant empezando, lo que invocar a la ejecuci on del objetivo empezando denido en el chero build.xml. El resultado de la ejecuci on ser a parecido a: Rayuela:ant oscar$ ant empezando Buildfile: build.xml empezando: [echo] Empezando con Ant. BUILD SUCCESSFUL Total time: 0 seconds Pasemos a ver c omo utilizar algunas otras tareas m as u tiles que <echo>.

10.3.

Compilar el c odigo fuente de un proyecto

La etiqueta <javac> nos permite compilar c odigo Java. Para poder usar esta etiqueta debemos indicar el directorio donde est a el c odigo fuente mediante el atributo srcdir, e indicar donde se escribir an los cheros de clases compilados mediante el atributo destdir. El Listado 10.4 muestra el uso de la etiqueta <javac>.
< 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 "> < 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 t> 7 </ p r o j e c t >
1 2

Listado 10.4: Compilar un proyecto usando Ant. F jate que previamente a la tarea de compilaci on de la l nea 3, hemos utilizado la tarea mkdir para crear el directorio de destino de las clases compiladas. Ahora ya puedes compilar el c odigo de tu proyecto invocando al objetivo 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 chero de destino de los cheros 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 on, podemos utilizar las propiedades que nos permiten asociar a un nombre un valor, y hacer referencia al valor a trav es de su nombre en cualquier lugar del chero build.xml, tal y como muestra el Listado 10.5.
1 2

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

10.5. ESTRUCTURAS PATH-LIKE


3 4 5 6 7 8 9

147

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

< 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 "> <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 t> 14 </ p r o j e c t >
10

Listado 10.5: Uso de las propiedades. En este caso, cada una de las propiedades hace referencia a una direcci on representada por el atributo location.

10.5.

Estructuras path-like

Con las propiedades podemos denir valores a los que poder hacer referencia por su nombre. Las estructuras path-like son a un m as potentes, y nos permiten denir grupos de directorios o cheros. Por ejemplo, en nuestro proyecto de la aplicaci on de conversi on de temperaturas, tenemos programadas una serie de clases de pruebas unitarias que necesitamos compilar antes de ejecutar. Para compilar las clases de pruebas unitarias necesitaremos la biblioteca junit.jar, adem as del directorio donde se encuentran las clases de prueba que queremos compilar. Para denir grupos de directorios y cheros 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.
< 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 "> < ! 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 " />
1 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

<path i d=" t e s t . c o m p i l e . c l a s s p a t h "> < f i l e s e t d i r=" $ { l i b } " i n c l u d e s=" * . j a r " /> < 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 } " /> </ path> < 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 "> <mkdir d i r=" $ { b u i l d . c l a s s e s . d i r } " /> < j a v a c s r c d i r=" \ $ { s r c . d i r } " d e s t d i r=" $ { b u i l d . c l a s s e s . d i r } " /> </ t a r g e t> < t a r g e t name=" c o m p i l e - t e s t s " depends=" 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 l o s t e s t s . "> <mkdir d i r=" $ { t e s t . c l a s s e s . d i r } " /> < j a v a c s r c d i r=" $ { t e s t . d i r } " d e s t d i r=" $ { t e s t . c l a s s e s . d i r } ">

148
32 33

DE PROYECTOS CON ANT CAP ITULO 10. CONSTRUCCION


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

<c l a s s p a t h </ j a v a c> 34 </ t a r g e t> 35 </ p r o j e c t >

Listado 10.6: Uso de estructuras path-like para denir la ruta a las bibliotecas del proyecto. En las l neas 9, 11 y 13 del Listado 10.6 estamos deniendo las propiedades que hacen referencia al directorio con el c odigo fuente de las clases de prueba, al directorio destino de las clases compiladas de prueba y al directorio donde est an todas las bibliotecas del proyecto respectivamente. Por su lado, entre las l neas 15-18, mediante una estructura path-like, estamos deniendo donde est an las bibliotecas necesarias para compilar las clases de prueba (junit.jar y harmcret.jar) y donde est an las clases compiladas del proyecto. Finalmente, entre las l neas 26-34 estamos deniendo un objetivo para compilar 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 compilar 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 objetivos especicados en antes de cubrir el objetivo actual.

10.6.

Ejecuci on de las Pruebas Unitarias

El Listado 10.7 muestra c omo ejecutar una bater a de pruebas y grabar el resultado a un chero como un informe con formato de texto.
< 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 " d e f a u l t=" t e s t "> < ! 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 " />
1 2 18 19 20 21 22 23 24 25 26 27 28 29 30 31

< ! Path p a r a c o m p i l a r l a s c l a s e s de prueba > <path i d=" t e s t . c o m p i l e . c l a s s p a t h "> < f i l e s e t d i r=" $ { l i b } " i n c l u d e s=" * . j a r " /> < 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 } " /> </ path> < ! Path p a r a e j e c u t a r l a s c l a s e s de prueba > <path i d=" t e s t . c l a s s p a t h "> <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 " /> < 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 } " /> </ path> < 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 ">

DE LAS PRUEBAS UNITARIAS 10.6. EJECUCION


32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

149

<mkdir d i r=" $ { b u i l d . c l a s s e s . d i r } " /> < j a v a c s r c d i r=" \ $ { s r c . d i r } " d e s t d i r=" $ { b u i l d . c l a s s e s . d i r } " /> </ t a r g e t> < t a r g e t name=" c o m p i l e - t e s t s " depends=" 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 l o s t e s t s . "> <mkdir d i r=" $ { t e s t . c l a s s e s . d i r } " /> < j a v a c s r c d i r=" $ { t e s t . d i r } " d e s t d i r=" $ { t e s t . c l a s s e s . d i r } "> < 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 " /> </ j a v a c> </ t a r g e t>

< t a r g e t name=" t e s t " 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 t > 60 </ t a r g e t> 61 </ p r o j e c t >

Listado 10.7: Ejecutar la bater a de pruebas unitarias. En el Listado 10.7 se ha a nadido el atributo default="test" al proyecto para indicar que el objetivo test es el objetivo por defecto, si no se selecciona ning un otro, este es el objetivo que se invoca al ejecutar Ant. Tambi en se han a nadido las propiedades y estructuras path-like necesarias para la ejecuci on de las pruebas. Si u nicamente 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 olo las l neas a nadidas al chero build.xml.
1 2 3 4 5 6 7

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

< t a r g e t name=" t e s t - x m l " 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 t > 20 </ b a t c h t e s t> 21 </ j u n i t > 22 </ t a r g e t>

Listado 10.8: Ejecutar s olo algunas de las pruebas. F jate, que en este u ltimo caso, adem as estamos indicando que el formato de los informes sea xml, esta posibilidad es interesante ya que a partir de estos

150

DE PROYECTOS CON ANT CAP ITULO 10. CONSTRUCCION

informes podremos hacer sobre ellos una transformaci on para generarlos en formato html tal y como muestra el Listado 10.9, donde, de nuevo, s olo aparecen las l neas a nadidas al chero build.xml.
1 2 3 4 5 6 7

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

< t a r g e t name=" t e s t . r e p o r t s " 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 t > 13 < 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 t > 16 < 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 on de la documentaci on

Otra tarea que se puede automatizar es la generaci on de la documentaci on 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 etodos y atributos de los que se generar a la documentaci on seg un su modicador de acceso. En nuestro ejemplo, se generar a documentaci on de todos los m etodos y atributos cuya visibilidad sea private o mayor. Otros atributos u tiles son author, si su valor es true se a nadir a informaci on del autor a la documentaci on, y version, si su valor es true se a nadir a informaci on de la versi on.
1 2 3 4 5 6 7

< ! 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 o n > < 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 " /> ...

< t a r g e t name=" j a v a d o c " 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 h> 16 < 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 h> 19 </ j a v a d o c> 20 </ t a r g e t>

Listado 10.10: Generaci on de la documentaci on del proyecto.

10.8. EMPAQUETADO DE LA APLICACION

151

10.8.

Empaquetado de la aplicaci on

Ant Tambi en nos proporciona la tarea <jar> para empaquetar nuestra aplicaci on, tal y como muestra el Listado 10.11. En este caso, hemos empleado la etiqueta <property> con el atributo <value> para denir el nombre del chero empaquetado. La sintaxis de la tarea es bastante autoexplicativa, hay que indicar el nombre del chero empaquetado con el atributo destfile de la etiqueta jar. E indicar los cheros que se van a incluir dentro del chero empaquetado mediante una estructura path-like. Podemos tambi en indicar el contenido del chero de maniesto con la etiqueta <manifest>.
< ! D i r e c t o r i o p a r a e l f i c h e r o empaquetado > < 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 " />
1 2 5 6 7 8 9

...

< t a r g e t name=" p a c k a g e " 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 t> 15 <a t t r i b u t e 16 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 t> 19 </ j a r > 20 </ t a r g e t>

Listado 10.11: Empaquetado del proyecto.

10.9.

Ejecuci on y limpieza

Tambi en podemos ejecutar nuestra aplicaci on, como un chero empaquetado, usando la tarea <java> de Ant, tal y como se muestra en el Listado 10.12.
< t a r g e t name=" e x e c u t e " 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>
1 2

Listado 10.12: Ejecuci on del proyecto como un chero empaquetado. Cabe destacar que para ejecutar la aplicaci on se debe crear una nueva instancia de la m aquina virtual, cosa que indicamos con el valor true del atributo fork de la tarea java. Tambi en podemos borrar los directorios con las clases compiladas y los informes 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 2

< 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 "> < d e l e t e d i r=" \ $ { d i s t . d i r } " />

152
3 4

DE PROYECTOS CON ANT CAP ITULO 10. CONSTRUCCION

< d e l e t e d i r=" \ $ { b u i l d . d i r } " /> < 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 endice A se muestra el listado completo del chero build.xml.

Lecturas recomendadas.
Sin duda la referencia b asica sobre Ant se encuentra el la propia p agina web del proyecto, esta es la direcci on directa http://ant.apache.org/ manual/index.html. El cap tulo 1 de la referencia [13] presenta la herramienta Ant de modo conciso pero muy informativo. En espa nol, es interesante el cap tulo 3 de la referencia [5].

Cap tulo 11

Interfaces gr acas de usuario


Contenidos
11.1. APIs para la programaci on de interfaces gr acos de usuario en Java: AWT y Swing . . . . . . . . . 11.2. Contenedores y Componentes . . . . . . . . . . . . 11.3. Gestores de Aspecto (Layout Managers ) . . . . . 11.4. Detecci on de eventos: Escuchadores . . . . . . . . 11.5. Algunos componentes Swing . . . . . . . . . . . . . 11.5.1. JLabel, muestra texto o iconos . . . . . . . . . . . 11.5.2. JButton, botones que el usuario puede pulsar . . . 11.5.3. JTextField, campos de introducci on de texto . . . 11.5.4. JRadioButton, botones de opciones . . . . . . . . . 11.5.5. JCheckBox, botones de selecci on m ultiple . . . . . 11.5.6. JList, listas de selecci on . . . . . . . . . . . . . . . 11.6. El patr on de dise no Modelo/Vista/Controlador . 154 155 155 157 162 162 162 163 164 166 166 168

. . . . . .

Introducci on
Hasta ahora, la interacci on con nuestras aplicaciones ha sido a trav es de consola, los datos de entrada los tecleamos en consola, y la respuesta la obten amos tambi en directamente en consola. Cada vez es menor el n umero de aplicaciones con interfaces de usuario basados en consola. Uno de los principales inconvenientes de este tipo de interfaces de usuario es que son poco intuitivos y por lo tanto susceptibles de crear confusi on en el usuario y como resultado que los datos de entrada al programa sean err oneos. Por contrapartida, las aplicaciones basadas en interfaces gr acos de usuario son m as intuitivas y la entrada de datos puede estar acotada, evitando que el usuario introduzca valores err oneos. Java proporciona dos grandes APIs para programar interfaces gr acas de 153

154

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

usuario: Abstract Windows Toolkit (AWT) y Swing. En este cap tulo veremos cuales son las caracter sticas de ambas y justicaremos por qu e elegir una sobre otra. La programaci on de interfaces gr acos de usuario est a basada en la idea de que los componentes gr acos, tales como botones y cajas de edici on de texto, son capaces de lazar eventos cuando el usuario interacciona sobre ellos. Por ejemplo, cada vez que el usuario hace click sobre un bot on, este lanza un evento como respuesta, y ser a tarea del programador escribir el c odigo necesario para escuchar el tipo de eventos que le interese y actuar en consecuencia cuando se produzca uno. Los interfaces gr acos de usuario son anteriores a la aparici on del lenguaje de programaci on Java, y en su programaci on se han descubierto interesantes patrones de dise no, uno de ellos, ampliamente utilizado en otros lenguajes de programaci on como por ejemplo Smalltalk, es el patr on de dise no Modelo/Vista/Controlador. Este patr on de dise no agrupa las clases de una aplicaci on seg un su responsabilidad, de modo que una clase s olo puede formar parte bien del Modelo, bien de la Vista o bien del Controlador. Veremos con detalle este patr on de dise no y alguna t ecnica u til para su implementaci on.

11.1.

APIs para la programaci on de interfaces gr acos de usuario en Java: AWT y Swing

En Java, existen dos APIs para la programaci on de interfaces gr acos de usuario AWT (Abstract Window Toolkit ) y Swing. AWT fue la primera API disponible en Java y sus principales caracter sticas son: La creaci on de componentes gr acos se delega al Sistema Operativo. El Sistema Operativo se encarga de dibujar los componentes gr acos y de la detecci on de eventos. El aspecto de la aplicaci on es el nativo del Sistema Operativo. La principal desventaja de AWT es que descansa directamente sobre el Sistema Operativo quien interviene tanto en la creaci on de componentes gr acos como en la detecci on de eventos, de modo que la aplicaci on se puede ralentizar si la interfaz contiene muchos elementos gr acos, y por otro lado no se pueden introducir cambios en el aspecto de los componentes. El API Swing viene a liberar la creaci on de interfaces gr acos de la carga que supone la dependencia con respecto al Sistema Operativo. Las principales caracter sticas de este API son: Swing se encarga de dibujar los componentes y de detectar la interacci on sobre ellos. El conjunto de componentes es m as grande que el que proporciona el Sistema Operativo. Se tiene control absoluto sobre el aspecto de los componentes. Por todo ellos, Swing ha ido desplazando a AWT en la creaci on de interfaces gr acos de usuario en Java.

11.2. CONTENEDORES Y COMPONENTES

155

Figura 11.1: Colocaci on de Componentes con FlowLayout.

11.2.

Contenedores y Componentes

Dentro de Swing tenemos dos grandes grupos de elementos: los Contenedores y los Componentes. La diferencia entre ellos es que los Contenedores pueden albergar otros Contenedores o Componentes dentro de ellos, y los Componentes son los elementos gr acos con los que el usuario puede interaccionar, como por ejemplo botones, listas, etc etera. Los tres Contenedores que disponibles en Swing son JFrame que representa una ventana con marco, JWindow que representa una ventana sin marco, y JPanel que no tiene un aspecto visual, su cometido es albergar otros Contenedores o Componentes dentro de el. La idea b asica es que vamos a utilizar los JPanel como mu necas rusas, de modo que colocaremos Componentes dentro de JPanel y esos JPanel dentro de otros JPanel con m as Componentes para ir creando el aspecto deseado para el interfaz gr aco de nuestra aplicaci on.

11.3.

Gestores de Aspecto (Layout Managers )

Cuando se programan interfaces gr acos de usuario, un aspecto importante es la colocaci on de los Componentes dentro de la ventana de nuestra aplicaci on. Java nos facilita esta tarea mediante el uso de Gestores de Aspecto (Layout Managers en ingl es). Estos Gestores de Aspecto son los encargados de colocar los Componentes que vamos a nadiendo en los Contenedores. Cada uno de los Gestores de Aspecto sigue una pol tica de colocaci on de los componentes, as , por ejemplo, BoxLayout coloca cada nuevo componente al la izquierda del u ltimo componente a nadido, como en el sentido de la escritura del espa nol, de tal modo que si no hay espacio suciente para insertar un nuevo Componente en la l nea actual, porque se ha llegado al borde de la ventan, el Componente se a nadir a al principio de una nueva l nea por debajo de la actual. La Figura 11.1 muestra un ejemplo de este comportamiento. Este Gestor de Aspecto es el que por defecto utiliza JPanel cada

156

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

Figura 11.2: Colocaci on de Componentes con BorderLayout.

vez que a nadimos sobre el un Componente. JFrame posee otro Gestor de Aspecto por defecto BorderLayout. Este Gestor de Aspecto dene 5 zonas BorderLayout.CENTER, BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.EAST, BorderLayout.WEST, dentro de las cuales s olo podemos a nadir un Componente o Contenedor con el m etodo add(Component componente, int zona), si no indicamos la zona (add(Component componente), el nuevo componente ser a a nadido a la regi on central. La Figura 11.2 muestra una ventana en la que se observa la disposici on de las cinco zonas de BorderLayout. El c odigo fuente de este sencilla ejemplo aparece en el Listado 11.1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

package g u i ; import j a v a . awt . BorderLayout ; import j a v a . awt . C o n t a i n e r ; import j a v a x . s w i n g . JButton ; import j a v a x . s w i n g . JFrame ; 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 ; public f i n a l c l a s s EjemploBorderLayout { private EjemploBorderLayout ( ) { super ( ) ; } private void creaGUI ( ) { 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 " ) ; Container contenedor = ventana . getContentPane ( ) ; c o n t e n e d o r . add ( new JButton ( " C e n t r o " ) ) ; c o n t e n e d o r . add ( new JButton ( " N o r t e " ) , BorderLayout .NORTH) ; c o n t e n e d o r . add ( new JButton ( " S u r " ) , BorderLayout .SOUTH) ; c o n t e n e d o r . add ( new JButton ( " E s t e " ) , BorderLayout . EAST) ; c o n t e n e d o r . add ( new JButton ( " O e s t e " ) , BorderLayout .WEST) ; ventana . s e t S i z e ( 4 0 0 , 400) ; v e n t a n a . s e t V i s i b l e ( true ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) {

DE EVENTOS: ESCUCHADORES 11.4. DETECCION


31 32 33 34 35

157

new EjemploBorderLayout ( ) . creaGUI ( ) ; } }) ; } }

Listado 11.1: Ejemplo de uso de BorderLayout El Listado 11.1 contiene algunos detalles importante. En la l nea 16, estamos almacenando una referencia al contenedor de la ventana principal, que es donde a nadimos los botones. Las l neas 17-21 a naden un bot on, instancia de JButton a cada uno de las cinco regiones que dene un BorderLayout. En la l nea 22 estamos indicando el tama no de la ventana, y nalmente, en la l nea 23 hacemos visible la ventana. En las l nea 28-33 estamos utilizando un t ecnica que quedar a denitivamente clara cuando veamos c omo programar hilos en Java en el Cap tulo 14, en este momento basta decir que en esas l neas de c odigo se est a creando un hilo para atender al interfaz gr aco de usuario de modo que no interere con el hilo de la aplicaci on principal. Los Gestores de Aspecto por defecto se pueden cambiar con el m etodo setLayout(java.awt.LayoutManager) al que se le pasa una instancia del nuevo Gestor de Aspecto que queremos utilizar. Otros Gestores de Aspecto son GridLayout, que permite denir una rejilla con las y columnas, y podemos a nadir un componente dentro de cada una 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 on de eventos: Escuchadores

Si has probado a ejecutar el c odigo del Listado 11.1 quiz as te hayas dado cuenta del siguiente detalle, al pulsar sobre el bot on de cerrar la ventana que aparece en el marco de esta, la ventana se cierra pero la aplicaci on sigue ejecut andose. No existe ning un error en la aplicaci on, simplemente, al cerrar la ventana, lo que en realidad est a ocurriendo es que se hace invisible, pero con ello no su fuerza que acabe la ejecuci on de la aplicaci on. Para entender lo que est a realmente ocurriendo tenemos que conocer de qu e modo act uan los Contenedores y Componentes en Java1 . La idea b asica es que cuando el usuario interacciona con los Contenedores 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 odigo que observa cuando un evento ocurre, 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 on de dise no llamado Observador. La secci on 16.9 presenta este patr on de dise no con detalle. La Figura 11.3 muestra gr acamente la din amica en la detecci on de eventos. 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 on

158

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

Figura 11.3: Din amica de la detecci on de eventos en Swing.

Figura 11.4: Din amica de la detecci on de eventos para el caso particular de WindowEvent. 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 nadir una instancia de la clase escuchadora a la lista de observadores del Contenedor o el Componente. Para ver de modo detallado como los pasos anteriores se concretan en c odigo, supongamos que deseamos cerrar la aplicaci on cuando el usuario haga click sobre el bot on de cierre de la ventana. El primer paso es conocer qu e tipo de eventos lanza JFrame, que es nuestra ventana, cuando el usuario hace click sobre el bot on correspondiente; para ello podemos consultar la p agina http://download.oracle.com/docs/cd/E17409_01/javase/ tutorial/uiswing/events/eventsandcomponents.html, donde encontramos que JFrame lanza eventos de tipo WindowEvent. El siguiente paso es conocer cual es el interface capaz de escuchar este tipo de eventos. En la anterior direcci on web encontramos que es WindowListener2 . Este interface declara m etodos cuyo signicado aparece en la Tabla 11.1. La Figura 11.4 muestra la din amica en la detecci on de eventos generados por JFrame. La clase que implemente este interface debe denir cada uno de los m etodos de la Tabla 11.1. Si lo u nico que queremos hacer es cerrar la aplicaci on cuando el usuario cierra la ventana, el u nico m etodo al que a nadiremos c odigo es public void windowClosing(WindowEvent e). Finalmente a nadiremos una instancia de la clase que implementa el interface WindowListener como escuchador a la ventana, con el m etodo addWindowListener3 .
2 Observa la nomenclatura usada en el nombrado de eventos y sus correspondientes escuchadores: si encontramos un evento de tipo xxxEvent, el interface capaz de escucharlo se llamar a xxxListener 3 F jate de nueva en la nomenclatura utilizada para nombrar el m etodo que a nade el escuchador al Contenedor, si el escuchador es xxxListener, el m etodo que lo a nade al Contenedor o Componente es addxxxListener(interface xxxListener)

DE EVENTOS: ESCUCHADORES 11.4. DETECCION windowOpened(WindowEvent e) windowClosing(WindowEvent e) windowClosed(WindowEvent e) windowIconified(WindowEvent e) windowDeiconified(WindowEvent e)

159

windowActivated(WindowEvent e) windowDeactivated(WindowEvent e)

Se invoca cuando la ventana se abre. Se invoca cuando se intenta cerrar la ventana. Se invoca cuando la ventana se ha cerrado denitivamente. Se invoca cuando la ventana se minimiza. Se invoca cuando la ventana pasa de estar minimizada a tener su estado normal. Se invoca cuando la ventana pasa a ser la ventana activa. Se invoca cuando la ventana deja de ser la ventana activa.

Tabla 11.1: M etodos declarados en la interface WindowListener todos ellos son public void. El Listado 11.2 muestra un ejemplo completo de un escuchador para los eventos de la ventana que cierra la aplicaci on cuando el usuario pulsa el bot on de cerrar ventana.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

package g u i ; import j a v a . awt . e v e n t . WindowEvent ; import j a v a . awt . e v e n t . WindowListener ; import j a v a x . s w i n g . JFrame ; 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 ; // 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 WindowEvent public c l a s s EjemploWindowListener implements WindowListener { private EjemploWindowListener ( ) { super ( ) ; } private void creaGUI ( ) { // Creamos l a v e n t a n a JFrame v e n t a n a = new JFrame ( " A p l i c a c i o n que se cierra con la ventan . " ) ; // A n 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 v e n t a n a . addWindowListener ( t h i s ) ; ventana . s e t S i z e ( 4 0 0 , 400) ; v e n t a n a . s e t V i s i b l e ( true ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) { new EjemploWindowListener ( ) . creaGUI ( ) ; } }) ; } // Los m e 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 WindowListener @Override public void windowOpened ( WindowEvent e ) { } interface

160
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

// E s t e e s e l u n i c o m e todo con c o digo @Override public void w i n d o w C l o s i n g ( WindowEvent e ) { 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 . . . " ) ; System . e x i t ( 0 ) ; } @Override public void windowClosed ( WindowEvent e ) { } @Override public void w i n d o w I c o n i f i e d ( WindowEvent e ) { } @Override public void w i n d o w D e i c o n i f i e d ( WindowEvent e ) { } @Override public void wi ndowA cti vate d ( WindowEvent e ) { } @Override public void w i n d o w D e a c t i v a t e d ( WindowEvent e ) { } }

Listado 11.2: Aplicaci on que naliza cuando se cierra la ventana. La clase principal implementa el interface WindowListener, por lo que se puede a nadir como escuchador de eventos WindowEvent. Si el Listado 11.2 te ha parecido tedioso, ya que hemos tenido que denir todos los m etodos dej andolos vac os excepto el m etodo windowClosing(WindowEvent e), tu sensaci on es acertada. En los casos en los que una interface tiene declarados muchos m etodos, de los que usualmente s olo se escribe c odigo para algunos de ellos, Java nos proporciona un clase de conveniencia, llamada adaptadora, que implementa la interface deniendo todos los m etodos vac os. Cual es la ventaja de utilizar estas clases adaptadoras?, pues que nuestros escuchadores en vez de implementar el interface extienden la clase adaptadora, y s olo sobrescriben los m etodos necesarios, la implementaci on del resto de m etodos ser a la que nos proporcione la clase adaptadora, es decir, ser an todos vac os. El Listado 11.3 muestra un ejemplo cuyo comportamiento es el mismo que el ejemplo del Listado 11.2, pero utilizando una clase interna an onima que extiende la clase adaptadora WindowAdapter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

package g u i ; import j a v a . awt . e v e n t . WindowAdapter ; import j a v a . awt . e v e n t . WindowEvent ; import j a v a x . s w i n g . JFrame ; 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 ; 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 { private E j e m p l o C l a s e A d a p t a d o r a ( ) { super ( ) ; } private void creaGUI ( ) { // Creamos l a v e n t a n a 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 . " ) ; // A n 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 o nima // que e x t i e n d e a WindowAdapter y s o l o s o b r e s c r i b e e l m e todo windowClosing .

DE EVENTOS: ESCUCHADORES 11.4. DETECCION


19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

161

v e n t a n a . addWindowListener ( new WindowAdapter ( ) { @Override public void w i n d o w C l o s i n g ( WindowEvent e ) { System . e x i t ( 0 ) ; } }) ; ventana . s e t S i z e ( 4 0 0 , 400) ; v e n t a n a . s e t V i s i b l e ( true ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) { new E j e m p l o C l a s e A d a p t a d o r a ( ) . creaGUI ( ) ; } }) ; } }

Listado 11.3: Uso de la clase adaptadora WindowAdapter para cerrar la aplicaci on al cerrar la ventana. Por otro lado, la propia clases JFrame nos ofrece un m etodo para denir el comportamiento de la aplicaci on cuando se cierra la ventana, este m etodo es setDefaultCloseOperation(int modo). El Listado 11.4 muestra un ejemplo de uso de este m etodo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

package g u i ; import j a v a x . s w i n g . JFrame ; 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 ; 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 { 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 ( ) { super ( ) ; } private void creaGUI ( ) { // Creamos l a v e n t a n a 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 . " ) ; // Usamos e l m e 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 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) ; ventana . s e t S i z e ( 4 0 0 , 400) ; v e n t a n a . s e t V i s i b l e ( true ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) { 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 ( ) ; } }) ; } }

Listado 11.4: Uso del m etodo setDefaultCloseOperation(int) para acabar la aplicaci on cuando se cierra la ventana. Esta t ecnica de escucha de eventos es transversal a todos los Contenedores 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 on siguiente vamos a ver algunos de los Componentes Swing m as

162

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

comunes y se mostrar an ejemplos de c omo escuchar los eventos que se producen cuando el usuario interacciona sobre ellos.

11.5.

Algunos componentes Swing

Esta secci on no pretende ser una presentaci on exhaustiva de Componentes Swing, si no una muestra de c omo utilizar la t ecnica que se ha mostrado en la secci on anterior para escuchar los eventos que producen.

11.5.1.

JLabel, muestra texto o iconos

El primer y m as sencillo Componente Swing es JLabel que se muestra como una cadena de texto o un icono que el usuario no puede modicar, pero s el programador. Este componente no lanza ning un tipo de evento, ya que el usuario, como hemos dicho, no puede interaccionar sobre el, nos sirve para mostrar 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, necesitamos implementar la interface ActionListener que u nicamente declara un m etodo public void actionPerformed(ActionEvent e) que ser a invocado cada vez que el usuario pulse el bot on. Finalmente, registraremos el escuchador al bot on con el m etodo addActionListener(ActionListener escuchador) de la clase JButton. El Listado 11.5 muestra un ejemplo de detecci on de los eventos ActionEvent, donde tambi en se ha incluido un JLabel. En la l nea 21 creamos el bot on como una instancia de JButton, en las l neas 22-27 a nadimos al bot on un escuchador como una clase interna an onima que implementa el interface ActionListener y denimos el m etodo que declara esta interfaz public void actionPerformed (ActionEvent e). Otro detalle interesante es que hemos utilizado un JPanel para a nadir sobre el los componentes JLabel y JButton y aprovechar que el 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 etodo public void pack(); este m etodo calcula el tama no optimo de la ventana para contener todos los componentes que se le han a nadido, de este modo estamos delegando en Swing el c alculo del tama no de la ventana.
1 2 3 4 5 6 7 8 9 10 11 12 13 14

package g u i ; import j a v a . awt . e v e n t . A c t i o n E v e n t ; 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 ; import import import import import javax javax javax javax javax . s w i n g . JButton ; . s w i n g . JFrame ; . swing . JLabel ; . swing . JPanel ; . swing . S w i n g U t i l i t i e s ;

public c l a s s EjemploJButton { private EjemploJButton ( ) { super ( ) ;

11.5. ALGUNOS COMPONENTES SWING


15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

163

} private void creaGUI ( ) { 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 " ) ; J P a n e l c o n t e n e d o r = new J P a n e l ( ) ; 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 on :" ) ) ; JButton jbBoton = new JButton ( " P ulsame " ) ; 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 ( ) { @Override 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 ) { System . o u t . p r i n t l n ( " B o t o n pulsado " ) ; } }) ; c o n t e n e d o r . add ( jbBoton ) ; 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 ) ; 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) ; v e n t a n a . pack ( ) ; v e n t a n a . s e t V i s i b l e ( true ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) { new EjemploJButton ( ) . creaGUI ( ) ; } }) ; } }

Listado 11.5: Ejemplo de uso de JButton

11.5.3.

JTextField, campos de introducci on de texto

La clase JTextField dibuja una caja de edici on de texto de una u nica l nea donde el usuario puede introducir texto. En el momento de la instanciaci on de JTextField podemos indicar el n umero de columnas. Un JTextField lanza eventos de tipo ActionEvent cada vez que el usuario pulsa el bot on Enter y este componente tiene el foco. Este es el mismo evento que lanza un bot on cuando el usuario lo pulsa, luego el procedimiento para 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 etodo String getText() para obtener el texto introducido por el usuario en el JTextField.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

package g u i ; import import import import import import import import java java java java . awt . C o n t a i n e r ; . awt . FlowLayout ; . awt . e v e n t . A c t i o n E v e n t ; . awt . e v e n t . A c t i o n L i s t e n e r ; . s w i n g . JFrame ; . swing . JLabel ; . swing . JTextField ; . swing . S w i n g U t i l i t i e s ;

javax javax javax javax

public c l a s s E j e m p l o J T e x t F i e l d { private J T e x t F i e l d j t f T e x t o ; private E j e m p l o J T e x t F i e l d ( ) { super ( ) ; }

164
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

public void creaGUI ( ) { JFrame v e n t a n a = new JFrame ( ) ; v e n t a n a . s e t L a y o u t ( new FlowLayout ( ) ) ; Container contenedor = ventana . getContentPane ( ) ; 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 : " ) ) ; j t f T e x t o = new J T e x t F i e l d ( 5 0 ) ; 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 ( ) { @Override 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 ) { 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 ( ) ) ; } }) ; v e n t a n a . add ( j t f T e x t o ) ; 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) ; v e n t a n a . pack ( ) ; v e n t a n a . s e t V i s i b l e ( true ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) { new E j e m p l o J T e x t F i e l d ( ) . creaGUI ( ) ; } }) ; } }

Listado 11.6: Ejemplo de uso de JTextField

11.5.4.

JRadioButton, botones de opciones

El siguiente Componente en complejidad es el bot on de radio JRadioButton que dibuja un bot on asociado a una opci on, como por ejemplo para elegir la forma 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 conocido ActionEvent y el nuevo ItemEvent. El evento ItemEvent nos da m as informaci on sobre lo que ha ocurrido que ActionEvent, ya que nos dice si lo que ha ocurrido es una selecci on o una des-selecci on del bot on. Ya sabemos que los eventos de tipo ActionEvent los podemos escuchar con una clase que implemente el interface ActionListener a nadida al Componente que queremos escuchar con el m etodo addActionListener(ActionListener escuchador), y que este interface s olo declara un m etodo public void actionPerformed(ActionEvent e) que se invoca cada vez que el bot on se pulsa. En el caso de un evento de tipo ItemEvent lo podemos escuchar con una clase que implemente el interface ItemListener siempre que a nadamos la instancia al Componente que queremos escuchar con addItemListener(ItemListener escuchador). Este interface s olo declara un m etodo public void itemStateChanged(ItemEvent e) que se invoca cada vez que el usuario selecciona o des-selecciona un JRadioButton. Para conocer si lo que ha ocurrido es una selecci on o una de-selecci on, podemos utilizar el m etodo getStateChange() de la clase ItemEvent que nos devolver a ItemEvent.SELECTED si lo que ha ocurrido es una selecci on, o ItemEvent.DESELECTED si lo que ha ocurrido es una de-selecci on.

11.5. ALGUNOS COMPONENTES SWING

165

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

package g u i ; import import import import import import import import import import import java java java java java . awt . BorderLayout ; . awt . e v e n t . A c t i o n E v e n t ; . awt . e v e n t . A c t i o n L i s t e n e r ; . awt . e v e n t . ItemEvent ; . awt . e v e n t . I t e m L i s t e n e r ; . s w i n g . BoxLayout ; . s w i n g . ButtonGroup ; . s w i n g . JFrame ; . swing . JPanel ; . s w i n g . JRadioButton ; . swing . S w i n g U t i l i t i e s ;

javax javax javax javax javax javax

public f i n a l c l a s s EjemploJRadioButton { private EjemploJRadioButton ( ) { super ( ) ; } 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 ) { J P a n e l c o n t e n e d o r = new J P a n e l ( ) ; 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 ) ) ; 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 ( ) ; JRadioButton jrbMucho = new JRadioButton ( " M u c h o " ) ; 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 ) ; 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 ) ; c o n t e n e d o r . add ( jrbMucho ) ; JRadioButton j r b N o r m a l = new JRadioButton ( " N o r m a l " ) ; jrbNormal . a d d A c t i o n L i s t e n e r ( escuchador ) ; jrbNormal . addItemListener ( escuchador ) ; c o n t e n e d o r . add ( j r b N o r m a l ) ; JRadioButton j r b P o c o = new JRadioButton ( " P o c o " ) ; jrbPoco . addActionListener ( escuchador ) ; jrbPoco . addItemListener ( escuchador ) ; c o n t e n e d o r . add ( j r b P o c o ) ; i f ( p o s i c i o n == BorderLayout . EAST) { ButtonGroup grupo = new ButtonGroup ( ) ; grupo . add ( jrbMucho ) ; grupo . add ( j r b N o r m a l ) ; grupo . add ( j r b P o c o ) ; } return c o n t e n e d o r ; } private void creaGUI ( ) { 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 " ) ; 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) ; 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) ; 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) ; v e n t a n a . pack ( ) ; v e n t a n a . s e t V i s i b l e ( true ) ; } 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 , public E s c u c h a d o r ( ) { super ( ) ; } @Override 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 ) { System . o u t . p r i n t l n ( " B o t on pulsado " ) ; } @Override public void i t e m S t a t e C h a n g e d ( ItemEvent e ) { 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 ( ) ; i f ( e . g e t S t a t e C h a n g e ( ) == ItemEvent .DESELECTED) System . o u t . f o r m a t ( " B o t o n %s d e s e l e c c i o n a d o . \ n " , t e x t o ) ; e l s e i f ( e . g e t S t a t e C h a n g e ( ) == ItemEvent . SELECTED) System . o u t . f o r m a t ( " B o t o n %s s e l e c c i o n a d o . \ n " , t e x t o ) ; } } ItemListener {

166
74 75 76 77 78 79 80 81 82 83 84

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) { new EjemploJRadioButton ( ) . creaGUI ( ) ; } }) ; } }

Listado 11.7: Ejemplo de uso de JRadioButton El Listado 11.7 muestra un ejemplo con este nuevo Componente. Adem as 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 un otro bot on previamente seleccionado este u ltimo se des-seleccionar a. La clase ButtonGroup crea un grupo l ogico, y no tiene ninguna representaci on gr aca. En la l nea 67 puedes ver como hemos recuperado el texto escrito a la derecha de cada uno de los JRadioButton. Y nalmente, 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 on m ultiple

Usualmente los botones de tipo JRadioButton se utilizan cuando las opciones presentadas al usuario son mutuamente excluyentes entre s , y se han a nadido a 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 na caja que al seleccionarlo aparece 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 u ltimos, si lo que ha ocurrido es una selecci on o una de-selecci on. Por lo tanto todo lo comentado en la secci on 11.5.4 sobre los JRadioButton es v alido para los JCheckBox.

11.5.6.

JList, listas de selecci on

La clase JList presentan al usuario varias opciones en forma de lista. El usuario puede seleccionar una o m as opciones dependiendo del modo de selecci on de la lista. Los eventos que un JList puede lazar cada vez que el usuario selecciona una opci on de la lista son nuestro conocido ActionEvent y el nuevo ListSelectionEvent. Este evento nos indica si la selecci on se est a efectuando (por ejemplo, el usuario pulsa sobre un elemento de la lista y, sin soltar el bot on del rat on, se desplaza sobre los elementos de la lista), o es la acci on nal, cuando el usuario suelta el bot on del rat on. Para escuchar los eventos de tipo ItemSelectionEvent debemos implementar la interface ItemSelectionListener que declara un u nico m etodo public void valueChanged(ListSelectionEvent e). Para consultar si

11.5. ALGUNOS COMPONENTES SWING

167

la selecci on est a en marcha o es denitiva podemos usar el m etodo 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 etodo setVisibleRowCount(int) de la l nea 25 para indicar cuantos elementos son visibles en la lista. En la l nea 23 activamos el modo de selecci on de los elementos de olo se la lista a ListSelectionModel.SINGLE SELECTION, de modo que s podr a seleccionar un elemento u nico de la lista (otros modos posible son SINGLE INTERVAL SELECTION y MULTIPLE INTERVAL SELECTION).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

package g u i ; import j a v a . awt . C o n t a i n e r ; import import import import import import import javax javax javax javax javax javax javax . s w i n g . JFrame ; . swing . J L i s t ; . swing . J S c r o l l P a n e ; . swing . L i s t S e l e c t i o n M o d e l ; . swing . S w i n g U t i l i t i e s ; . swing . event . L i s t S e l e c t i o n E v e n t ; . swing . event . L i s t S e l e c t i o n L i s t e n e r ;

public f i n a l c l a s s E j e m p l o J L i s t { private E j e m p l o J L i s t ( ) { super ( ) ; } private void creaGUI ( ) { JFrame v e n t a n a = new JFrame ( " E j e m p l o J L i s t " ) ; Container contenedor = ventana . getContentPane ( ) ; 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 rcoles " , " Jueves " , " Viernes " , " Sa bado " , " Domingo " }) ; opciones . setVisibleRowCount (5) ; 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) ; 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 ( ) ) ; 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 ) ; c o n t e n e d o r . add ( s c r o l l ) ; v e n t a n a . pack ( ) ; 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) ; v e n t a n a . s e t V i s i b l e ( true ) ; } 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 { @Override public void valueChanged ( L i s t S e l e c t i o n E v e n t e ) { i f ( e . g e t V a l u e I s A d j u s t i n g ( ) == true ) 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 () ) ; 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 ) 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 () ) ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { 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 ( ) { @Override public void run ( ) { new E j e m p l o J L i s t ( ) . creaGUI ( ) ; } }) ; } }

Listado 11.8: Ejemplo de uso de JList

168

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

Por defecto, el Componente JList no tiene una barra de desplazamiento para poder visualizar los elementos de la lista, si queremos que la lista posea uno, lo tenemos que a nadir tal y como se muestra en las l neas 25 y 26. F jate que indicamos el Componente al que asociaremos la barra de desplazamiento en el momento de crear esta. Finalmente a nadimos la barra de desplazamiento a la ventana y no la lista original. Otros m etodos interesantes del Componente JList son Object getSelectedValue() el elementos actualmente seleccionado, si la lista es de selecci on u nica; y Object [] getSelectedValues() si se lista es de selecci on m ultiple. Con el componente JList acabamos la breve muestra de las posibilidades de creaci on de interfaces gr acos de usuario Swing. Esta secci on no pretende ser un exposici on exhaustiva de todas las posibilidades que nos proporciona Swing, lo que pretende mostrar es la t ecnica de c omo programar interfaces gr acos de usuario con el patr on de dise no Observable.

11.6.

El patr on de dise no Modelo/Vista/Controlador

Quiz as el patr on de dise no Modelo/Vista/Controlador sea uno de los m as utilizados en el desarrollo de proyectos inform aticos, tanto es as que incluso existe una adaptaci on al mundo de aplicaciones web de este patr on de dise no. Este patr on de dise no dene tres actores con las siguientes responsabilidades: Modelo es el responsable de mantener y gestionar los datos de la aplicaci on. Vista es la responsable del interfaz gr aco de usuario y la detecci on de eventos sobre los componentes. Controlador es quien hace corresponder la interacci on del usuario con posible cambios en el Modelo. Veamos , con un ejemplo, el papel de cada uno de estos actores. En la Figura 11.5 se muestra el interfaz gr aco de una aplicaci on que calcula la cuota mensual de una hipoteca. El usuario puede introducir los tres datos que se necesita para el c alculo en tres cajas de edici on de texto, y cuando pulsa el bot on Calcula aparece la nueva cuota en la parte inferior de la ventana. En este caso, el Modelo contiene los datos de la hipoteca: cantidad hipotecada, duraci on de la hipoteca, inter es del pr estamo y la cuota mensual. La Vista es la encargada de crear el interfaz gr aco y la detecci on de los eventos sobre el interfaz. El Controlador sabe que cuando el usuario pulsa el bot on Calcula, debe leer los datos de la hipoteca y envi arselos al Modelo para que este haga el c alculo. En la Figura 11.6 se muestra la din amica de este patr on de dise no, que se detalla en los siguiente pasos: 1. El usuario interacciona sobre la Vista. 2. La Vista informa al Controlador de lo ocurrido.

DE DISENO MODELO/VISTA/CONTROLADOR 11.6. EL PATRON

169

Figura 11.5: Un interfaz gr aco para el c alculo de la cuota mensual de una hipoteca.

Figura 11.6: Din amica del modelo MVC. 3. El Controlador decide que datos necesita de la Vista para llevar a cabo la tarea como respuesta a la interacci on del usuario. 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 es para visualizarlos. En el ejemplo del c alculo de la cuota mensual de una hipoteca esta din amica se concretar a del siguiente modo: 1. El usuario introduce la cantidad, el tiempo y el inter es de la hipoteca y pulsa el bot on Calcula. 2. La Vista informa al Controlador de que el usuario ha pulsado el bot on Calcula. 3. La l ogica del negocio programada en el Controlador indica que si el usuario pulsa el bot on Calcula se debe recuperar la cantidad, el tiempo y el inter es de la hipoteca que est an en la Vista. 4. El Controlador env a estos datos al Modelo para que calcule la nueva 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.

170

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

Figura 11.7: Diagrama UML para el patr on de dise no MVC. Aunque en un primer momento, este patr on puede resultar farragoso, o parecernos que hay muchas idas y venidas entre el c odigo de los actores, es todo lo contrario, gracias a esta divisi on de responsabilidades los posibles cambios en la implementaci on de uno de los actores es completamente transparente al resto. Imagina por ejemplo que hay un cambio en la Vista, si despu es de cambiar, la Vista sigue proporcion andonos los datos sobre los que efectuar el c alculo y le podemos seguir informando de los cambios en el Modelo para que se actualice, este cambio ser a totalmente transparente tanto para el Controlador como para el Modelo. F jate que para poder implementar este patr on de dise no, la Vista debe conocer tanto al Controlador como al Modelo, y por su parte el Controlador debe conocer tanto a la Vista como al Modelo. Por su lado, el u nico actor que necesita conocer el Modelo es a la Vista, de hecho, en la Figura 11.6 no hay ninguna echa que salga desde el Modelo hacia el Controlador. Que un actor tenga conocimiento de los otros implica que tendr a una referencia hacia el actor con el que necesita intercambiar mensajes. Para dejar a un m as clara la potencia de este patr on, vamos a implementar las referencias a una interface y no a una clase concreta, es decir, la funcionalidad que un actor ofrece la vamos a recoger en un interface y adem as tendremos la clase concreta que implementar a el interface, la Figura 11.7 muestra el esquema de clases citado. En el Ap endice B se encuentra el c odigo fuente de la aplicaci on del c alculo de la cuota mensual de una hipoteca.

Cuestiones.
1. Cuales son las ventajas y desventajas de utilizar el m etodo de conveniencia setDefaultCoseOperation(JFrame.EXIT ON CLOSE) para cerrar la aplicaci on cuando el usuario cierra la ventana?.

DE DISENO MODELO/VISTA/CONTROLADOR 11.6. EL PATRON

171

2. El c odigo del c alculo de la cuota mensual de una hipoteca mostrado en el Ap endice B sigue la estrategia de no enviar datos desde la Vista al Controlador cuando se produce el evento de pulsar el bot on Calcula, es decir, la Vista no env a la cantidad hipoteca, el tiempo ni el inter es, es el Controlador quien pide estos datos una vez que la Vista le informa de que el usuario puls o el bot on Calcula. Lo mismo ocurre cuando el Modelo cambia su estado, al informar a la Vista no le env a el nuevo valor de la cuota mensual de la hipoteca, simplemente le informa de que hay un nuevo valor disponible y es nalmente la Vista quien pide el nuevo valor al Modelo. Cuales son las ventajas de esta aproximaci on?. Cuales son las desventajas?.

Ejercicios.
1. Recupera el ejercicio de la agenda telef onica y crea un interfaz gr aco de usuario para ella.

Lecturas recomendadas.
El cap tulo 1 de la referencia [8] presenta el patr on de dise no MVC haciendo referencia al resto de patrones que utiliza. La referencia [3] tambi en presenta de modo riguroso y muy ameno el patr on de dise no MVC.

172

CAP ITULO 11. INTERFACES GRAFICAS DE USUARIO

Cap tulo 12

Applets
Contenidos
12.1. Qu e son los Applets? . . . . . . . . . . . . . . . 12.2. Ciclo de vida de un Applet . . . . . . . . . . . 12.3. C odigo HTML para contener un Applet . . . . 12.4. Lectura de par ametros de la p agina HTML . . 12.5. Convertir una aplicaci on Swing en un Applet 12.6. Comunicaci on entre Applets . . . . . . . . . . . . . . . . . . . . . . . 173 174 175 176 176 177

Introducci on
Para muchos de nosotros, el primer contacto con Java fue a trav es de los Applets, esos peque nos programas que se ejecutan dentro de una p agina web y con los que se puede interaccionar. Algunos ejemplos se pueden encontrar en las siguientes direcciones: http://www.falstad.com/mathphysics.html, http:// openastexviewer.net/web/thinlet.html. En este cap tulo se va a presentar la programaci on de Apples, sus caracter sticas, particularidades, y c omo, con poco trabajo extra podemos convertir nuestras aplicaciones Swing en Applets siempre que se cumpla una serie de restricciones.

12.1.

Qu e son los Applets?

Los Applets son aplicaciones Java que se ejecutan en el contexto de un navegador web. A trav es de c odigo HTML reservamos una zona de la p agina web para visualizar el Applet, y es el navegador web el encargado de descargar las clases del Applet desde la url especicada, iniciar una instancia de la m aquina virtual de Java y ejecutar el Applet. La seguridad, en el caso de los Applets, es un importante factor a tener en cuenta. Para ejecutar un Applet es necesario descargar desde la web las clases que se ejecutar an en nuestra m aquina. F jate el riesgo que en principio se corre si no hubiese restricciones de seguridad, descargas un programa que no sabes 173

174

CAP ITULO 12. APPLETS

quien ha escrito ni con qu e prop osito y lo ejecutas en tu m aquina. Si no hubiesen restricciones de seguridad un programa malicioso podr a acceder a tu disco duro para leer informaci on personal, o podr a borrar o modicar cheros, o escribir en 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 un otro servidor salvo aquel desde el que se descarg o. Un Applet no puede ejecutar aplicaciones en el cliente. Un Applet no puede acceder a la informaci on privada del usuario. Estas restricciones de seguridad y el hecho de que nalmente el Applet se ejecutar a en un M aquina Virtual Java los hacen muy seguros.

12.2.

Ciclo de vida de un Applet

Un Applet se ejecuta en el contexto de un navegador web y tiene fuertes restricciones de seguridad, tal y como hemos visto. El hecho de que los Applets se ejecuten en el contexto de un navegador web implica que su ciclo de vida no es el de una aplicaci on de escritorio, como las que ya hemos aprendido a programar. El ciclo de vida de un Applet est a directamente relacionado con las llamadas que el navegador web hace a m etodos del Applet. Para que una de nuestras clases sea un Applet debe extender a la clase JApplet que dene los siguientes m etodos relacionados con su ciclo de vida: public void init(), el navegador web llama a este m etodo cuando el Applet ha sido efectivamente cargado. Este m etodo es el primero que se invoca en un Applet. public void start(), el navegador web llama a este m etodo para indicarle que debe empezar su ejecuci on. public void paint(Graphics g), el navegador web llama a este m etodo cada vez que se debe dibujar el contenido del Applet, y nos permite el acceso al contexto gr aco de bajo nivel Graphics. Un detalle muy importante es que desde nuestro c odigo nunca llamaremos directamente a este m etodo, para forzar su llamada utilizaremos el m etodo public void repaint(). public void stop(), el navegador web llama a este m etodo para indicar que el Applet debe detener su ejecuci on, por ejemplo, cuando se abandona la p agina web que contiene el Applet. public void destroy(), el navegador web llama a este m etodo antes de eliminar el Applet de memoria, en cuyo caso se llamar a previamente al m etodo stop().

12.3. CODIGO HTML PARA CONTENER UN APPLET

175

Figura 12.1: Llamada a los m etodos de un Applet durante su ciclo de vida. La Figura 12.1 muestra gr acamente el orden de las llamadas entre los m etodos que constituyen el ciclo de vida de un Applet. Para programar de modo eciente un Applet debemos seguir estas reglas: los recursos que el Applet necesita para su ejecuci on deben ser creados en su m etodo init(), y esos mismos recursos deben ser liberados en su m etodo destroy(). Nunca llamaremos, desde nuestro c odigo, al m etodo paint(Graphics g) para forzar el dibujado del Applet, para ellos utilizaremos el m etodo repaint().

12.3.

C odigo HTML para contener un Applet

Para poder visualizar un Applet al cargar una p agina HTML debemos utilizar la etiqueta <html> como se muestra en el Listado 12.1. El atributo achive nos sirve para indicar el chero empaquetado de nuestra aplicaci on; con la etiqueta code especicamos la clase que implementa el Applet, como veremos en la siguiente secci on; y mediante las etiquetas width y height especicamos el ancho y alto dentro de la p agina web reservado para la visualizaci on del Applet.
<html> <head> < t i t l e >El 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> </ html>

Listado 12.1: C odigo HTML que muestra la aplicaci on de la hipoteca dentro de una p agina web. 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 agina web. hspace, el margen a la izquierda y derecha del Applet en unidades de p xeles.

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 on entre Applets que residen en la misma p agina web, como veremos en la Secci on 12.6.

12.4.

Lectura de par ametros de la p agina HTML

La etiqueta <applet> puede contener otras etiquetas de inter es adem as de las que ya hemos visto. Con la etiqueta <param> especicamos un par ametro con su valor, por ejemplo <param name="saludo"value="Hola/>. Desde el c odigo de nuestro Applet podremos leer los par ametros denidos dentro de la etiqueta Applet con el m etodo String getParameter(String nombreParametro) que recibe como argumento el nombre del par ametro que queremos leer ("saludo" en el ejemplo anterior). Esto nos permite denir par ametros de entrada a nuestro Applet sin necesidad de modicar el c odigo de nuestro Applet, en vez de ello, los deniremos en el c odigo HTML que contiene al Applet. En la Secci on 12.6 se mostrar a c omo hacer uso de esta t ecnica.

12.5.

Convertir una aplicaci on Swing en un Applet

Un Applet, a efectos pr acticos, es una aplicaci on Java con la particularidad de que se ejecuta en el contexto de un navegador web. Un Applet tiene una zona, dentro de la p agina web, donde se va a visualizar, y lo que podemos visualizar es, entre otras cosas un interfaz gr aco de usuario. Dicho de otro modo, si una aplicaci on Swing cumple con las restricciones de seguridad impuestas a los Applets, podremos, con pocas modicaciones, transformarla en un Applet. Este es un sencillo recetario para convertir una aplicaci on Swing en un Applet: 1. No podemos hacer uso de JFrame, en vez de esta clase utilizaremos JApplet. 2. Un Applet no tiene constructores, el c odigo dentro del constructor en la aplicaci on Swing lo escribiremos dentro del m etodo public void init() del Applet. 3. No se pueden utilizar m etodos de JFrame relativos al tama no de la ventana (setSize(...)), al t tulo de esta (setTitle(String titulo), o su posici on (setLocation(int, int), ya que la posici on y el tama no del Applet se especican dentro del c odigo HTML. 4. Un Applet no puede tener escuchadores de tipo WindowListener. Siguiendo estos sencillos pasos, el Listado 12.2 muestra c omo convertir la aplicaci on Swing del Listado B.7 del Ap endice B del c alculo de la cuota mensual de una hipoteca en un Applet.
1 2

package a p p l e t s . h i p o t e c a ;

ENTRE APPLETS 12.6. COMUNICACION


3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

177

import j a v a x . s w i n g . J A p p l e t ; import import import import import import gui gui gui gui gui gui . . . . . . hipoteca hipoteca hipoteca hipoteca hipoteca hipoteca . controlador . Controlador ; . controlador . ControladorImpl ; . modelo . Modelo ; . modelo . ModeloImpl ; . v i s t a . Vista ; . v i s t a . VistaImpl ;

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 { 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 ; @Override public void i n i t ( ) { V i s t a v i s t a = new V i s t a I m p l ( ) ; Modelo modelo = new ModeloImpl ( ) ; 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 ( ) ; modelo . s e t V i s t a ( v i s t a ) ; vista . setControlador ( controlador ) ; v i s t a . s e t M o d e l o ( modelo ) ; c o n t r o l a d o r . s e t M o d e l o ( modelo ) ; controlador . setVista ( vista ) ; setContentPane ( v i s t a . getContenedor ( ) ) ; } }

Listado 12.2: Applet con la aplicaci on del c alculo de la cuota mensual de una hipoteca Como tuvimos cuidado de aislar todo lo relativo al actor Vista siguiendo el patr on de dise no MVC, la transformaci on de aplicaci on Swing a Applet 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 on entre Applets

Applets que residen dentro de la misma p agina web pueden obtener referencias a los otros Applets dentro de la misma p agina y a trav es de estas referencias un Applet puede llamar a los m etodos de otro Applet dentro de la misma p agina web. Para ello utilizaremos el m etodo Applet getApplet(String nombre) que recibe como argumento el nombre del Applet del que queremos obtener una referencia. Recuerda que el nombre de un Applet lo podemos denir con el atributo name de la etiqueta <applet>. Este m etodo pertenece al contexto donde se est a ejecutando el Applet, que no es otra cosa que la propia p agina web. Para obtener este contexto desde un Applet utilizamos el m etodo public AppletContext getAppletContext(). El Listado 12.3 muestra un sencillo ejemplo de comunicaci on entre Applets residentes en la misma p agina web mostrado en el Listado 12.4. La Figura 12.2 muestra c omo se visualiza la p agina web en un navegador.
1 2 3 4 5 6 7 8 9

package a p p l e t s . c o m u n i c a c i o n ; import j a v a . awt . BorderLayout ; import j a v a . awt . e v e n t . A c t i o n E v e n t ; 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 ; import j a v a x . s w i n g . J A p p l e t ; import j a v a x . s w i n g . JButton ; import j a v a x . s w i n g . J L a b e l ;

178
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

CAP ITULO 12. APPLETS

import j a v a x . s w i n g . J P a n e l ; import j a v a x . s w i n g . J T e x t F i e l d ; 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 { 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 ; private J T e x t F i e l d j t f T u D i c e s ; private J L a b e l j l E l D i c e ; 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 ; 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 ( ) ) ; @Override public void i n i t ( ) { J P a n e l miEntrada = new J P a n e l ( ) ; miEntrada . add ( new J L a b e l ( " T u dices :" ) ) ; j t f T u D i c e s = new J T e x t F i e l d ( 5 0 ) ; miEntrada . add ( j t f T u D i c e s ) ; JButton j b E n v i a r = new JButton ( " E n v i a r " ) ; 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 ( ) ) ; miEntrada . add ( j b E n v i a r ) ; J P a n e l s u E n t r a d a = new J P a n e l ( ) ; 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 . . . . " ) ; s u E n t r a d a . add ( j l E l D i c e ) ; c o n t e n e d o r . add ( suEntrada , BorderLayout .SOUTH) ; c o n t e n e d o r . add ( miEntrada , BorderLayout .NORTH) ; setContentPane ( contenedor ) ; } 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 ) { j l E l D i c e . setText ( " M e n s a j e : " + mensaje ) ; repaint () ; } 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 { @Override 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 ) { i f ( e l O t r o == n u l l ) elOtro = ( ComunicacionApplets ) getAppletContext ( ) . getApplet ( getParameter ( " ElOtro " ) ) ; elOtro . recibeMensaje ( jtfTuDices . getText ( ) ) ; } } }

Listado 12.3: C odigo HTML de la p agina visualizada en la Figura 12.2

<html> <head> 3 < t i t l e >C o n v e r s a c i o n e n t r e A p p l e t s </ t i t l e > 4 </head >


1 2 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

<body> <a p p l e t 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 " 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 " width =800 h e i g h t =70 name=" S u p e r i o r "> <param name=" E l O t r o " v a l u e=" I n f e r i o r " /> 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 > <br /> <a p p l e t 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 " 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 " width =800 h e i g h t =70 name=" I n f e r i o r "> <param name=" E l O t r o "

ENTRE APPLETS 12.6. COMUNICACION

179

Figura 12.2: Llamada a los m etodos de un Applet durante su ciclo de vida.

27 28 29 30 31

v a l u e=" S u p e r i o r " /> 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> </html>

Listado 12.4: Applet con la aplicaci on del c alculo de la cuota mensual de una hipoteca

Ejercicios.
1. Convierte la aplicaci on de la Agenda en un Applet para que puedas interaccionar con ella a trav es de un navegador web.

Lecturas recomendadas.
En esta direcci on web http://download.oracle.com/javase/tutorial/ deployment/applet/index.html podr as encontrar toda la informaci on necesaria para programar Applets.

180

CAP ITULO 12. APPLETS

Cap tulo 13

Control de errores con MyLyn y Bugzilla


Contenidos
13.1. Sistema de control de tareas MyLyn . . . . . . . . 13.1.1. Cual es el objetivo de MyLyn . . . . . . . . . . . . . 13.1.2. Trabajar con MyLyn . . . . . . . . . . . . . . . . . . 13.1.2.1. Trabajar con tareas . . . . . . . . . . . . . 13.1.2.2. Trabajar con categor as . . . . . . . . . . . 13.1.2.3. Trabajar con Working Sets . . . . . . . . . 13.2. Sistema de gesti on de errores Bugzilla . . . . . . . 13.2.1. Cual es el objetivo de Bugzilla . . . . . . . . . . . . 13.2.2. Instalaci on de Bugzilla . . . . . . . . . . . . . . . . . 13.2.3. Trabajar con Bugzilla . . . . . . . . . . . . . . . . . 13.3. Acceso a Bugzilla desde MyLyn y Eclipse . . . . . 13.3.1. Benecios de la combinaci on de Bugzilla y MyLyn desde Eclipse . . . . . . . . . . . . . . . . . . . . . . 13.3.2. Trabajo con MyLyn y Bugzilla desde Eclipse . . . . 13.3.2.1. A nadir errores a Bugzilla desde Eclipse . . 13.3.2.2. Recuperar errores desde Bugzilla como tareas en MyLyn . . . . . . . . . . . . . . . . 182 182 182 182 186 187 188 188 188 195 199 201 201 201 201

Introducci on
Los Entornos de Desarrollo Integrado son una excelente herramienta para la creaci on y mantenimiento de c odigo. Sin embargo, cuando se trabaja en proyectos cuyo c odigo est a organizado en un gran n umero de clases dentro de sus correspondiente paquetes, navegar por le c odigo para encontrar un m etodo o una clase puede llegar a consumir mucho tiempo. MyLyn es una herramienta, integrada en Eclipse a trav es de un plug-in, que oculta el c odigo que no es relevante a la tarea que estamos llevando a cabo. Aunque MyLyn es mucho m as que eso. 181

182CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA Por otro lado, una tarea importante en todo proyecto inform atica es el seguimiento y gesti on de errores en el c odigo. Bugzilla es una excelente herramienta que cumple esta funci on. Quiz as, lo mejor de ambas herramientas es la posibilidad de combinarlas dentro de Eclipse de tal manera que podemos inicial el seguimiento de un error en Bugzilla desde el propio Eclipse y darlo de alta como una tarea en MyLyn para poder centrar nuestra atenci on s olo en el c odigo relativo a ese error.

13.1.

Sistema de control de tareas MyLyn

MyLyn es una potente herramienta que se encuentra en el paquete b asico de Eclipse. Al descargarnos Eclipse nos estamos descargando tambi en esta herramienta.

13.1.1.

Cual es el objetivo de MyLyn

Cuando trabajamos en equipo y en grandes proyectos es usual que el c odigo fuente del proyecto est e organizado en un gran n umero de paquetes, dentro de los cuales encontramos un gran n umero de clases. El n umero total de cheros con c odigo fuente puede ser muy elevado. Generalmente, cuando desarrollamos una tarea dentro de un proyecto de gran envergadura, s olo son relevantes a esta tarea un n umero reducido de cheros frente al total. Si el entorno de desarrollo que estamos utilizando nos presenta todos los cheros del proyecto, podemos perder 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 olo en el c odigo de un proyecto sobre el que estamos trabajando, ocultando el resto del c odigo del proyecto que no es relevante a la tarea que estamos llevando a cabo. Pero MyLyn no es s olo eso, si no un sistema de control de trabajo que podemos utilizar bien de forma local o bien en conexi on con un sistema de control de errores. Y es en este u ltimo caso cuando Bugzilla se convierte en una herramienta imprescindible. En MyLyn la unidad b asica de trabajo es la tarea. Las tareas se pueden organizar en categor as y estas a su vez se pueden agrupar en grupos de trabajo. Veamos c omo trabajar con cada uno de estos nuevos conceptos.

13.1.2.

Trabajar con MyLyn

MyLyn dispone de una vista propia en Eclipse. El nombre de esta vista es Task List. Si no aparece esta vista por defecto, puedes hacerla visible seleccionando la opci on que se muestra en la Figura 13.1. La nueva vista que se abrir a en Eclipse se muestra en la Figura 13.2. 13.1.2.1. Trabajar con tareas

Lo primero que vamos a hacer es crear una nueva tarea, para ello, despliega el icono que se muestra en la Figura 13.3 y selecciona la opci on New Task.... A continuaci on se nos abrir a la ventana que se muestra en la Figura 13.4 donde debemos seleccionar la opci on por defecto Local. Lo que estamos haciendo en este punto es indicarle a MyLyn que la tarea que estamos creando es local a

13.1. SISTEMA DE CONTROL DE TAREAS MYLYN

183

Figura 13.1: Opci on de men u para abrir la vista de MyLyn.

Figura 13.2: Aspecto de la vista Task List de MyLyn.

184CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.3: Creaci on de una nueva tarea en MyLyn. nuestra m aquina de desarrollo, no estamos utilizando ning un repositorio externo. La creaci on de tareas en repositorios externos la cubriremos en la secci on 13.3. Tras seleccionar el repositorio local, se nos abrir a la ventana mostrada en la Figura 13.5 donde podemos introducir las propiedades de la nueva tarea. En esta ventana podemos denir las siguiente propiedades para la nueva tarea: Nombre de la tarea. Status : el estado de la tarea, que puede ser Completo o Incompleto. Scheduled : cuando est a planicado que trabajaremos en la tarea. Due : en que fecha debe estar completada la tarea. Estimate : n umero de horas estimadas de dedicaci on a la tarea. Campo de comentarios. Una vez denidas 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 a en la carpeta Uncategorized. En esta carpeta se a naden, de modo autom atico, todas las tareas que no se han asignado a ninguna categor a. Antes de ver c omo crear nuevas categor as veamos cual es el trabajo b asico con las tareas. MyLyn nos permite concentrarnos s olo en el c odigo que necesitamos para desarrollar una determinada tarea. Para activar una tarea pulsa el icono con aspecto de c rculo, con relleno blanco la primera vez, que se encuentra a la izquierda del nombre de la tarea. Ver as que ocurren varios cambios en Eclipse, por un lado, el aspecto del icono a la izquierda de la tarea cambia de aspecto para mostrar una esfera sombreada en gris. Y lo que es m as importante, la vista Package Explorer ha ocultado toda la informaci on relativa a la organizaci on de los paquetes y las clases en ella contenida. Observa tambi en que el bot on de tarea activa de MyLyn mostrado en la Figura 13.6 est a pulsado. Qu e ha ocurrido al activar la tarea? Por qu e han desaparecido todas las clases de la vista Package Explorer ? Como hemos comentado, MyLyn nos permite concentrarnos s olo en el c odigo relacionado con la tarea que activa en cada momento. C omo lo consigue? Ocultando el c odigo que no hemos modicado o consultado en el desarrollo de la tarea. Como es la primera vez que activamos la tarea, y no hemos modicado o consultado ning un c odigo, MyLyn oculta toda la jerarqu a de paquetes. Para desactivar el ltro, pulsa sobre el icono mostrado en la Figura 13.6, se mostrar a de nuevo la jerarqu a completa de clases. Abre

13.1. SISTEMA DE CONTROL DE TAREAS MYLYN

185

Figura 13.4: Selecci on del repositorio donde se a nadir a la nueva tarea.

Figura 13.5: Denici on de las propiedades de una nueva tarea en MyLyn.

186CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.6: Tarea activa en MyLyn MyLyn.

el c odigo de una clase y activa de nuevo el ltro. Ver as que todas las clases se ocultan excepto aquella que aparece en el editor. Realiza alg un cambio en el c odigo de la clase, como a nadir alg un comentario, ver as que en la vista Package Explorer aparecen s olo los m etodos en los que has a nadido comentarios, el resto de m etodos de la clase permanece oculto. Esta es la base de la potencia de MyLyn presentar s olo el c odigo de las clases que hemos modicado o consultado al desarrollar una determinada tarea. Observa que en el editor tambi en aparece el icono de ltro de MyLyn, si lo activas, los m etodos sobre los que no has trabajado al desarrollar la tarea se colapsar an, s olo se mostrar a el c odigo de aquellos m etodos que has modicado o consultado. Finalmente, si desactivas la tarea, pulsando en el icono a la izquierda de la tarea en la vista Task List, Eclipse te mostrar a todos los proyectos, paquetes y clases en la vista Package Explorer. Si activas de nuevo la tarea, Eclipse te mostrar a s olo el c odigo relacionado con ella. MyLyn recuerda el estado en que qued o la informaci on de trabajo relacionada con la tarea en el momento de su desactivaci on.

13.1.2.2.

Trabajar con categor as

Las categor as son agrupaciones de tareas. Una tarea s olo puede pertenecer a una categor a. Las categor as nos sirven para estructurar las tareas. Para un proyecto concreto podemos, por ejemplo, crear una categor a relativa a las tareas relacionadas con la creaci on del interface gr aco de usuario, otra categor a relativa a las tareas relacionadas con el modelo de datos de nuestra aplicaci on y as sucesivamente. Para crear una nueva categor a despliega la opci on de creaci on de nuevos elementos en la vista Task List y selecciona la opci on New Cathegory tal y como muestra la Figura 13.7. Como nombre de la categor a introduzcamos, por ejemplo, Modelo de datos. Al pulsar el bot on Ok observar as que se ha creado una carpeta con ese nombre en la vista Task List. Para a nadir, o cambiar de ubicaci on, una tarea ya existente, simplemente pincha sobre la tarea y arr astrala hasta la categor a deseada. Ver as que la descripci on de la tarea se actualiza para dar cuenta de la categor a a la que pertenece. Para crear una tarea dentro de una determinada categor a, simplemente selecciona la categor a antes de crear la tarea. Por defecto, la tarea se crear a en la categor a actualmente seleccionada.

13.1. SISTEMA DE CONTROL DE TAREAS MYLYN

187

Figura 13.7: Creaci on de una nueva categor a en MyLyn.

Figura 13.8: Creaci on de un Workin Set en MyLyn.

13.1.2.3.

Trabajar con Working Sets

Por encima de las categor as existe un nuevo escal on que nos permite organizarlas en nuevos grupos. F jate que las tareas s olo pueden pertenecer a una u nica categor a, una tarea no puede estar en m as de una categor a. Los Working Sets nos permiten agrupar categor as, y una categor a puede pertenecer a m as de un Working Set. La pertenencia de una categor a a un Working Set no es exclusiva. Supongamos que tenemos una jerarqu a de tareas y categor as como la mostrada en la Figura 13.8. Pulsa el tri angulo negro a la izquierda del enlace All para desplegar las opciones y selecciona Edit. Se abrir a la ventana de la Figura 13.9. Pulsa el bot on New para crear un nuevo Working Set, se abrir a la ventana mostrada en la Figura 13.10. Introduce un nombre para este nuevo Working Set y selecciona las categor as Vista y Modelo de datos. Para acabar pulsa el bot on Finish. Volver as a la ventana de la Figura 13.8 pero esta vez podr a ver el nuevo Working Set reci en creado, pulsa el bot on Ok. Ahora, si despliegas de nuevo la lista de Working Sets (tri angulo a la izquierda del enlace All de la Figura 13.8 ver as el nombre del nuevo Working Set, si lo seleccionas s olo te aparecer an las categor as Modelo de datos y Vista que son las que has incluido en este Working

188CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.9: Selecci on y edici on de un Workin Set en MyLyn. Set, el resto de categor as no se visualiza. Puedes probar a crear un nuevo Working Set, ll amalo Relaci on entre Modelo y Controlador y a nade a el s olo las categor as Modelo y Controlador. Como ves, la categor a Modelo est a dentro de dos Working Sets.

13.2.

Sistema de gesti on de errores Bugzilla

Bugzilla es una herramienta libre y de c odigo abierto utilizada por grandes compa n as, como Mozilla, Apache o Eclipse. Se puede trabajar con Bugzilla desde un navegador web o bien desde un cliente como MyLyn si se dispone del conector adecuado. Afortunadamente, y no es de extra nar, Eclipse proporciona por defecto este conector.

13.2.1.

Cual es el objetivo de Bugzilla

Bugzilla es un sistema de seguimiento de errores. Durante el ciclo de vida del software, una de las etapas b asicas es la detecci on y soluci on de errores. Bugzilla nos permite gestionar y automatizar el seguimiento de errores hasta su resoluci on nal.

13.2.2.

Instalaci on de Bugzilla

En el momento de escribir estas p agina, la u ltima versi on estable de Bugzilla es la 4.0, que se puede descargar desde http://www.bugzilla.org. En esta secci on se muestra c omo instalar Bugzilla en Ubuntu 10.10. Para otros sistemas operativos o versiones de Linux consultar los detalles en la p agina web de Bugzilla.

DE ERRORES BUGZILLA 13.2. SISTEMA DE GESTION

189

Figura 13.10: Propiedades de un Workin Set en MyLyn. Antes de instalar Bugzilla necesitamos instalar algunos paquetes de los que depende Bugzilla. El primero de ellos es perl, para comprobar si tenemos perl instalado abrimos un terminal y escribimos: $perl -v This is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multi Copyright 1987-2009, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using "man perl" or "perldoc perl". If you have access to the Internet, point your browser at http://www.perl.org/, the Perl Home Page. Si podemos ver lo anterior es que tenemos la versi on 5.10.1 de perl instalada, Bugzilla necesita al menos la versi on 5.8. Si perl no est a instalado lo podemos instalar tecleando en el terminal: $sudo apt-get install perl F jate que para instalar nuevos paquetes en linux necesitamos permiso de superusuario, de ah que utilicemos sudo. Antes de proceder a la instalaci on se nos preguntar a por la clave de superusuario.

190CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA Lo siguiente que debemos instalar, si no lo tenemos ya instalado, es un gestor de bases de datos. Vamos a utilizar MySQL aunque tambi en es posible utilizar PostgreSQL. Para comprobar si MySQL est a instalado escribimos de nuevo en un terminal: $mysql --version mysql Ver 14.14 Distrib 5.1.41, for debian-linux-gnu (i486) using readline 6.1 Lo anterior nos indica que tenemos instalada la versi on 5.1.41 de este gestor de bases de datos. Si no lo tenemos, lo podemos instalar tecleando: apt-get install mysql-admin mysql-client mysql-server Lo anterior nos instalar a tanto el gesto de bases de datos como el cliente y las herramientas de administraci on. Durante la instalaci on de MySQL se nos pedir a que denamos la clave de acceso del administrador. Esta clave de acceso la utilizaremos m as tarde en la conguraci on de Bugzilla. El siguiente paso es comprobar si tenemos instalado el servidor web Apache. Para ello, de nuevo, escribe en un terminal: $apache2 -v Server version: Apache/2.2.16 (Ubuntu) Server built: Nov 18 2010 21:17:43 En este caso, el mensaje nos indica que tenemos instalado el servidor web Apache en nuestro sistema en su versi on 2.2.16. Si no fuese el caso lo puedes instalar tecleando en el terminal $sudo apt-get install apache2 En este punto, ya tenemos instalados todos los paquetes necesarios para poder trabajar con Bugzilla. Lo siguiente es descargar y congurar Bugzilla. El directorio por defecto donde el servidor web Apache busca los cheros solicitados es, en el caso de la distribuci on de Linux Ubuntu 10.10, /var/www1 . Sit uate en ese directorio y descarga Bugzilla escribiendo en un terminal lo siguiente: $sudo wget http://ftp.mozilla.org/pub/mozilla.org/ webtools/bugzilla-4.0.tar.gz Al acabar la descarga ver as que tienes un chero comprimido con nombre bugzilla-4.0.tar.gz. Para descomprimir este chero escribe en el terminal: $ tar -xvf bugzilla-4.0.tar.gz Observar as que se ha creado el directorio bugzilla-4.0. El siguiente paso para tener Bugzilla a punto es instalar los m odulos perl que necesita Bugzilla, para ello sit uate en el directorio /var/www/bugzilla-4.0 y escribe en el terminal: $sudo ./checksetup.pl ?check-modules
1 Para

otras distribuciones de Linux y sistemas operativos consultar la documentaci on.

DE ERRORES BUGZILLA 13.2. SISTEMA DE GESTION

191

Te aparecer a una lista con los m odulos necesarios y los opcionales que necesita Bugzilla. Tal y como se indica al nal de la lista de m odulos, puedes instalar todos los necesarios escribiendo en el terminal: $sudo /usr/bin/perl install-module.pl ?all Para comprobar que tenemos todos los m odulos necesarios instalados escribe de nuevo en el terminal: $sudo ./checksetup.pl ?check-modules Si alguno de los m odulos necesarios no se ha instalado escribe de nuevo en un terminal: $sudo /usr/bin/perl install-module.pl ?all En este punto necesitamos instalar los m odulos perl necesarios para Apache. Los instalamos escribiendo en el terminal: $sudo apt-get install libapache2-mod-perl2 libapache2-mod-perl2-dev libapache2-mod-perl2-doc $ sudo /usr/bin/perl install-module.pl Apache2::SizeLimit Con todo esto ya podemos continuar con la instalaci on de Bugzilla, escribe en el terminal: $sudo ./checksetup.pl Con ello, entre otras cosas, se habr a creado el chero localconfig en el directorio /var/www/bugzilla-4.0. Edita este chero y modica las siguiente variables de Bugzilla : $webservergroup = ?www-data?; $db_pass = ?clave de administrado de MySQL?; El siguiente paso es crear la base de datos que manejar a Bugzilla. Para crear esta base de datos utilizaremos el cliente de MySQL que ya hemos instalado. Escribe en un terminal: $ sudo mysql -u root -p Se te solicitar a la clave de administrador que has introducido en la instalaci on de MySQL. Una vez introducida con exito nos encontraremos en el ambiente del cliente de MySQL. Para crear la base de datos que manejar a Bugzilla escribe: mysql> create database bugs; Query OK, 1 row affected (0.00 sec) Para ver todas las bases de datos que gestiona MySQL escribe:

192CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA mysql> show databases; +-----------+ | Database | +-----------+ | information_schema | | bugs | | mysql | +-----------+ 3 rows in set (0.00 sec) Ver as que efectivamente tenemos la base de datos llamada bugs creada. Lo siguiente es asignar todos los privilegios para trabajar con esta base de datos al administrador root, para ello escribe: mysql> GRANT ALL PRIVILEGES ON bugs.* TO root@localhost IDENTIFIED BY ?clave de administrador?; mysql> FLUSH PRIVILEGES; Para salir del cliente de MySQL escribe: mysql> quit; De regreso al terminal vamos a continuar con la instalaci on de Bugzilla, escribe lo siguiente en el terminal: $ ./checksetup.pl Este script de perl crear a, entre otras cosas, todas las tablas necesarias en la base de datos bugs y congurar a Bugzilla. Tambi en se nos pedir a que introduzcamos el correo electr onico y clave de acceso del Administrador de Bugzilla. El nombre de usuario en Bugzilla debe ser un correo electr onico. El siguiente paso es congurar Apache para que reconozca el directorio donde tenemos instalado Bugzilla, para ello sit uate en el directorio /etc/apache22 y edita el chero httpd.conf y escribe en el lo siguiente: Alias /bugzilla "/var/www/bugzilla-4.0" <Directory /var/www/bugzilla-4.0> AddHandler cgi-script .cgi Options +Indexes +ExecCGI DirectoryIndex index.cgi AllowOverride Limit </Directory> En este punto s olo nos queda reiniciar el servidor web Apache escribiendo lo siguiente en el terminal: $sudo /etc/init.d/apache2 restart Y si no tenemos ning un problema ya podemos abrir un navegador web para conectarnos a Bugzilla escribiendo como url la siguiente http://localhost/

DE ERRORES BUGZILLA 13.2. SISTEMA DE GESTION

193

Figura 13.11: P agina de inicio de Bugzilla.

Figura 13.12: P agina de introducci on de usuario y clave de Bugzilla. bugzilla. Deberemos ver la p agina de inicio de Bugzilla tal y como se muestra en la siguiente Figura 13.11: Una vez ingresado en Bugzilla utilizando el correo electr onico del administrador como usuario y la clave que hemos denido en la instalaci on de de Bugzilla, se presentar a una pantalla como la mostrada en la Figura 13.12, seleccionamos el enlace Administration para acabar de congurar Bugzilla. En la nueva pantalla mostrada en la Figura 13.13 seleccionamos el enlace Parameters lo que nos dar a paso a la pantalla mostrada en la Figura 13.14 En esta pantalla, los u nicos par ametros que es necesario congurar son urlbase y cookiepath. Como valor de urlbas escribiremos la url de nuestro servidor, en el ejemplo se muestra http://localhost/bugzilla/ lo que indica que estamos utilizando Bugzilla en el servidor local, no tendremos acceso a este servidor desde otra m aquina. Si Bugzilla estuviese instalado en
2 El directorio donde se encuentra el chero httpd.conf puede ser distinto dependiendo de la distribuci on de Linux o del sistema operativo.

194CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.13: P agina de administraci on de Bugzilla.

Figura 13.14: P agina de denici on de par ametros de Bugzilla.

DE ERRORES BUGZILLA 13.2. SISTEMA DE GESTION

195

Figura 13.15: Lista de usuarios en Bugzilla. un servidor con nombre mi.servidor.com el valor del par ametro urlbase ser a http://mi.servidor.com/bugzilla/. Como valor de cookiepath escribiremos /bugzilla/. Finalmente pulsamos el bot on Sava changes.

13.2.3.

Trabajar con Bugzilla

Como ya se hemos comentado, Bugzilla es un sistema de gesti on de errores con interfaz web. El n ucleo de trabajo con Bugzilla es el proyecto. En Bugzilla se pueden crear tantos proyectos como sea necesario. Una vez creado un proyecto, todos los usuarios que est en autorizados a dar de alta los errores encontrados en el proyecto pueden empezara a hacerlo. Por lo tanto, lo primero que vamos a hacer es crear un nuevo proyecto. Despu es crearemos un usuario y le asignaremos privilegios para dar de alta los errores encontrados en el proyecto reci en creado. Todo este proceso lo vamos a hacer con la cuenta de Administrador de Bugzilla. Ingresa en Bugzilla pulsando sobre el enlace Log In que se muestra en la Figura 13.11, se te solicitar a un nombre de usuario y su clave de acceso. Como ya hemos comentado, los nombre de usuario en Bugzilla deben ser direcciones de correo electr onico, debemos introducir la direcci on de correo electr onico de Administrador que introdujimos en la conguraci on de Bugzilla y su clave. Como en el caso de la conguraci on de los par ametros de Bugzilla, seleccionamos en enlace Administration y esta vez, dentro de esta pantalla, seleccionamos el enlace Users, lo que nos dar a paso a la pantalla mostrada en la Figura 13.15, donde seleccionamos el enlace add a new user, se nos mostrar a la pantalla de la Figura 13.16 donde introduciremos los datos del nuevo usuario. F jate que como nombre de usuario se debe introducir una direcci on de correo electr onico. Finalmente pulsamos el bot on Add. Lo siguiente que vamos a hacer es crear un nuevo producto. Como ya se ha comentado, los errores se dan de alta en un determinado producto, puedes pensar que los productos son tus proyectos. Para crear un nuevo producto, vuelve a la p agina Administration y selecciona el enlace Products, ver as una p agina como la de la Figura 13.17, por defecto hay un producto de prueba creado, llamado TestProduct. Vamos a a nadir un nuevo producto selecciona el enlace Add lo que te llevar a a la nueva p agina mostrada en la Figura 13.18, introduce los par ametros del nuevo producto y pulsa el bot on Add, esto te llevar a a la nueva p agina mostrada en la Figura 13.19, en esta p agina se nos indica que debemos crear al menos un componente dentro del nuevo producto, puedes pensar que los componentes de los productos son como las tareas de tu proyecto. Selecciona

196CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.16: P agina de creaci on de un nuevo usuario en Bugzilla.

Figura 13.17: Lista de productos de Bugzilla. el enlace Edit components, lo que te llevar a a la p agina mostrada en la Figura 13.20. Selecciona el enlace Add con lo que te llevar a a la p agina de denici on del nuevo componente mostrada en la Figura 13.21, introduce en ella los datos del nuevo componente y pulsa el bot on Add lo que te dar a entrada de nuevo a la p agina donde se muestra los componentes de un producto. Ahora ver as que el componente ComponentePrueba se ha a nadido a la lista. Resumiendo lo que he hemos hecho hasta este momento: 1. Hemos congurado Bugzilla. 2. Hemos creado un nuevo usuario.

Figura 13.18: Propiedades de un producto en Bugzilla.

DE ERRORES BUGZILLA 13.2. SISTEMA DE GESTION

197

Figura 13.19: Producto reci en creado en Bugzilla. Debemos crear al menos un componente en este producto.

Figura 13.20: Lista de los componentes de un producto en Bugzilla.

Figura 13.21: Propiedades de un nuevo componente en Bugzilla.

198CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.22: Lista de los productos en Bugzilla en los que podemos dar de alta nuevos errores.

Figura 13.23: Denici on de los par ametros del error que se est a dando de alta en Bugzilla. 3. Hemos creado un nuevo producto (proyecto). 4. Hemos a nadido un nuevo componente (tarea) al producto (proyecto). Ahora ya podemos ingresar a Bugzilla con nuestro nuevo usuario y dar de alta un error en el componente reci en creado. Para ello sal de Bugzilla ya que hemos ingresado como administradores, e ingresa de nuevo pero esta vez con el usuario y clave reci en creados. En la p agina de inicio de Bugzilla selecciona el icono con leyenda File a Bug, lo que te llevar a a la nueva p agina mostrada en la Figura 13.22 donde seleccionar as el producto donde quieres dar de alta el error. En nuestro caso, seleccionamos el enlace NuevoProducto, lo que nos llevar a a la p agina mostrada en la Figura 13.23 y pulsa el bot on Submit Bug, lo que te dar a paso a la p agina mostrada en la Figura 13.24 donde se muestran todos los detalles del error reci en creado. Esta u ltima contiene todos los detalles del error, y a medida que trabajemos sobre este error y a nadamos nueva informaci on, toda ella aparecer a en esta pantalla. Para completar esta breve introducci on al uso de Bugzilla realicemos una b usqueda sobre la base de datos de errores. Para ello vuelve a la p agina inicial de Bugzilla y selecciona el icono que tiene como leyenda Search, y en la nueva p agina seleccionemos el enlace Advanced Search, en este momento se nos mostrar a la p agina de la Figura 13.25. En esta p agina seleccionemos el producto

13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE

199

Figura 13.24: Informaci on sobre el error reci en creado en Bugzilla. NuevoProducto y dejemos el resto de campos tal y como est an, esta consulta se interpreta como ((Busca todos los errores dados de alta en el producto NuevoProducto)), pulsa el bot on Search y ver a la p agina mostrada en la Figura 13.26 con los resultados de la b usqueda. S olo aparece un u nico error y al pulsar sobre el identicador de este error volveremos a la p agina de informaci on detallada sobre el. Hasta aqu hemos visto el trabajo b asico con Bugzilla como una potent sima herramienta de control y gesti on de errores, pero sin duda, la potencia de esta herramienta se magnica en combinaci on con MyLyn, tal y como se muestra en la siguiente secci on.

13.3.

Acceso a Bugzilla desde MyLyn y Eclipse

Tanto MyLyn como Bugzilla son herramientas muy u tiles en el desarrollo de software utilizadas de modo independiente. Pero sin duda, la posibilidad de combinar ambas soluciones en Eclipse nos facilita enormemente el desarrollo de software y el seguimiento de errores. Hemos visto c omo MyLyn nos permite denir tareas y concentrar nuestra atenci on u nicamente en el c odigo relacionado con ellas. Por otro lado, Bugzilla es un potente sistema de seguimiento de errores. Como desarrolladores de software, algunas de nuestras tareas consisten en solucionar errores en las aplicaciones que desarrollamos, podemos ver la resoluci on de cada error como una tarea espec ca. Ser a muy interesante poder combinar la potencia de MyLyn con la de Bugzilla para trabajar en la resoluci on de errores como si se tratase de otra

200CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.25: B usqueda de errores en Bugzilla.

Figura 13.26: Resultado de la b usqueda de errores en Bugzilla.

13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE tarea cualquiera.

201

13.3.1.

Benecios de la combinaci on de Bugzilla y MyLyn desde Eclipse

La principal ventaja de poder trabajar en Eclipse con Bugzilla a trav es de MyLyn es que podemos realizar el seguimiento de los errores como si de otra tarea cualquiera se tratase. En MyLyn podemos denir una consulta ha Bugzilla de tal modo que al dar de alta un nuevo error este nos aparezca como una nueva tarea en MyLyn. Otra ventaja importante es que podemos trabajar con Bugzilla directamente desde Eclipse sin necesidad de abandonar el entorno de desarrollo para realizar el seguimiento de los errores.

13.3.2.

Trabajo con MyLyn y Bugzilla desde Eclipse

En esta secci on vamos a presentar los fundamentos del trabajo conjunto desde Eclipse con MyLyn y Bugzilla. 13.3.2.1. A nadir errores a Bugzilla desde Eclipse

Para crear un nuevo error en Bugzilla desde Eclipse crea una nueva tarea en la vista Task List tal y como se muestra en la Figura 13.3. Cuando se abra la ventana mostrada en la Figura 13.4 pulsa esta vez sobre el bot on Add Task Repository. Lo que vamos a hacer esta vez, es crear un repositorio hacia Bugzilla. Al pulsar sobre el bot on Add Task Repository se abrir a la ventana mostrada en la Figura 13.27, selecciona Bugzilla y pulsa el bot on Next 3 . En la nueva ventana que se abrir a, introduce los datos de conexi on al repositorio de Bugzilla como se muestra en la Figura 13.28. Una vez rellenados los datos de conexi on es u til pulsar el bot on Validate Settings para comprobar que se puede establecer la conexi on con el repositorio. Si la conexi on se establece podemos pulsar el bot on Finish, de lo contrario, corrige los datos de conexi on y vuelve a pulsar el bot on Finish, ver as la ventana de la Figura 13.29 donde aparecer a el repositorio reci en creado Errores. Pulsa de nuevo el bot on Finish con lo que se abrir a una solapa en Eclipse para denir las propiedades del nuevo error. Rell enala con los datos de un error tal y como muestra la Figura 13.30. Finalmente pulsa el bot on Submit con lo que se enviar a el nuevo error al repositorio Bugzilla. Como ejercicio puedes probar a buscar el nuevo error desde un navegador web en Bugzilla. 13.3.2.2. Recuperar errores desde Bugzilla como tareas en MyLyn

Por u ltimo, veamos c omo podemos crear una consulta desde MyLyn para que nos aparezcan como tareas en la vista Task List los errores presentes en el repositorio de Bugzilla. Para ello seleccionemos el bot on New Query que se muestra en la Figura 13.3 y en la ventana que se abrir a seleccionemos el repositorio creado en la secci on anterior Errores, y pulsemos el bot on Next.
3 Es posible que tengas que actualizar MyLyn para trabajar con la versi on 4.0 de Bugzilla, consulta la direcci on http://www.eclipse.org/mylyn/new/ para conocer los detalles.

202CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.27: Creaci on de un nuevo repositorio en MyLyn.

Figura 13.28: Datos del repositorio Bugzilla.

13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE

203

Figura 13.29: Repositorio Errores reci en creado. En la nueva ventana que se abrir a tenemos dos opciones: Create query using form. Create query from existing URL. La opci on que nos interesa es la primera, que aparece seleccionada por defecto, y pulsemos el bot on Next. En la nueva ventana que se abrir a introduce los par ametros de la consulta tal y como muestra la Figura 13.31, y nalmente pulsa el bot on Finish, en la vista Task List podr as ver los errores importados a MyLyn desde Bugzilla, tal y como se muestra en la Figura 13.32. Ahora podr as trabajar con ellos como con cualquier otra tarea local, adem as de disponer de todos los campos que ofrece el conector a Bugzilla.

Lecturas recomendadas.
El cap tulo 25 de la referencia [13] est a dedicado enteramente a MyLyn. En la direcci on http://wiki.eclipse.org/index.php/Mylyn/User_Guide se puede encontrar un manual en l nea completo para el trabajo con MyLyn. En la direcci on http://www.eclipse.org/mylyn/start/ se pueden encontrar otros enlaces de gran inter es como v deo tutoriales sobre MyLyn, muy aconsejables de ver.

204CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Figura 13.30: Datos del repositorio Bugzilla. El cap tulo 27 de la referencia [13] est a dedicado enteramente a Bugzilla La direcci on web de Bugzilla es http://www.bugzilla.org/, all podr as encontrar gran cantidad de informaci on sobre esta herramienta.

13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE

205

Figura 13.31: Datos del repositorio Bugzilla.

Figura 13.32: Datos del repositorio Bugzilla.

206CAP ITULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA

Cap tulo 14

Programaci on concurrente con Hilos


Contenidos
14.1. Qu e es un hilo? Utilidades. Consideraciones sobre el uso de hilos . . . . . . . . . . . . . . . . . . . 14.2. Creaci on de hilos en Java . . . . . . . . . . . . . . 14.2.1. Creaci on de un Hilo extendiendo a la clase Thread . 14.2.2. Creaci on de un Hilo mediante una clase interna . . . 14.2.3. Creaci on de un Hilo mediante una clase interna an onima . . . . . . . . . . . . . . . . . . . . . . . . . 14.3. Ciclo de vida de un hilo . . . . . . . . . . . . . . . 14.4. Control de hilos . . . . . . . . . . . . . . . . . . . . 14.5. Sincronizaci on . . . . . . . . . . . . . . . . . . . . . 14.5.1. Sincronizac on utilizando los cerrojos intr nsecos . . . 14.5.2. Sincronizaci on utilizando el interface Lock . . . . 208 209 209 210 211 212 213 215 215 218

Introducci on
La programaci on concurrente es nativa en Java, esto quiere decir que no necesitamos ninguna biblioteca adicional para poder trabajar con Hilos, los Hilos son nativos en Java. Un buen entendimiento de c omo trabajar con Hilos es fundamental cuando en nuestras aplicaciones se ejecutan m as de una tarea de manera concurrente. Y precisamente las aplicaciones con interfaces gr acos son un excelente ejemplo de las ventajas de trabajar con Hilos. Imagina que has escrito una aplicaci on, con un interfaz gr aco en la que al pulsar un bot on se lleva a cabo un complejo c alculo que consume mucho tiempo de CPU. Si tu aplicaci on tiene un u nico Hilo de ejecuci on, el interfaz gr aco se quedar a congelado cuando el usuario inicie el c alculo, ya que hasta que este no se acabe, no volver a el control al interfaz gr aco. Obviamente, ninguna aplicaci on se comporta as , o al menos no deber a. Todos los Hilos de una aplicaci on en Java comparten el mismo espacio de 207

208

CONCURRENTE CON HILOS CAP ITULO 14. PROGRAMACION

memoria, lo que implica que varios Hilos pueden estar accediendo a la misma zona de memoria (por ejemplo una variable) para modicar su valor. En este caso se pueden producir colisiones e inconsistencias por el uso compartido de memoria. De alg un modo hay que establecer un mecanismo de sincronizaci on para que el acceso a zonas de memoria compartida no den lugar a este tipo de comportamiento err oneo. En este cap tulo vas a conocer c omo trabajar con Hilos en Java, vas a conocer c omo se crean Hilos en Java y como sincronizar su ejecuci on cuando acceden a recursos compartidos para que no haya inconsistencias en tus aplicaciones.

14.1.

Qu e es un hilo? Utilidades. Consideraciones sobre el uso de hilos

De un modo muy resumido podemos decir que un Hilo es un ujo de control independiente dentro de un programa. Como tal tiene su propia pila de llamadas, pero todos los Hilos creados en el mismo programa comparten el mismo espacio de direcciones, lo que implica que comparten todas las instancias del programa excepto las locales. Nuestros programas pueden tener un gran n umero de Hilos ejecut andose al mismo tiempo y todos ellos comparten el uso de la CPU. Esto proporciona grandes ventajas, ya que si disponemos, como empieza a ser la habitual, de CPUs con m as de un n ucleo, varios hilos pueden estar ejecut andose de modo paralelo en cada uno de los n ucleos de la CPU. Tambi en podemos pensar que cada uno de los Hilos es un programa secuencial en s mismo, con lo que la programaci on de soluciones complejas, cuando se piensan como un todo, se simplican enormemente al ser divididas en partes que se ejecutan de modo independiente pero coordinado. A cambio tenemos que pagar un precio, ya que si varios Hilos intentan acceder al mismo recurso compartido y al mismo tiempo, el resultado, si el acceso no est a sincronizado, puede no ser el deseado. Para ver lo que queremos decir con claridad, supongamos dos hilos que se est an ejecutando sobre una u nica CPU, el c odigo que ejecuta el primero de ellos aparece en el Listado 14.1 y el que ejecuta el segundo aparece en el Listado 14.2. Supongamos que el primer Hilo tiene el disfrute de la u nica CPU, y que el valor de la variable cantidad en ese momento es 9, la condici on de la l nea 1 se cumple, y antes de que se ejecute la segunda l nea, se cede el disfrute de la CPU al segundo hilo, que comprueba que la condici on de su l nea 1 se cumple, por lo que incrementa en dos unidades la variable cantidad que pasa a valer 11, y despu es de modicar el valor de cantidad, de nuevo, se cede el disfrute de la CPU al primer hilo. En este momento, la condici on que comprob o el primer Hilo ya no es v alida y a un as , se incrementar a el valor de la variable suma que de 11 pasar a a valer 12. Evidentemente se ha producido un error ya que en este caso el acceso al recurso compartido suma no est a sincronizado.
1 2 3

i f ( cantidad < 10) c a n t i d a d ++; System . o u t . p r i n t l n ( " C a n t i d a d : " + c a n t i d a d ) ;

Listado 14.1: C odigo que ejecuta el primer Hilo.

DE HILOS EN JAVA 14.2. CREACION

209

Figura 14.1: La idea de c omo crear un Hilo en Java gr acamente. Necesitamos dos ingredientes, la tarea cuyo c odigo se va a ejecutar concurrentemente, y el controlador del hilo (clase Thread).

1 2 3

i f ( cantidad < 20) c a n t i d a d += 2 ; System . o u t . p r i n t l n ( " C a n t i d a d : " + c a n t i d a d ) ;

Listado 14.2: C odigo que ejecuta el segundo Hilo.

14.2.

Creaci on de hilos en Java

Lo primero que hay que conocer de la creaci on de Hilos en Java es que se componen de dos partes, por un lado deniremos la tarea que queremos que se ejecute de manera concurrente con otras tareas, y por otro lado tenemos el controlador de esa tarea, que nos permitir a sincronizarla con el resto de tareas. La clase que representa una tarea, y que controlar a un Hilo, ha de implementar el interface Runnable. Este interface declara un u nico m etodo public void run(), el c odigo del cual se ejecutar a de manera concurrente con otros Hilos cuando se inicie. Por otro lado, el controlador del Hilo ser a una instancia de la clase Thread, a quien en el momento de su creaci on debemos pasarle como argumento una instancia de la clase que implementa el interface Runnable. La Figura 14.1 muestra gr acamente este concepto. Podemos utilizar tres t ecnicas distintas para crear un hilo en Java: 1. Extender a la clase Thread. 2. Denir una clase interna que implemente el interface Runnable. 3. Denir una clase interna an onima que implemente el interface Runnable. Veamos cada una de estas t ecnicas por separado, y cuales son sus ventajas y desventajas.

14.2.1.

Creaci on de un Hilo extendiendo a la clase Thread

La clase Thread implementa la interface Runnable, luego simplemente extendi endola y sobrescribiendo su m etodo public void run() tendremos un nuevo Hilo. El Listado 14.3 muestra el uso de esta primera t ecnica. F jate que al crear

210

CONCURRENTE CON HILOS CAP ITULO 14. PROGRAMACION

el Hilo, este no empieza su ejecuci on, para indicar que el Hilo debe empezar a ejecutarse debemos llamar a su m etodo start. El Hilo permanece vivo siempre que se est e ejecutando su m etodo public void run(). El resultado de la ejecuci on de este Hilo es que se muestran 10 mensajes de texto por consola.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

package h i l o s ; public f i n a l c l a s s ThreadExtendido extends Thread { public ThreadExtendido ( ) { super ( ) ; } @Override public void run ( ) { f o r ( i n t i = 0 ; i < 1 0 ; i ++) System . o u t . p r i n t l n ( " E s t a m o s e n : " + i ) ; } public s t a t i c void main ( S t r i n g [ ] new ThreadExtendido ( ) . s t a r t ( ) ; } } args ) {

Listado 14.3: Creaci on de un Hilo extendiendo a la clase Thread F jate que el m etodo public void run() es un m etodo p ublico, luego, mientras el Hilo se est a ejecutando, cualquier otra clase podr a llamar al m etodo run() de esta clase, provocando con ello inconsistencias durante la ejecuci on de nuestra aplicaci on. Por ello, esta t ecnica de creaci on de Hilos est a desaconsejada.

14.2.2.

Creaci on de un Hilo mediante una clase interna

El Listado 14.4 muestra un ejemplo de esta segunda t ecnica. Hemos denido la clase ClaseInterna que implementa el interface Runnable, dentro de la clase HiloClaseInterna. Y en la l nea 20 creamos una instancia de la clase Thread con un argumento que es una instancia de la clase ClaseInterna que dene en su m etodo public void run() la tarea que se ejecuta concurrentemente. Igual que en el ejemplo anterior, el Hilo permanecer a vivo siempre que se encuentre ejecutando su m etodo run().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

package h i l o s ; public f i n a l c l a s s H i l o C l a s e I n t e r n a { private H i l o C l a s e I n t e r n a ( ) { super ( ) ; } private c l a s s C l a s e I n t e r n a implements Runnable { private C l a s e I n t e r n a ( ) { super ( ) ; } public void run ( ) { f o r ( i n t i = 0 ; i < 1 0 ; i ++) System . o u t . p r i n t l n ( " E s t a m o s e n : " + i ) ; } } private void e j e c u t a ( ) { new Thread ( new C l a s e I n t e r n a ( ) ) . s t a r t ( ) ; } public s t a t i c void main ( S t r i n g [ ] args ) {

DE HILOS EN JAVA 14.2. CREACION


24 25 26

211

new H i l o C l a s e I n t e r n a ( ) . e j e c u t a ( ) ; } }

Listado 14.4: Creaci on de un Hilo como una clase interna que implementa el interface Runnable Al contrario que en la creaci on de hilos como extensiones de la clase Thread, en este caso la clase interna ClaseInterna es private, luego s olo se podr a acceder a su m etodo publi void run() desde dentro de la clase en la que est a denida, hemos evitado que otra clase llame de modo accidental al m etodo run() previniendo con ello inconsistencias en nuestra aplicaci on. Este m etodo est a recomendado cuando la tarea que se debe ejecutar de manera concurrente es compleja, y la clase interna implementa el algoritmo de ejecuci on de la tarea. Adem as, ya que la tarea concurrente est a implementada dentro de una clase, podremos crear instancias de esta clase en cualquier otro lugar de la clase que la contiene.

14.2.3.

Creaci on de un Hilo mediante una clase interna an onima

El Listado 14.5 muestra un ejemplo del uso de clases internas an onimas para la creaci on de Hilos. Este t ecnica es muy parecida a la t ecnica de la secci on anterior salvo que se ha utilizado una clase interna an onima para implementar la tarea que se ejecuta de manera concurrente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

package h i l o s ; public f i n a l c l a s s H i l o C l a s e I n t e r n a A n o n i m a { private H i l o C l a s e I n t e r n a A n o n i m a ( ) { super ( ) ; } private void e j e c u t a ( ) { new Thread ( new Runnable ( ) { @Override public void run ( ) { f o r ( i n t i = 0 ; i < 1 0 ; i ++) System . o u t . p r i n t l n ( " E s t a m o s e n : " + i ) ; } }) . s t a r t ( ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { new H i l o C l a s e I n t e r n a A n o n i m a ( ) . e j e c u t a ( ) ; } }

Listado 14.5: Creaci on de un Hilo como una clase interna an onima que implementa el interface Runnable Esta t ecnica est a recomendada cuando el c odigo de la tarea concurrente es s olo de algunas l neas y no se va a reutilizar en ning un otro caso, ya que al ser una clase an onima no tienen nombre y no podr amos crear una instancia de una clase sin nombre en ninguna otra parte de nuestro c odigo.

212

CONCURRENTE CON HILOS CAP ITULO 14. PROGRAMACION

Figura 14.2: Estados en los que se puede encontrar un Hilo y las posibles transiciones entre ellos.

14.3.

Ciclo de vida de un hilo

Los Hilos pueden encontrarse en m as estados que u nicamente en el estado de ejecuci on cuando est an vivos. La Figura 14.2 muestra los estados en los que se puede encontrar un hilo y las transiciones entre los estados. Un hilo inicia su ejecuci on cuando se llama a su m etodo public void start(), y en este momento se dice que est a en Ejecuci on. Durante su ejecuci on, un Hilo puede pasar al estado Pausado o al estado Planicado, en este estado el Hilo no se ejecuta, pero siempre podr a pasar de nuevo al estado en Ejecuci on. Un Hilo puede pasar al estado Terminado desde el estado en Ejecuci on, se dice que el Hilo se ha terminado, y un hilo Terminado nunca m as puede pasar de nuevo al estado en Ejecuci on, Pausado o Planicado. La diferencia entre los estados Pausado o Planicado es que de un estado Planicado conocemos el momento en que de nuevo pasar a a Ejecuci on. De un estado Pausado no sabemos cuando volver a al estado Ejecuci on. Dependiendo del m etodo de la clase Thread que utilicemos, podremos llevar un Hilo en Ejecuci on a uno de estos estados. Un hilo pasa al estado Terminado cuando ocurre alguna de estas tres cosas: 1. Sale de su m etodo public void run(). 2. Se produce una excepci on dentro del m etodo run() no gestionada. 3. Se llama al m etodo public void stop() del Hilo.

Buena pr actica El uso del m etodo stop() para detener la ejecuci on de un Hilo est a fuertemente desaconsejado ya que puede provocar inconsistencias y de hecho est a marcado como obsoleto en el API Java. Para acabar un Hilo limpiamente se aconseja salir de su m etodo run(). Esta buena pr actica es la que se sigue en los ejemplo mostrados, cuando se ha iterado un cierto n umero de veces, se sale del m etodo public void run().

14.4. CONTROL DE HILOS

213

Otra t ecnica muy extendida es utilizar dentro del m etodo public void run() un bucle while(condicion) cuya condici on inicialmente es true y se cambia a false cuando se quiere acabar el Hilo limpiamente.

14.4.

Control de hilos

El control de hilos se puede llevar a cabo desde una perspectiva gruesa tratando cada Hilo como un bloque, o desde una perspectiva m as na mediante el uso de la sincronizaci on. En esta secci on veremos como coordinar la ejecuci on de Hilos desde una perspectiva gruesa dejando para la siguiente secci on la sincronizaci on de Hilos desde una perspectiva m as na. Podemos suspender temporalmente la ejecuci on de un Hilo mediante el uso del m etodo public static void sleep(long milisegundos)1 . Pasado el tiempo especicado, el Hilo volver a al estado en Ejecuci on. Es decir, con el m etodo sleep(long milisegundos) planicamos que el Hilo vuelva a su estado Ejecuci on pasados los milisegundos especicados. El Listado 14.6 muestra un ejemplo que muestra un mensaje cada 1000 milisegundos, es decir, cada segundo. El m etodo sleep(long) puede lanzar una excepci on de tipo InterruptedException que debemos gestionar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

package h i l o s ; public f i n a l c l a s s E j e m p l o S l e e p { private E j e m p l o S l e e p ( ) { super ( ) ; } private void e j e c u t a ( ) { new Thread ( new Runnable ( ) { @Override public void run ( ) { try { f o r ( i n t i = 0 ; i < 1 0 ; i ++) { System . o u t . p r i n t l n ( " E s t a m o s e n : " + i ) ; Thread . s l e e p ( 1 0 0 0 ) ; } } catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } }) . s t a r t ( ) ; } public s t a t i c void main ( S t r i n g [ ] new E j e m p l o S l e e p ( ) . e j e c u t a ( ) ; } } args ) {

Listado 14.6: Uso del m etodo sleep(long milisegundos) para pausar la ejecuci on del Hilo durante el intervalo de tiempo espedicado. Si un Hilo est a en estado Pausado o Planicado siempre lo podremos sacar de este estado con el m etodo void interrupt(). Este m etodo lanza una excepci on de tipo InterruptedException. Existen casos en los que un Hilo debe esperar a que otro acabe su ejecuci on para que el pueda continuar. Piensa por ejemplo en un Hilo que cuenta el n umero
1 Tambi en existe una versi on de mayor precisi on public static void sleep(long milisegundos, int nanosegundos

214

CONCURRENTE CON HILOS CAP ITULO 14. PROGRAMACION

de caracteres que se leen desde un chero. Si la lectura del chero se hace en un Hilo y la cuenta del n umero de caracteres en otro, el Hilo que cuenta el n umero de caracteres debe esperar a que el Hilo que carga el chero acabe su tarea antes de empezar con la cuenta. Para que un Hilo espere a que otro acabe su ejecuci on llamaremos al m etodo public final void join()2 del segundo Hilo en el c odigo del primero, tal y como muestra el ejemplo del Listado 14.7.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

package h i l o s ; public f i n a l c l a s s E j e m p l o J o i n { private Thread h i l o ; private E j e m p l o J o i n ( ) { super ( ) ; } private void e j e c u t a ( ) { h i l o = new Thread ( new Tarea ( ) ) ; hilo . start () ; new Thread ( new TareaQueEspera ( ) ) . s t a r t ( ) ; } private c l a s s Tarea implements Runnable { public void run ( ) { try { f o r ( i n t i = 0 ; i < 1 0 ; i ++) { System . o u t . p r i n t l n ( " C u e n t a : " + i ) ; Thread . s l e e p ( 5 0 0 ) ; } } catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } } private c l a s s TareaQueEspera implements Runnable { public void run ( ) { try { f o r ( i n t i = 0 ; i < 5 ; i ++) { System . o u t . p r i n t l n ( " C u e n t a y e s p e r a : " + i ) ; Thread . s l e e p ( 5 0 0 ) ; } hilo . join () ; f o r ( i n t i = 5 ; i < 1 0 ; i ++) { System . o u t . p r i n t l n ( " S a l g o d e l a e s p e r a y c u e n t a : " + i ) ; Thread . s l e e p ( 5 0 0 ) ; } } catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } } public s t a t i c void main ( S t r i n g [ ] new E j e m p l o J o i n ( ) . e j e c u t a ( ) ; } } args ) {

Listado 14.7: Uso del m etodo join() para pausar la ejecuci on del Hilo hasta que acabe la ejecuci on de otro.
2 Existen dos versiones de este m etodo public final void join(long milisegundo y public final void sleep(long milisegundos, int nanosegundos en los que se espera, como m aximo, el tiempo especicado

14.5. SINCRONIZACION

215

14.5.

Sincronizaci on

En los ejemplos anteriores el control sobre Hilos no implicaba acceso concurrente a recursos. Cuando varios Hilos que se ejecutan de modo concurrente intentan acceder al mismo recurso debemos sincronizar los accesos al recurso para que no se produzcan inconsistencias en nuestras aplicaciones. La sincronizaci on en Java se base en el uso de cerrojos, varios Hilos que intentan acceder a un recurso compartido sincronizan su acceso a trav es de un cerrojo. Antes de acceder al recurso compartido un Hilo intentar a adquirir un cerrojo, si el cerrojo no est a en posesi on de ning un otro Hilo lo adquirir a, de lo contrario tendr a que esperar a que el cerrojo se libere. Una vez en posesi on del cerrojo puede trabajar con el recurso compartido con la seguridad de que ning un otro Hilo acceder a a este recurso hasta que el no libere el cerrojo que indica la propiedad de uso del recurso. Y donde se encuentran esos cerrojos que nos sirven para sincronizar el acceso a recursos compartidos?. Todo objeto tiene un cerrojo intr nseco, recuerda que los Hilos y su sincronizaci on son nativos en Java. En las secciones siguientes vamos a ver c omo sincronizar el acceso a recursos compartidos con dos t ecnicas: mediante el uso de cerrojos intr nsecos y mediante la interface Lock y la interface Condition introducidas ambas en la versi on 5.0 de Java.

14.5.1.

Sincronizac on utilizando los cerrojos intr nsecos

En Java todo objeto posee un cerrojo intr nseco que podemos utilizar para sincronizar el acceso a los recursos que nos proporciona el objeto. Como ya sabemos, un m etodo es un recurso o servicio que proporciona un objeto, si queremos sincronizar el acceso a un m etodo de un objeto basta marcarlo con la palabra reservada synchronized, como en el Listado 14.8.
1 2 3 4 5

class A { synchronized void metodo1 ( ) {} synchronized void metodo2 ( ) {} ... }

Listado 14.8: Para indica que el acceso a un m etodo est a sincronizado utilizamos la palabra reservada synchronized. Denir un m etodo como synchronized implica que antes de que un Hilo pueda ejecutar el m etodo, debe adquirir el cerrojo intr nseco del objeto para poder ejecutarlo. Si el cerrojo intr nseco est a en posesi on de otro Hilo, el Hilo que intenta adquirir el cerrojo tendr a que esperar a que el otro lo libere. El cerrojo intr nseco pertenece al objeto y sincroniza el acceso a todos los m etodos denidos como synchronized, si un Hilo adquiere el cerrojo intr nseco accediendo al metodo1() del ejemplo anterior, y otro Hilo intenta acceder al metodo2() no podr a hacerlo ya que no podr a adquirir el cerrojo intr nseco del objeto. Un Hilo libera el cerrojo cuando acaba la ejecuci on del m etodo sincronizado, y en ese momento cualquier otro Hilo que se encuentre a la espera podr a adquirir el cerrojo. Como el cerrojo intr nseco pertenece al objeto, la sincronizaci on en el ejemplo del Listado 14.8 es relativa a todo el objeto, si un Hilo est a en posesi on del cerrojo

216

CONCURRENTE CON HILOS CAP ITULO 14. PROGRAMACION

intr nseco, cualquier otro Hilo no podr a acceder a ning un otro m etodo denido como synchronized. Hay casos en los que no nos interesa sincronizar el acceso a los m etodo del objeto, si no sincronizar s olo bloques de c odigo. Incluso a veces nos interesa tener varios bloques de c odigo sincronizados pero que el acceso a distintos bloques de c odigo no est e sincronizado, si no que la sincronizaci on sea relativa al bloque y no al objeto completo. Java nos permite sincronizar el acceso a bloque de c odigo tambi en mediante el uso de la palabra reservada synchronized, tal y como se muestra en el Listado 14.9. Un Hilo puede estar ejecutando el bloque sincronizado por el cerrojo1 mientras que otro Hilo puede estar ejecutando el bloque sincronizado por el cerrojo2; pero ning un otro Hilo podr a acceder a estos bloques sincronizados hasta que los Hilos en posesi on de los cerrojos no abandonen el bloque sincronizado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

class A { O b j e c t c e r r o j o 1 = new O b j e c t ( ) ; O b j e c t c e r r o j o 2 = new O b j e c t ( ) ; void metodo1 ( ) { ... synchronized ( c e r r o j o 1 ) { // El a c c e s o a e s t e b l o q u e e s t a sincronizado // p o r c e r r o j o 1 . } ... } void metodo2 ( ) {} ... synchronized ( c e r r o j o 2 ) { // El a c c e s o a e s t e b l o q u e e s t a sincronizado // p o r c e r r o j o 2 . } } }

Listado 14.9: Cada uno de los bloques de c odigo est a sincronizado por un cerrojo intr nseco de un objeto distinto. El acceso s olo est a sincronizado si el acceso es al mismo bloque de c odigo. F jate que, por otra parte, esta t ecnica nos permite sincronizar con m as detalle el c odigo conictivo, no sincronizamos todo un m etodo si no s olo las l neas de c odigo que acceden al recurso compartido. Incluso podemos utilizar esta t ecnica para sincronizar bloques de c odigo en distintos m etodos con el mismo cerrojo. Ya hemos visto que cuando un Hilo no puede adquirir el cerrojo para acceder a un m etodo o bloque sincronizado tiene que esperar a que el Hilo que tiene en posesi on el cerrojo lo libere, y que el cerrojo se libera cuando el Hilo que lo tiene en posesi on acaba la ejecuci on del m etodo o bloque sincronizado. Tambi en podemos forzar que un Hilo entre en espera mediante el m etodo public final void wait() throws InterruptedException3 . Cuando un Hilo entra en espera, libera el cerrojo intr nseco que tenga en posesi on, y esta es la diferencia con respecto al m etodo sleep(int milisegundos) que provoca una espera del
3 Existen otras dos versiones de este m etodo public final void wait(long timeout) throws InterruptedException y public final void wait(long timeout, int nanos) throws InterruptedException

14.5. SINCRONIZACION

217

Hilo, pero el Hilo no libera el cerrojo intr nseco que tenga en posesi on. El uso del m etodo wait() lleva un Hilo desde el estado Ejecuci on al estado Pausado ya que no conocemos cuando el Hilo volver a al estado Ejecuci on. Un Hilo que ha entrado en espera mediante una llamada a wait() saldr a de este estado si ocurre alguna de estas cuatro cosas: Alg un otro Hilo llama al m etodo public final void notify(). Alg un otro Hilo llama al m etodo public final void notifyAll(). Alg un otro Hilo llama al m etodo public void interrupt(). Mientras un Hilo permanece en estado de espera nunca ser a elegido para competir por la adquisici on del cerrojo. Existe la posibilidad de que el Hilo en estado de espera sufra un despertar esp ureo, lo que implica que el Hilo se despierte aunque no ocurra ninguna circunstancia de la indicada anteriormente, de modo que, si detuvimos el Hilo porque no se comprobaba cierta condici on, si el Hilo se despierta de modo esp ureo puede que la condici on siga sin satisfacerse, por lo que siempre debemos comprobar que la condici on se satisface cuando un Hilo sale del estado de espera. El Listado 14.10 muestra una posible implementaci on de un Buer de tama no jo con sus operaciones getDato() y setDato(T dato) sincronizadas y de modo que si un Hilo intenta tomar un dato y el Buer est a vac o el Hilo debe esperar (wait()), y si el Hilo intenta poner un nuevo dato y el Buer est a lleno tambi en debe esperar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

package b u f f e r ; public c l a s s B u f f e r S i n L o c k <T> { private i n t c a b e z a = 0 ; private i n t c a p a c i d a d ; private O b j e c t d a t o s [ ] ; private i n t o c u p a c i o n = 0 ; public B u f f e r S i n L o c k ( i n t c a p a c i d a d ) { super ( ) ; this . capacidad = capacidad ; d a t o s = new O b j e c t [ c a p a c i d a d ] ; } @SuppressWarnings ( " u n c h e c k e d " ) public synchronized T g e t D a t o ( ) throws I n t e r r u p t e d E x c e p t i o n { T dato ; while ( o c u p a c i o n == 0 ) wait ( ) ; d a t o = (T) d a t o s [ c a b e z a ] ; o c u p a c i o n ; c a b e z a ++; cabeza % = capacidad ; System . o u t . f o r m a t ( " - % s [ % d ] \ n " , dato , o c u p a c i o n ) ; notifyAll () ; return d a t o ; } public synchronized void s e t D a t o (T d a t o ) throws I n t e r r u p t e d E x c e p t i o n { while ( o c u p a c i o n == c a p a c i d a d ) wait ( ) ; datos [ ( cabeza + ocupacion ) % capacidad ] = dato ; o c u p a c i o n ++; System . o u t . f o r m a t ( " + % s [ % d ] \ n " , dato , o c u p a c i o n ) ; notifyAll () ;

218
36 37

CONCURRENTE CON HILOS CAP ITULO 14. PROGRAMACION

} }

Listado 14.10: Implementaci on de un Buer de tama no jo utilizando cerrojos intr nsecos En el Ap endice C se muestra un ejemplo de uso de este Buer.

14.5.2.

Sincronizaci on utilizando el interface Lock

Desde la versi on Java 5, se ha enriquecido el API de Java con nuevos paquetes para facilitar la programaci on con Hilos. Estos nuevos paquetes son: java.util.concurrent Que proporciona nuevas estructuras de datos y clases de utilidad de acceso concurrente. java.util.concurrent.atomic Conjunto de clases para acceso at omico a tipos de datos primitivos y referencia. java.util.concurrent.locks Conjunto de clases para facilitar la sincronizaci on y acceso concurrente a recursos compartidos. El u ltimo paquete java.util.concurrent.locks de la lista anterior proporciona clases que facilitan la sincronizaci on concurrente a recursos compartidos. En particular la interface Lock y las clases que lo implementan ReentrantLock, ReentrantReadWriteLock.ReadLock y ReentrantReadWriteLock.WriteLock son de gran ayuda. La idea b asica es que un Hilo debe obtener el cerrojo que representa alguna de estas clases antes de poder ejecutar el c odigo que nos interesa de manera concurrente, y una vez que se ha terminado la ejecuci on del c odigo cr tico, debemos liberar el cerrojo. La t ecnica recomendada para hacerlo se muestra en el Listado 14.11
1 2 3 4 5 6 7

Lock l = . . . ; // creamos a l g u n a i n s t a n c i a que implemente a Lock l . lock () ; try { // a c c e s o a l o s r e c u r s o s c o m p a r t i d o s } finally { l . unlock ( ) ; }

Listado 14.11: Modo aconsejado de trabajar con los objetos que implementan la interface Lock Otra interface de gran ayuda es Condition con la que podemos denir varias condiciones sobre un mismo Lock de modo que el hilo que adquiere el Lock puede pausarse, liberando el Lock, utilizando un Condition si la condici on de ocupaci on del Hilo no es v alida, y se le informar a a trav es de esa misma Condition de que la condici on de ocupaci on del Hilo puede ser v alida de nuevo. Como ejemplo de funcionamiento de estas interfaces, el Listado 14.12 muestra una implementaci on de un Buer de tama no jo y acceso sincronizado, como el del Listado 14.10 pero esta vez haciendo uso de estas interfaces. En el Ap endice C se muestra un ejemplo de uso de este Buer.
1 2

package b u f f e r ;

14.5. SINCRONIZACION
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

219

import j a v a . u t i l . c o n c u r r e n t . l o c k s . C o n d i t i o n ; import j a v a . u t i l . c o n c u r r e n t . l o c k s . Lock ; import j a v a . u t i l . c o n c u r r e n t . l o c k s . R e e n t r a n t L o c k ; public c l a s s BufferConLock <T> { private i n t c a b e z a = 0 ; private i n t c a p a c i d a d ; private O b j e c t d a t o s [ ] ; private i n t o c u p a c i o n = 0 ; private Lock c e r r o j o ; private C o n d i t i o n c o n d i c i o n ; public Bu ffe rC onL ock ( i n t c a p a c i d a d ) { super ( ) ; this . capacidad = capacidad ; d a t o s = new O b j e c t [ c a p a c i d a d ] ; c e r r o j o = new R e e n t r a n t L o c k ( ) ; c o n d i c i o n = c e r r o j o . newCondition ( ) ; } @SuppressWarnings ( " u n c h e c k e d " ) public T g e t D a t o ( ) throws I n t e r r u p t e d E x c e p t i o n { T dato ; cerrojo . lock () ; try { while ( o c u p a c i o n == 0 ) condicion . await ( ) ; d a t o = (T) d a t o s [ c a b e z a ] ; o c u p a c i o n ; c a b e z a ++; cabeza % = capacidad ; System . o u t . f o r m a t ( " - % s [ % d ] \ n " , dato , o c u p a c i o n ) ; } finally { condicion . signalAll () ; c e r r o j o . unlock ( ) ; } return d a t o ; } public void s e t D a t o (T d a t o ) throws I n t e r r u p t e d E x c e p t i o n { cerrojo . lock () ; try { while ( o c u p a c i o n == c a p a c i d a d ) condicion . await ( ) ; datos [ ( cabeza + ocupacion ) % capacidad ] = dato ; o c u p a c i o n ++; System . o u t . f o r m a t ( " + % s [ % d ] \ n " , dato , o c u p a c i o n ) ; } finally { condicion . signalAll () ; c e r r o j o . unlock ( ) ; } } }

Listado 14.12: Implementaci on de un Buer de tama no jo utilizando las clases ReentrantLock y Condition Cuando utilizar los cerrojos intr nsecos y cuando el interface Lock?. La secci on 13.2 de la referencia [1] clarica mucho cuando utilizar una t ecnica u otra.

Ejercicios.
1. Escribe una aplicaci on en la que varios Hilos intenten acceder a un recurso compartido que muestre un mensaje por consola. El acceso a este recurso compartido debe hacerse de modo sincronizado.

220

CONCURRENTE CON HILOS CAP ITULO 14. PROGRAMACION

Lecturas recomendadas.
El Cap tulo 10 de la referencia [2] es sin duda una muy buena exposici on de las t ecnicas de concurrencia en Java utilizando cerrojos intr nsecos. La referencia [1] es obligada de principio a n si la concurrencia es capital en nuestras aplicaciones. El Cap tulo 9 de la referencia [4] expone criterios muy interesante a seguir en el uso de Hilos para que no se vea reducida la potencia de nuestras aplicaciones por un mal uso de los Hilos.

Cap tulo 15

Programaci on para la Red


Contenidos
15.1. Trabajo con URLs . . . . . . . . . . . . . . . . 15.1.1. Qu e es una URL? . . . . . . . . . . . . . . . 15.1.2. Leer desde una URL . . . . . . . . . . . . . . 15.1.3. Escribir a una URL . . . . . . . . . . . . . . 15.2. Trabajo con Sockets . . . . . . . . . . . . . . . 15.2.1. Qu e es un Socket? . . . . . . . . . . . . . . . 15.2.2. Sockets bajo el protocolo TCP . . . . . . . . 15.2.2.1. Sockets TCP en el lado del servidor 15.2.2.2. Sockets TCP en el lado del cliente . 15.2.3. Sockets bajo el protocolo UDP . . . . . . . . 15.2.3.1. Sockets UDP en el lado del servidor 15.2.3.2. Sockets UDP en el lado del cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 222 223 223 225 225 225 225 226 227 228 228

Introducci on
Java naci o como un lenguaje de prop osito general con grandes capacidades para trabajar en red. En el Cap tulo 12 vimos c omo programar Applets, que son aplicaciones Java que se ejecutan en el contexto de un navegador web. Java nos permite trabajar en red de un modo muy sencillo gracias a la gran cantidad de clases que nos proporciona el paquete java.net. En este cap tulo veremos como trabajar con URLs, pilar b asico para referenciar recursos en el protocolo HTTP, y como trabajar con Sockets tanto bajo el protocolo TCP como bajo el protocolo UDP. Como ver as, tanto si trabajamos con URLs como si trabajamos con Sockets, la lectura y escritura sobre estos canales la haremos a trav es de los Flujos que vimos en el Cap tulo 7. Recuerda que todo proceso de lectura/escritura es independiente del dispositivo de entrada/salida y se realiza a trav es de Flujos, de ah la importancia de conocerlos a fondo. 221

222

PARA LA RED CAP ITULO 15. PROGRAMACION

15.1.

Trabajo con URLs

En esta secci on vamos a recordar qu e es una URL. Veremos que en el paquete java.net tenemos una clase para crear URLs. Esta clase nos permite tanto leer el contenido de la URL como escribir hacia la URL.

15.1.1.

Qu e es una URL?

Una URL representa un recurso en Internet. Este recurso viene especicado por cuatro partes, por ejemplo en la siguiente URL http://www.google.es: 80/index.html, se est a especicando: 1. El protocolo http. 2. El host de destino www.google.es. 3. El puerto de conexi on 80. 4. El chero solicitado index.html. Los servidores que atienden peticiones HTTP generalmente utilizan el puerto de escucha 80, y si no se indica la contrario, el chero al que por defecto se accede es index.html1 ; por lo que la anterior URL la podemos escribir de forma abreviada de modo http://www.google.es. En esta secci on vamos a utilizar como ejemplo siempre el protocolo HTTP, aunque, como el lector sabr a, existe otra gran cantidad de protocolos sobre TCP. Java nos proporciona la clase URL para poder especicar recursos en Internet como muestra el Listado 15.1. En el primer caso especicamos la URL como una u nica cadena. En el segundo caso lo especicamos como cuatro cadenas indicando el protocolo, direcci on de Internet, puerto y recurso respectivamente. Si el protocolo tiene un puerto bien conocido no hace falta especicarlo, como en el tercer caso. Todos estos constructores pueden lanzar una excepci on de tipo MalformedURLException para indicar que la URL que se intenta especicar no es v alida.
1 2 3

URL u r l = new URL( " h t t p : / / w w w . g o o g l e . e s : 8 0 / i n d e x . h t m l " ) ; URL u r l = new URL( " h t t p " , " w w w . u j i . e s " , 8 0 , " i n d e x . h t m l " ) ; URL u r l = new URL( " h t t p " , " w w w . u j i . e s " , " i n d e x . h t m l " ) ;

Listado 15.1: Algunos constructores de la clase URL La clase URL nos proporciona m etodos para recuperar la informaci on contenida en una URL: public String getProtocol(), devuelve el protocolo. public String getHost(), devuelve el host. public int getPort(), devuelve el puerto de conexi on. public String getFile(), devuelve el recurso que se solicita. Pasemos ahora a ver c omo leer desde una URL.
1 La

p agina de inicio de un sitio web tambi en puede ser index.js, o index.cgi

15.1. TRABAJO CON URLS

223

15.1.2.

Leer desde una URL

Una vez que tenemos construida una URL v alida, podemos recuperar a partir de ella un InputStream para leer el contenido del recurso al que apunta la URL, mediante el m etodo public final InputStream openStream() throws IOException, y como ya vimos en el Cap tulo 7 a partir de una referencia de tipo InputStream podemos recuperar un BufferedReader con el que leer l nea a l nea desde el recurso. El Listado 15.2 muestra un ejemplo completo de c omo leer el contenido de un chero de esto apuntado por una URL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

package r e d ; import import import import import import java java java java java java . io . BufferedReader ; . i o . IOException ; . i o . InputStream ; . i o . InputStreamReader ; . n e t . MalformedURLException ; . n e t .URL;

public f i n a l c l a s s LeerDesdeURL { private LeerDesdeURL ( ) { super ( ) ; } private void e j e c u t a ( S t r i n g d i r e c c i o n ) { try { URL u r l = new URL( d i r e c c i o n ) ; I n p u t S t r e a m i s = u r l . openStream ( ) ; 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 ( new I n p u t S t r e a m R e a d e r ( i s ) ) ; try { S t r i n g cadena ; while ( ( cadena = br . r e a d L i n e ( ) ) != n u l l ) System . o u t . p r i n t l n ( cadena ) ; } finally { br . c l o s e ( ) ; } } catch ( MalformedURLException e ) { e . printStackTrace () ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { new LeerDesdeURL ( ) . e j e c u t a ( a r g s [ 0 ] ) ; } }

Listado 15.2: Lectura desde una URL

15.1.3.

Escribir a una URL

Escribir a una URL exige un poco m as de trabajo, ya que no podemos escribir directamente a una URL. Para escribir a una URL necesitamos una referencia a la clase URLConnection. La referencia necesaria nos la devuelve el m etodo public URLConnection openConnection() throws IOException de la clase URL. Una vez obtenida esta referencia, debemos indicar que queremos escribir sobre ella, para lo que utilizaremos el m etodo public void setDoOutput(boolean dooutput) de la clase URLConnection con un argumento true. Ahora ya podemos recuperar un OutputStream con el m etodo public OutputStream getOutputStream() throws IOException de la clase URConnection

224

PARA LA RED CAP ITULO 15. PROGRAMACION

El Listado 15.3 muestra todos los pasos necesarios para escribir a una URL y obtener una respuesta de ella. En este caso hemos utilizado la direcci on http://rubi.dlsi.uji.es/~oscar/PHP/nombre.php que espera un par ametro de entrada llamado nombre y devuelve un saludo2 .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

package r e d ; import import import import import import import java java java java java java java . io . BufferedReader ; . i o . IOException ; . i o . InputStreamReader ; . io . PrintWriter ; . n e t . MalformedURLException ; . n e t .URL; . n e t . URLConnection ;

public f i n a l c l a s s EscribirAURL { private EscribirAURL ( ) { super ( ) ; } private void e j e c u t a ( S t r i n g s U r l ) { try { URL u r l = new URL( s U r l ) ; URLConnection c o n e x i o n = u r l . o p e n C o n n e c t i o n ( ) ; c o n e x i o n . setDoOutput ( true ) ; P r i n t W r i t e r pw = new P r i n t W r i t e r ( c o n e x i o n . getOutputStream ( ) , true ) ; pw . p r i n t l n ( " n o m b r e = o s c a r " ) ; 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 ( new I n p u t S t r e a m R e a d e r ( c o n e x i o n . getInputStream ( ) ) ) ; try { String respuesta ; while ( ( r e s p u e s t a = br . r e a d L i n e ( ) ) != n u l l ) System . o u t . p r i n t l n ( r e s p u e s t a ) ; } finally { pw . c l o s e ( ) ; br . c l o s e ( ) ; } } catch ( MalformedURLException e ) { e . printStackTrace () ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { new EscribirAURL ( ) . e j e c u t a ( a r g s [ 0 ] ) ; } }

Listado 15.3: Escritura a una URL En el Listado 15.3, presta atenci on al segundo par ametro del constructor de PrintWriter que es true para indicar que se haga auto-ush cada vez que escribimos en el stream. Si el protocolo que utilizamos en la conexi on es HTTP, podemos modelar la referencia de respuesta del m etodo openConnection() al tipo HttpURLConnection. Esta nueva clase tiene m etodos que nos permiten especicar el tipo de petici on que se realiza public void setRequestMethod(String method) throws ProtocolException para indicar si la petici on es GET, POST, HEAD, etc.; y otros m etodos interesantes para saber el c odigo de estado de la petici on (public int getResponseCode() throws IOException).
2 Para ver el resultado en un navegador, teclea la direcci on http://rubi.dlsi.uji.es/ oscar/PHP/nombre.php?nombre=oscar y obtendr as un saludo como respuesta

15.2. TRABAJO CON SOCKETS

225

15.2.

Trabajo con Sockets

Los Sockets son el ladrillo fundamental en comunicaciones a trav es del protocolo TCP o UDP. Como en el caso del trabajos con URLs veremos que una vez establecida la conexi on entre dos Sockets (cliente/servidor) todas las tareas de lectura y escritura entre los Sockets se realizar an sobre los Flujos que la clase Socket nos proporciona.

15.2.1.

Qu e es un Socket?

Un Socket es cada uno de los extremos que se establece en una comunicaci on, en toda comunicaci on tendremos un Socket en el lado del emisor y un Socket en el lado del receptor. En el paquete java.net encontramos clases para trabajar con Sockets tanto bajo el protocolo TCP como bajo el protocolo UDP. Como recordatorio, el protocolo TCP est a orientado a la conexi on, mientras que el protocolo UDP est a orientado al mensaje.

15.2.2.

Sockets bajo el protocolo TCP

En las conexiones bajo el protocolo TCP existe un canal de conexi on entre el cliente y el servidor. Entre otras cosas el protocolo TCP garantiza la recepci on de los datos en el mismo orden en que se emiten y sin p erdidas. El s mil que se utiliza para visualizar las conexiones bajo el protocolo TCP es el de una llamada de tel efono, en la que se reserva un canal por el que la informaci on circula sin p erdidas y de manera ordenada. 15.2.2.1. Sockets TCP en el lado del servidor

Para crear un Socket que acepte conexiones en un determinado puerto, Java nos proporciona la clase java.net.ServerSocket, en el momento de crear una instancia de esta clase indicamos el puerto de escucha. Para aceptar conexiones utilizamos el m etodo public Socket accept() throws IOException, que es bloqueante, es decir, la ejecuci on se detiene en este m etodo hasta que un cliente se conecta. Cada vez que un cliente se conecta al Socket servidor, el m etodo accept() nos devolver a una referencia al Socket cliente (instancia de la clase Socket esta vez), a partir de la cual podremos obtener Flujos de lectura o escritura. El Listado 15.4 muestra un ejemplo de un sencillo Servidor que env a un saludo a cada cliente que se le conecta. En la l nea 41 puedes ver c omo la clase Socket nos devuelve un Flujo de escritura para comunicarnos con el cliente.
1 2 3 4 5 6 7 8 9 10 11 12

package r e d ; import import import import java java java java . i o . IOException ; . io . PrintWriter ; . net . ServerSocket ; . net . Socket ;

public f i n a l c l a s s S e r v i d o r S e n c i l l o { private S e r v i d o r S e n c i l l o ( ) { super ( ) ; }

226
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

PARA LA RED CAP ITULO 15. PROGRAMACION

private void e j e c u t a ( i n t p u e r t o ) { try { System . o u t . p r i n t l n ( " S e r v i d o r a l a e s c u c h a " ) ; // Creamos un S o c k e t s e r v i d o r a l a e s c u c h a en e l p u e r t o i n d i c a d o S e r v e r S o c k e t s e r v i d o r = new S e r v e r S o c k e t ( p u e r t o ) ; Socket c l i e n t e ; try { // Cada v e z que s e c o n e c t a un c l i e n t e l e enviamos un s a l u d o while ( ( c l i e n t e = s e r v i d o r . a c c e p t ( ) ) != n u l l ) new Thread ( new S a l u d o ( c l i e n t e ) ) . s t a r t ( ) ; } finally { servidor . close () ; } } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } private c l a s s S a l u d o implements Runnable { private S o c k e t c l i e n t e ; public S a l u d o ( S o c k e t c l i e n t e ) { this . c l i e n t e = c l i e n t e ; } @Override public void run ( ) { System . o u t . p r i n t l n ( " C l i e n t e c o n e c t a d o " ) ; try { // Obtenemos un s t r e a m de e s c r i t u r a a p a r t i r d e l S o c k e t d e l c l i e n t e P r i n t W r i t e r pw = new P r i n t W r i t e r ( c l i e n t e . getOutputStream ( ) , true ) ; pw . p r i n t l n ( " H o l a d e s d e e l s e r v i d o r " ) ; pw . c l o s e ( ) ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } } public s t a t i c void main ( S t r i n g a r g s [ ] ) { new S e r v i d o r S e n c i l l o ( ) . e j e c u t a ( I n t e g e r . p a r s e I n t ( a r g s [ 0 ] ) ) ; } }

Listado 15.4: Un Socket servidor que env a un saludo a cada cliente que se le conecta Si, por ejemplo, inicias el servidor en el puerto 1234, pasando este entero por la l nea de argumentos, podr as conectarte al servidor desde un navegador web en la direcci on http://localhost:1234. Y obtendr as como resultado el mensaje de saludo. Tambi en puedes conectarte al servidor mediante telnet escribiendo en una consola telnet localhost 1234. 15.2.2.2. Sockets TCP en el lado del cliente

Para conectarnos a un Socket servidor desde Java disponemos de la clase java.net.Socket. Al crear una instancia de esta clase le pasamos la direcci on a la que nos queremos conectar y en que puerto, y una vez establecida la conexi on podremos abrir Flujos de lectura y escritura. El Listado 15.5 muestra un ejemplo de un cliente que se conecta al servidor del Listado 15.4 para obtener un saludo como respuesta.
1 2

package r e d ;

15.2. TRABAJO CON SOCKETS


3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

227

import import import import import

java java java java java

. io . BufferedReader ; . i o . IOException ; . i o . InputStreamReader ; . net . Socket ; . n e t . UnknownHostException ;

public f i n a l c l a s s C l i e n t e S e n c i l l o { private C l i e n t e S e n c i l l o ( ) { super ( ) ; } private void e j e c u t a ( i n t p u e r t o ) { try { // Me c o n e c t o a l s e r v i d o r l o c a l que e s c u h a e s e s t e p u e r t o S o c k e t c l i e n t e = new S o c k e t ( " l o c a l h o s t " , p u e r t o ) ; try { // Recupero un s t r e a m de l e c t u r a 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 ( new I n p u t S t r e a m R e a d e r ( c l i e n t e . getInputStream ( ) ) ) ; String saludo ; while ( ( s a l u d o = br . r e a d L i n e ( ) ) != n u l l ) System . o u t . p r i n t l n ( s a l u d o ) ; } finally { i f ( c l i e n t e != n u l l ) c l i e n t e . c l o s e ( ) ; } } catch ( UnknownHostException e ) { e . printStackTrace () ; } catch ( I O E x c e p t i o n e ) { e . printStackTrace () ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { new C l i e n t e S e n c i l l o ( ) . e j e c u t a ( I n t e g e r . p a r s e I n t ( a r g s [ 0 ] ) ) ; } }

Listado 15.5: Un Socket clinet que conecta al anterior servidor para recibir un saludo.

15.2.3.

Sockets bajo el protocolo UDP

En el caso del Protocolo UDP no se garantiza que los mensajes recibidos lo hagan en el mismo orden que los enviados, y tampoco se garantiza que al receptor lleguen todos los mensajes del emisor, existe la posibilidad de que alguno se pierda por el camino. En este caso el s mil que se utiliza es el de comunicaciones a trav es de un servicio postal. En el caso del protocolo UDP, al contrario que en el caso del protocolo TCP, no disponemos de dos clases diferenciadas dependiendo de si lo que queremos crear es un Socket servidor o un Socket cliente. En el caso del protocolo UDP disponemos de dos clases para realizar la comunicaci on. La clase DatagramPacket la utilizaremos para especicar los datos que queremos enviar o recibir. La clase DatagramSocket es la que se encarga de enviar o recibir cada uno de los DatagramPacket. Dependiendo del constructor que utilicemos para instanciar la clase DatagramPacket estaremos creando un datagrama para enviar datos, o un datagrama para recibir datos. De modo an alogo, dependiendo del constructor que utilicemos para instanciar la clase DatagramSocket estaremos creando un Socket UDP capaz de recibir datos en un cierto puerto, o un Socket UDP capaz de enviar datos a un determinado servidor.

228 15.2.3.1.

PARA LA RED CAP ITULO 15. PROGRAMACION Sockets UDP en el lado del servidor

Para crear un Socket UDP en el lado del servidor debemos utilizar el constructor de la clase DatagramSocket que recibe como par ametro el puerto de conexi on public DatagramSocket(int puerto). Una vez creado el Socket UDP, la clase DatagramSocket nos permitir a tanto enviar como recibir paquetes en el puerto indicado. Para recibir paquetes necesitamos crear una instancia de la clase DatagramPacket indicando el buer sobre el que se leer an los datos y su tama no, por ejemplo new DatagramPacket(new byte[100], 100) nos permitir a leer hasta 100 bytes del paquete recibido, si el paquete recibido contiene m as de 100 bytes, el resto ser a ignorado. El Listado 15.6 muestra un ejemplo de lectura de un paquete UDP.
1 2 3 4 5 6

byte [ ] b u f f e r = new byte [ 8 1 0 2 4 ] ; DatagramPacket p a q u e t e = new DatagramPacket ( b u f f e r , b u f f e r . l e n g t h ) ; DatagramSocket s e r v i d o r = new DatagramSocket ( 1 2 3 4 5 ) ; // P u e r t o de e s c u c h a 12345 s e r v i d o r . r e c e i v e ( p a q u e t e ) ; // El m e todo r e c e i v e e s b l o q u e a n t e InetSocketAddress d i r e c c i o n C l i e n t e = paquete . getSocketAddress ( ) ; System . o u t . p r i n t l n ( " H o s t c l i e n t e : " + d i r e c c i o n C l i e n t e . getHostName ( ) ) ;

Listado 15.6: Lectura de un paquete mediante el protocolo UDP. Para enviar un paquete como contestaci on al cliente del que se acaba de recibir un paquete crear amos una nueva instancia de la clase DatagramPacket pero esta vez utilizando el constructor que incluye un tercer par ametro con la direcci on de destino del paquete public DatagramPacket(byte[] buffer, int tamBuffer, SocketAddress direccion). El listado 15.7 es una continuaci on del listado 15.6 donde el servidor env a un paquete de respuesta al cliente.
7 8 9

bytes [ ] mensaje = " Hola " . getBytes ( ) ; DatagramPacket p a q u e t e R e s p u e s t a = new DatagramPacket ( mensaje , m e n s a j e . length , d i r e c c i o n C l i e n t e ) ; s e r v i d o r . send ( paqueteRespuesta ) ;

Listado 15.7: Escritura de un paquete mediante el protocolo UDP. Como puedes observar en el Listado 15.7, es posible utilizar la misma instancia de la clase DatagramSocket para enviar datos. La direcci on a la cual se debe enviar el paquete la obtiene la clase DatagramSocket a partir de la clase DatagramPacket. 15.2.3.2. Sockets UDP en el lado del cliente

Para escribir un cliente utilizando el protocolo UDP utilizamos de nuevo las clases DatagramPacket y DatagramSocket, pero esta vez, al crear una instancia de DatagramPacket, indicamos la direcci on y el puerto del servidor al que queremos hacer llegar el paquete, por ejemplo new DatagramPacket(new byte[1], 1, inetAddress, 12345). Para crear la instancia de DatagramSocket utilizaremos el constructor por defecto new DatagramSocket(). Finalmente, para enviar el paquete utilizaremos de nuevo el m etodo send(paquete) de la clase DatagramSocket. En el Listado 15.8 se muestra un ejemplo de env o de un paquete hacia un servidor UDP.

15.2. TRABAJO CON SOCKETS

229

1 2 3

DatagramPacket p a q u e t e = new DatagramPacket ( new byte [ 1 ] , 12345) ; DatagramSocket s e r v i d o r = new DatagramSocket ( ) ; s e r v i d o r . send ( paquete ) ;

1 , inetAddress ,

Listado 15.8: Env o de un paquete mediante el protocolo UDP desde un cliente a un servidor. Igual que en el caso del servidor, el cliente puede quedar a la escucha de los paquetes que reciba desde el servidor, como se muestra en el Listado 15.9 que es continuaci on del Listado 15.8.
4 5 6

byte [ ] b u f f e r = new byte [ 8 1 0 2 4 ] ; p a q u e t e = new DatagramPacket ( b u f f e r , s e r v i d o r . r e c e i v e ( paquete ) ;

buffer . length ) ;

Listado 15.9: Recepci on de paquetes por parte de un cliente.

Cuestiones.
1. Tanto el m etodo accept() de la clase ServerSocket como el m etodo receive() de la clase DatagramSocket son bloqueantes. Qu e debes utilizar para que tu aplicaci on no se quede ((congelada)) esperando a que se retorne de estos dos m etodos?. 2. El protocolo UDP no comprueba que un paquete ha llegado efectivamente a su destino. C omo podr as asegurarte de que s que llega a su destino? 3. El protocolo UDP tampoco tiene en cuenta que el orden de los paquetes recibidos es el mismo que el de los paquetes enviados. C omo podr as reordenar los paquetes que recibe tu aplicaci on para garantizar que siguen el mismo orden con el que se enviaron? 4. Al utilizar Sockets bajo el protocolo TCP, C omo puedes darte cuenta de que la conexi on entre el cliente y el servidor se ha cortado? Y en el caso del protocolo UDP?

Ejercicios.
1. Itenta crear una aplicaci on de chat. En la aplicaci on hay una parte de servidor, que atiende la conexi on de nuevos clientes. Cada vez que un cliente se conecta al servidor de chat queda registrado de modo que recibe todos los mensajes que env an el resto de clientes.

Lecturas recomendadas.
Una excelente referencia para casi cualquier tema relacionado con la programaci on para la red es el libro de Elliotte Rusty Harold [12].

230

PARA LA RED CAP ITULO 15. PROGRAMACION

Cap tulo 16

Patrones de dise no
Contenidos
16.1. Principios de POO . . . . . . . . . . . . . . . . . . . 16.2. Qu e son los patrones de dise no? . . . . . . . . . . 16.3. Qu e es el acoplamiento entre clases y por qu e hay que evitarlo? . . . . . . . . . . . . . . . . . . . . . . 16.4. Grupos de patrones de dise no . . . . . . . . . . . . 16.5. El patr on de dise no Singleton . . . . . . . . . . . . 16.5.1. Situaci on que intenta resolver . . . . . . . . . . . . 16.5.2. Ejemplo de implementaci on . . . . . . . . . . . . . 16.6. El patr on de dise no Factory Method . . . . . . . . 16.6.1. Situaci on que intenta resolver . . . . . . . . . . . . 16.6.2. Ejemplo de implementaci on . . . . . . . . . . . . . 16.7. El patr on de dise no Abstract Factory . . . . . . . . 16.7.1. Situaci on que intenta resolver . . . . . . . . . . . . 16.7.2. Ejemplo de implementaci on . . . . . . . . . . . . . 16.8. El patr on de dise no Strategy . . . . . . . . . . . . . 16.8.1. Situaci on que intenta resolver . . . . . . . . . . . . 16.8.2. Ejemplo de implementaci on . . . . . . . . . . . . . 16.9. El patr on de dise no Observer . . . . . . . . . . . . 16.9.1. Situaci on que intenta resolver . . . . . . . . . . . . 16.9.2. Ejemplo de implementaci on . . . . . . . . . . . . . 16.10. El patr on de dise no Decorator . . . . . . . . . . . . 16.10.1.Situaci on que intenta resolver . . . . . . . . . . . . 16.10.2.Ejemplo de implementaci on . . . . . . . . . . . . . 232 233 233 233 233 234 234 235 235 236 238 238 238 244 245 245 247 247 248 249 250 250

. . . . . . . . . . . .

Introducci on
En este cap tulo se presenta, en primer lugar y a modo de resumen, los Principios de Programaci on Orientada a Objetos. A continuaci on se dene lo que son los patrones de dise no software y por 231

232

CAP ITULO 16. PATRONES DE DISENO

qu e son u tiles. El grueso del cap tulo lo forma la presentaci on de algunos de los patrones de dise no m as utilizados en el desarrollo de proyectos inform aticos. Los patrones de dise no no est an ligados a ning un lenguaje de programaci on en particular, son directrices que nos pueden ayudar en la escritura de c odigo.

16.1.

Principios de POO

A lo largo de este libro se ha intentado presentar no s olo el lenguaje de programaci on Java, y las herramientas de ayuda en el desarrollo de proyectos, si no tambi en las buenas pr acticas a seguir en la codicaci on y en el dise no de nuestras aplicaciones. Las buenas pr acticas aplicadas al dise no de software orientado a objetos constituyen sus principios, unas normas de car acter general que conviene seguir en la construcci on de software. Siguiendo las expuestas en las referencias [8] y [9] y de modo resumido y son: Encapsular lo que var a. Favorecer la composici on frente a la herencia. Programar orientado a la interface no a la implementaci on. Evitar el acoplamiento entre clases. Reducir la responsabilidad de cada clase. Los principios SOLID son otro grupo de principios a tener en cuenta en el dise no de software. Estos principios establecen: Single responsability Una clase debe tener una u nica responsabilidad que justique su existencia. Open close principle La denici on de una clase debe ser abierta para su extensi on pero cerrada para su modicaci on. Liskov substitution Siempre debe ser posible sustituir una clase padre por otra hija sin que cambie el comportamiento de la aplicaci on. Interface segregation Una clase s olo debe implementar un interface si es necesario que ofrezca todos los m etodos que declara el interface, y no s olo unos cuantos. Dependency inversion Las clases no deben crear instancias de otras clases con las que trabajen, la dependencia de una clase con respecto de otra debe inyectarse desde fuera de la clase. Estos principios generales pueden concretarse, a veces, es soluciones bien conocidas a problemas recurrentes en el dise no del software. Y precisamente estas soluciones son lo que se conoce como Patrones de dise no.

SON LOS PATRONES DE DISENO? 16.2. QUE

233

16.2.

Qu e son los patrones de dise no?

Es usual que, durante el desarrollo de un proyecto inform atico, nos encontremos de modo recurrente con el mismo tipo de problemas. Por ejemplo c omo garantizar la existencia de una u nica instancia para poder acceder a un determinado dispositivo. O c omo estructurar una aplicaci on basada en un iterface gr aco para que me permita m ultiples representaciones de los mismos datos. En este u ltimo caso ya has visto un patr on de dise no muy potente, MVC, en el cap tulo 11. Los patrones de dise no son soluciones bien conocidas y ampliamente empleadas para resolver problemas recurrentes en el dise no de aplicaciones inform aticas. Cada uno de los patrones de dise no tiene, o suele tener, un nombre estandarizado, lo que dene un vocabulario com un que facilita el intercambio de ideas, y una plantilla de aplicaci on que muestra cuales son sus componentes y c omo se relacionan entre si.

16.3.

Qu e es el acoplamiento entre clases y por qu e hay que evitarlo?

Cuando al utilizar una clase desde el c odigo de otra, la clase cliente conoce detalles de la implementaci on de la clase que utiliza decimos que las dos clases est an fuertemente acopladas. El acoplamiento muchas veces implica que al cambiar la implementaci on de una clase, las clases cliente fuertemente acopladas con ella dejan de funcionar, deben ser modicadas para reejar los cambios en la clase inicial. Esta coyuntura nalmente desemboca en una serie de modicaciones en cascada a lo largo y ancho del c odigo de la aplicaci on. Lo que hay que evitar, ante todo, es que una clase dependa de los detalles de implementaci on de otra para que pueda utilizarla. Y este es el principio b asico que encontramos en todos los patrones de dise no, la independencia de la implementaci on concreta entre clases.

16.4.

Grupos de patrones de dise no

Los patrones de dise no se agrupan por su cometido, as nos encontramos con patrones de dise no de creaci on (Singleton, Factory method, Abstract factory ), de comportamiento (Strategy, Observer ) y estructurales (Decorator ) entre los m as conocidos sin ser una lista exhaustiva. Por cuesti on de espacio presentamos los indicados entre par entesis, dejando el resto para su consulta en la bibliograf a.

16.5.

El patr on de dise no Singleton

Aparentemente este es un patr on de dise no muy sencillo que acaba teniendo una implementaci on sosticada cuando se utiliza en ambientes de programaci on concurrente.

234

CAP ITULO 16. PATRONES DE DISENO

16.5.1.

Situaci on que intenta resolver

El patr on de dise no Singleton garantiza que s olo existe una instancia de una determinada clase. La clase no se instancia con el operador new si no a trav es de la llamada a un m etodo que siempre devuelve la misma instancia y u nica instancia de la clase. Este patr on de dise no es u til cuando queremos garantiza la unicidad de una instancia, por ejemplo nuestra aplicaci on s olo conoce una instancia de la clase que accede a una impresora, o al sistema de cheros.

16.5.2.

Ejemplo de implementaci on

En el listado 16.1 se muestra una implementaci on de este patr on de dise no. El las l neas 4-6 denimos como private el constructor por defecto de la clase, de esto modo prohibimos la creaci on de instancias de esta clase con el operador new. Por otro lado, al existir u nicamente el constructor por defecto con acceso private no se puede extender la clase. En este caso, el modicador final no es necesario, pero sirve para documentar la clase. En la l nea 3, denimos una referencia a la propia clase, que ser a la que devolvamos cada vez que se pida a trav es de la llamada al m etodo getInstancia() denido entra las l neas 8-12.
1 2 3 4 5 6 7 8 9 10 11 12 13

public c l a s s S i n g l e t o n { private S i n g l e t o n i n s t a n c i a = n u l l ; private S i n g l e t o n ( ) { super ( ) ; } public S i n g l e t o n g e t I n s t a n c i a ( ) { i f ( i n s t a n c i a == n u l l ) i n s t a n c i a = new S i n g l e t o n ( ) ; return i n s t a n c i a ; } }

Listado 16.1: Implementaci on sencilla del patr on Singleton Como ves, recuperamos la instancia llamando al m etodo getInstancia() y no con el operador new. Esta implementaci on es completamente funcional si nuestra aplicaci on no utiliza hilos, pero en caso de utilizarlos podemos encontrarnos con problemas al usar esta sencilla implementaci on. Veamos cual es el problema que puede aparecer, antes de ello, recordemos que la intenci on de este patr on de dise no es que u nicamente exista una instancia de la clase Singleton. Supongamos ahora que se est an ejecutando simult aneamente dos hilos que quieren recuperar una instancia de la clase Singleton, y que por simplicidad s olo contamos con un procesador (o un procesador con un u nico n ucleo). Supongamos que uno de los hilos llama al m etodo getInstancia(), que comprueba la condici on de la l nea 9 y que se eval ua a false, y que justo despu es de evaluar la condici on y antes de crear la instancia en la l nea 10, se cede la ejecuci on al segundo hilo, quien tambi en eval ua la condici on de la l nea 9 obteniendo false (ya que la instancia no fue creada por el primer hilo), y que, ahora s , crea una instancia de la clase Singleton. Cuando el hilo que est a a la espera contin ue su ejecuci on donde qued o (justo despu es de comprobar que no hab a ninguna instancia de Singleton creada), crear a una nueva instancia de la clase Singleton

DE DISENO FACTORY METHOD 16.6. EL PATRON

235

ya que al no volver a comprobar la condici on para este hilo sigue siendo v alido que no existe ninguna instancia. Como resultado nal nos encontraremos con dos instancias de la misma clase, justo lo que no dese abamos que ocurriera. La soluci on a este problema pasa por sincronizar el bloque de creaci on de la instancia de Singleton tal y como se muestra en el Listado 16.2. En este caso, inmediatamente despu es de comprobado si ya hay una instancia creada, escribimos un bloque sincronizado, dentro del cual lo primero que volvemos a comprobar es si la instancia sigue sin estar creada, si no lo est a la creamos dentro del bloque sincronizado garantizando que ning un otro hilo entrar a en este bloque si ya est a en posesi on de otro hilo. La doble comprobaci on es para evitar que no ocurra lo mismo que en el caso anterior, que justo despu es de comprobarla y antes de entrar en el bloque sincronizado otro hilo gane la carrera y ejecute el bloque sincronizado mientras el primero espera a seguir con la ejecuci on.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public c l a s s S i n g l e t o n C o n c u r r e n t e { private S i n g l e t o n C o n c u r r e n t e i n s t a n c i a = n u l l ; private S i n g l e t o n C o n c u r r e n t e ( ) { super ( ) ; } public S i n g l e t o n C o n c u r r e n t e g e t I n s t a n c i a ( ) { i f ( i n s t a n c i a == n u l l ) synchronized ( S i n g l e t o n C o n c u r r e n t e . c l a s s ) { i f ( i n s t a n c i a == n u l l ) i n s t a n c i a = new S i n g l e t o n C o n c u r r e n t e ( ) ; } return i n s t a n c i a ; } }

Listado 16.2: Implementaci on sencilla del patr on Singleton Por qu e no hacemos una u nica comprobaci on dentro del bloque sincronizado, ya que en este momento garantizamos que s olo hay un hilo ejecut andolo? Para evitar el sobrecoste que implica la ejecuci on de bloques sincronizados. Si hacemos una primera comprobaci on fuera del bloque sincronizado y obtenemos false nunca entraremos en el bloque sincronizado y no caeremos en el sobrecoste temporal que esto implica. Si eliminamos la comprobaci on fuera del bloque, siempre tendremos que pugnar por el cerrojo del bloque sincronizado con el consiguiente coste en tiempo de ejecuci on que esto supone.

16.6.

El patr on de dise no Factory Method

Factory Method es otro patr on de dise no dentro del grupo de patrones de dise no de creaci on. Este patr on de dise no es relativamente sencillo y las ventajas que presenta su uso son muchas.

16.6.1.

Situaci on que intenta resolver

Tal y como hemos visto en la introducci on de este cap tulo, el desacoplamiento entre clases muchas veces pasa porque una clase cliente no cree una instancia de otra clase con la que quiera trabajar. Supongamos, por ejemplo, que estamos creando una aplicaci on que representa una f abrica de Veh culos pudiendo ser estos Coches, Motos y Camiones. Si cada vez que necesitamos una instancia

236

CAP ITULO 16. PATRONES DE DISENO

de un tipo concreto usamos el operador new, corremos el riesgo de que la implementaci on de las clases concretas Coche, Moto, Cami on cambie y nuestro c odigo deje de funcionar. Una manera de desacoplar la clase cliente, la que quiere recuperar instancias de Veh culos concretos, y las instancias concretas de Coche, Moto, Cami on es denir una nueva clase encargada de crear las instancias de las clases concretas y devolver las referencias no al tipo concreto si no a un interface o clase abstracta.

16.6.2.

Ejemplo de implementaci on

Primero escribimos un interface del Listado 16.3 que es el tipo de datos abstracto para toda clase de Vehiculos, con independencia de si son Coches, Motos o Camiones. Este interface cuenta con constantes que identican a los distintos tipos de veh culos que se pueden crear.
1 2 3 4 5 6

public i n t e r f a c e V e h i c u l o { s t a t i c f i n a l i n t COCHE = 1 ; s t a t i c f i n a l i n t MOTO = 2 ; s t a t i c f i n a l i n t CAMION = 3 ; String getDescripcion () ; }

Listado 16.3: El tipo de datos abstracto Vehiculo En los Listados 16.4, 16.5 y 16.6 aparecen las clases concretas para cada uno de los tres tipos de Veh culos que la f abrica puede crear.
1 2 3 4 5 6

public c l a s s Coche implements V e h i c u l o { @Override public S t r i n g g e t D e s c r i p c i o n ( ) { return " S o y u n c o c h e " ; } }

Listado 16.4: Clase concreta que representa un Coche

1 2 3 4 5 6

public c l a s s Moto implements V e h i c u l o { @Override public S t r i n g g e t D e s c r i p c i o n ( ) { return " S o y u n a m o t o " ; } }

Listado 16.5: Clase concreta que representa una Moto

1 2 3 4 5 6

public c l a s s Camion implements V e h i c u l o { @Override public S t r i n g g e t D e s c r i p c i o n ( ) { return " S o y u n c a m i o n " ; } }

Listado 16.6: Clase concreta que representa un Camion Cada una de las clases anteriores da una implementaci on concreta para el m etodo getDescripcion().

DE DISENO FACTORY METHOD 16.6. EL PATRON

237

El Listado 16.7 muestra la implementaci on de la f abrica de veh culos. Esta clase posee un u nico m etodo est atico que recibe el tipo del veh culo que deseamos obtener. La f abrica crea la instancia del tipo concreto y la devuelve como una referencia al tipo abstracto Veh culo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

public c l a s s F a b r i c a V e h i c u l o s { public s t a t i c V e h i c u l o c r e a V e h i c u l o ( i n t t i p o ) { Vehiculo vehiculo ; switch ( t i p o ) { case V e h i c u l o .COCHE: v e h i c u l o = new Coche ( ) ; break ; case V e h i c u l o .MOTO: v e h i c u l o = new Moto ( ) ; break ; case V e h i c u l o .CAMION: v e h i c u l o = new Camion ( ) ; break ; default : v e h i c u l o = new Coche ( ) ; break ; } return v e h i c u l o ; } }

Listado 16.7: La f abrica de veh culos Para nalizar, veamos c omo un cliente concreto trabaja con esta f abrica de veh culos. El cliente se muestra en el Listado 16.8. Lo interesante de este cliente es que no crea en ning un momento una instancia concreta de ninguna clase, si no que delega la creaci on de instancias concretas en la clase FabricaVehiculos. Si la implementaci on de una clase concreta cambia, el cliente no lo percibe. Si la f abrica de veh culos incorpora nuevos veh culos, el cliente puede utilizarlos, de nuevo, sin conocer la implementaci on concreta.
1 2 3 4 5 6 7 8 9 10

public c l a s s C l i e n t e { public s t a t i c void main ( S t r i n g [ ] a r g s ) { V e h i c u l o v e h i c u l o = F a b r i c a V e h i c u l o s . c r e a V e h i c u l o ( V e h i c u l o .COCHE) ; System . o u t . p r i n t l n ( v e h i c u l o . g e t D e s c r i p c i o n ( ) ) ; v e h i c u l o = F a b r i c a V e h i c u l o s . c r e a V e h i c u l o ( V e h i c u l o .CAMION) ; System . o u t . p r i n t l n ( v e h i c u l o . g e t D e s c r i p c i o n ( ) ) ; v e h i c u l o = F a b r i c a V e h i c u l o s . c r e a V e h i c u l o ( V e h i c u l o .MOTO) ; System . o u t . p r i n t l n ( v e h i c u l o . g e t D e s c r i p c i o n ( ) ) ; } }

Listado 16.8: Un cliente de la f abrica de veh culos Como ves, este patr on de dise no es muy u til cuando se necesita crear clases concretas de un mismo tipo de datos abstracto, que en el ejemplo mostrado era el interface Vehiculo. Podr amos decir que siempre estamos creando instancias del mismo tipo de datos. Veamos un nuevo patr on de dise no que de alg un modo amplia el patr on de dise no Factory Method, permiti endonos la creaci on de familias de tipos de datos en vez de un u nico tipo de datos.

238

CAP ITULO 16. PATRONES DE DISENO

16.7.

El patr on de dise no Abstract Factory

Este nuevo patr on de dise no tambi en pertenece al grupo de los patrones de dise no de creaci on, pero esta vez no crea un u nico tipo de datos si no que crea familias de tipos de datos.

16.7.1.

Situaci on que intenta resolver

Siguiendo con el ejemplo de la f abrica de veh culos, imagina esta vez que se intenta describir f abricas de veh culos gen ericas. Dos ejemplos de f abricas de veh culos concretas pueden ser una f abrica de veh culos europea y otra f abrica de veh culos japonesa. Ambas f abricas de veh culos producen diferentes tipos de veh culos, que podemos restringir a Turismos, Berlinas y Deportivos. Lo que distingue un veh culo concreto, por ejemplo un Turismo creado en una f abrica europea o en una japonesa no es el proceso de construcci on, ya que ambos veh culos tienen motor, chasis y ruedas. Lo que distingue a un Turismo europeo de uno japones es que una f abrica europea de veh culos utiliza componentes europeos (ruedas europeas, chasis europeos, motores europeos), mientras que una f abrica japonesa utiliza componentes japoneses (ruedas japonesas, chasis japoneses, motores japoneses). Resumiendo, misma gama de productos (Veh culos ) pero construidos con componentes distintos (Motor, Chasis y Ruedas ). La diferencia con el patr on de dise no Factory Mehod (Vehiculo ) es que en este se crea un s olo producto, mientras que en Abstract Factory se crea una gama de productos (Motor, Chasis y Ruedas ).

16.7.2.

Ejemplo de implementaci on

Para ver un ejemplo concreto vamos a empezar describiendo las entidades que forman parte del problema. En una f abrica, tanto si es europea como japonesa se construyen tres modelos de veh culos: Turismos. Berlinas. Deportivos. Vamos a suponer un Turismo est a formado u nicamente por un Chasis y un Motor. Una Berlina est a formada por un Chasis, Motor y Ruedas. Y nalmente, un Deportivo est a formado por Chasis, Motor, Ruedas y Extras. En los Listados 16.9 a 16.12 se muestra el c odigo para los veh culos. En la interface Vehiculo, hemos a nadido un m etodo que permite obtener una descripci on de cada uno de los Vehiculos public void descripcion().
1 2 3 4 5 6 7

public abstract c l a s s V e h i c u l o { // p r o t e c t e d Rueda r u e d a s ; // p r o t e c t e d C h a s i s c h a s i s ; // p r o t e c t e d Motor motor ; // p r o t e c t e d E x t r a s e x t r a s ; public abstract void d e s c r i p c i o n ( ) ; }

Listado 16.9: Un Veh culo como abstracci on de los tres modelos que puede construir una f abrica Turismos, Berlinas y Deportivos

DE DISENO ABSTRACT FACTORY 16.7. EL PATRON

239

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

public c l a s s Turismo extends V e h i c u l o { private C h a s i s c h a s i s ; private Motor motor ; public Turismo ( FabricaComponentes f a b r i c a C o m p o n e n t e s ) { c h a s i s = fabricaComponentes . creaChasis ( ) ; motor = f a b r i c a C o m p o n e n t e s . c r e a M o t o r ( ) ; } @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " - - - D e s c r i p c i o n de un T U R I S M O --- " ) ; chasis . descripcion () ; motor . d e s c r i p c i o n ( ) ; System . o u t . p r i n t l n ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " ) ; } }

Listado 16.10: Un Turismo es una especializaci on de un Vehiculo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

public c l a s s B e r l i n a extends V e h i c u l o { private C h a s i s c h a s i s ; private Motor motor ; private Rueda r u e d a s ; public B e r l i n a ( FabricaComponentes f a b r i c a C o m p o n e n t e s ) { c h a s i s = fabricaComponentes . creaChasis ( ) ; motor = f a b r i c a C o m p o n e n t e s . c r e a M o t o r ( ) ; ruedas = fabricaComponentes . creaRuedas ( ) ; } @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " - - - D e s c r i p c i o n de una B E R L I N A --- " ) ; chasis . descripcion () ; motor . d e s c r i p c i o n ( ) ; ruedas . d e s c r i p c i o n () ; System . o u t . p r i n t l n ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " ) ; } }

Listado 16.11: Una Berlina es una especializaci on de un Vehiculo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public c l a s s D e p o r t i v o extends V e h i c u l o { private C h a s i s c h a s i s ; private E x t r a s e x t r a s ; private Motor motor ; private Rueda r u e d a s ; public D e p o r t i v o ( FabricaComponentes f a b r i c a C o m p o n e n t e s ) { ruedas = fabricaComponentes . creaRuedas ( ) ; c h a s i s = fabricaComponentes . creaChasis ( ) ; motor = f a b r i c a C o m p o n e n t e s . c r e a M o t o r ( ) ; e x t r a s = fabricaComponentes . creaExtras ( ) ; } @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " - - - D e s c r i p c i o n de un D E P O R T I V O --- " ) ; ruedas . d e s c r i p c i o n () ; chasis . descripcion () ; motor . d e s c r i p c i o n ( ) ; extras . descripcion () ; System . o u t . p r i n t l n ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " ) ; } }

240

CAP ITULO 16. PATRONES DE DISENO

Listado 16.12: Un Deportivo es una especializaci on de un Vehiculo Vamos ahora a detallar los componentes que forman parte de cada uno de los diferentes Vehiculos. Si el Veh culo es japones su Chasis es siempre ligero y de aluminio, si es europeo su Chasis es siempre reforzado. El Motor de un Vehiculo japones es siempre de muy bajo consumo y bajas emisiones de CO2; si es europeo, el Motor es de alto rendimiento. Las Ruedas de un Veh culo japones son siempre de muy larga duraci on; mientras que los Veh culos europeos tienen ruedas de perl bajo. Y nalmente, los Veh culos japoneses tienen extras de tipo deportivo japones y los europeos de tipo deportivo. El los listados 16.13 a 16.24 se muestra cada uno de los interface y las especializaciones de cada componente que puede formar parte de un Veh culo.
1 2 3

public i n t e r f a c e C h a s i s { void d e s c r i p c i o n ( ) ; }

Listado 16.13: Un Chasis como abstracci on.

1 2 3 4 5 6

public c l a s s C h a s i s L i g e r o implements C h a s i s { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " C h a s i s l i g e r o d e a l u m i n i o . " ) ; } }

Listado 16.14: Un ChasisLigero es una especializaci on de un Chasis que ser a utilizado s olo en la construcci on de Veh culos japoneses.

1 2 3 4 5 6

public c l a s s C h a s i s R e f o r z a d o implements C h a s i s { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " C h a s i s r e f o r z a d o " ) ; } }

Listado 16.15: Un ChasisReforzado es una especializaci on de un Chasis que ser a utilizado s olo en la construcci on de Veh culos europeos

1 2 3

public i n t e r f a c e E x t r a s { void d e s c r i p c i o n ( ) ; }

Listado 16.16: Unos Extras como abstracci on.

1 2 3 4 5 6 7 8

public c l a s s E x t r a s D e p o r t i v o s E s t i l o J a p o n e s implements E x t r a s { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " E x t r a s d e p o r t i v o s . . . . p e r o a l e s t i l o j a p o n e s . " ) ; } }

DE DISENO ABSTRACT FACTORY 16.7. EL PATRON

241

Listado 16.17: Unos ExtrasDeportivosEstiloJapones es una especializaci on de unos Extras que ser an utilizados s olo en la construcci on de Veh culos japoneses

1 2 3 4 5 6

public c l a s s E x t r a s D e p o r t i v o s implements E x t r a s { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " E x t r a s d e p o r t i v o s . " ) ; } }

Listado 16.18: Unos ExtrasDeportivos es una especializaci on de unos Extras que ser an utilizados s olo en la construcci on de Veh culos europeos

1 2 3

public i n t e r f a c e Motor { void d e s c r i p c i o n ( ) ; }

Listado 16.19: Un Motor como abstracci on.

1 2 3 4 5 6 7 8

public c l a s s MotorBajoConsumo implements Motor { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " M o t o r d e m u y b a j o c o n s u m o y b a j a s e m i s i o n e s d e C O 2 .") ; } }

Listado 16.20: Un MotorBajoCosumo es una especializaci on de un Motor que ser a utilizado s olo en la construcci on de Veh culos japoneses

1 2 3 4 5 6

public c l a s s Mo torA lto Ren dimi ent o implements Motor { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " M o t o r d e a l t o r e n d i m i e n t o . " ) ; } }

Listado 16.21: Un MotorAltoRendimienot es una especializaci on de un Motor que ser a utilizado s olo en la construcci on de Veh culos europeos

1 2 3

public i n t e r f a c e Rueda { void d e s c r i p c i o n ( ) ; }

Listado 16.22: Una Rueda como abstracci on.

1 2 3 4 5 6 7

public c l a s s RuedaLargaDuracion implements Rueda { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " R u e d a s d e l a r g a d u r a c i o n " ) ; }

242
8

CAP ITULO 16. PATRONES DE DISENO

Listado 16.23: Una RuedaLargaDuracion es una especializaci on de una Rueda que ser a utilizada s olo en la construcci on de Veh culos japoneses

1 2 3 4 5 6 7 8

public c l a s s R u e d a P e r f i l B a j o implements Rueda { @Override public void d e s c r i p c i o n ( ) { System . o u t . p r i n t l n ( " R u e d a d e p e r f i l b a j o . " ) ; } }

Listado 16.24: Una RuedaPerlBajo es una especializaci on de una Rueda que ser a utilizada s olo en la construcci on de Veh culos europeos Es el momento de construir las f abricas de componentes. Las f abricas de componentes son las encargadas de crear cada uno de los componentes que forman un Veh culo dependiendo de si estamos en Europa o Jap on. Para implementarlas, vamos a utilizar el patr on de dise no Factory Method. Los listados 16.25 a 16.27 muestran el c odigo para cada una de las f abricas de componentes.
1 2 3

public i n t e r f a c e Rueda { void d e s c r i p c i o n ( ) ; }

Listado 16.25: Una FabricaComponentes como abstracci on.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

public c l a s s F a b r i c a C o m p o n e n t e s J a p o n e s a implements FabricaComponentes { @Override public Rueda c r e a R u e d a s ( ) { return new RuedaLargaDuracion ( ) ; } @Override public C h a s i s c r e a C h a s i s ( ) { return new C h a s i s L i g e r o ( ) ; } @Override public Motor c r e a M o t o r ( ) { return new MotorBajoConsumo ( ) ; } @Override public E x t r a s c r e a E x t r a s ( ) { return new E x t r a s D e p o r t i v o s E s t i l o J a p o n e s ( ) ; } }

Listado 16.26: Una FabricaComponentesJaponesa es una especializaci on de una FabricaComponentes que crea los distintos componentes para cada tipo de Veh culos japoneses

1 2 3

public c l a s s FabricaComponentesEuropea implements FabricaComponentes { @Override

DE DISENO ABSTRACT FACTORY 16.7. EL PATRON


4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

243

public Rueda c r e a R u e d a s ( ) { return new R u e d a P e r f i l B a j o ( ) ; } @Override public C h a s i s c r e a C h a s i s ( ) { return new C h a s i s R e f o r z a d o ( ) ; } @Override public Motor c r e a M o t o r ( ) { return new Mo torA lto Ren dimi ent o ( ) ; } @Override public E x t r a s c r e a E x t r a s ( ) { return new E x t r a s D e p o r t i v o s ( ) ; } }

Listado 16.27: Una FabricaComponentesEuropea es una especializaci on de una FabricaComponentes que crea los distintos componentes para cada tipo de Veh culos europeos Ya nos encontramos en la recta nal para acabar de ver la implementaci on de este patr on de dise no. Vemos por u ltimo, la implementaci on de cada una de las f abricas de veh culos, la japonesa y la europea en los listados 16.28 al 16.30.
1 2 3

public i n t e r f a c e F a b r i c a V e h i c u l o s { Vehiculo creaVehiculo ( TipoVehiculo tipoVehiculo ) ; }

Listado 16.28: Una FabricaVehiculos como abstracci on.


public c l a s s F a b r i c a V e h i c u l o s J a p o n e s a implements F a b r i c a V e h i c u l o s { @Override public V e h i c u l o c r e a V e h i c u l o ( T i p o V e h i c u l o t i p o V e h i c u l o ) { FabricaComponentes f a b r i c a C o m p o n e n t e s = new F a b r i c a C o m p o n e n t e s J a p o n e s a () ; Vehiculo vehiculo ; switch ( t i p o V e h i c u l o ) { case TURISMO: System . o u t . p r i n t l n ( " F a b r i c a n d o u n t u r i s m o j a p o n e s . . . " ) ; v e h i c u l o = new Turismo ( f a b r i c a C o m p o n e n t e s ) ; break ; case BERLINA : System . o u t . p r i n t l n ( " F a b r i c a n d o u n a b e r l i n a j a p o n e s a . . . " ) ; v e h i c u l o = new B e r l i n a ( f a b r i c a C o m p o n e n t e s ) ; break ; case DEPORTIVO: System . o u t . p r i n t l n ( " F a b r i c a n d o u n d e p o r t i v o j a p o n e s . . . " ) ; v e h i c u l o = new D e p o r t i v o ( f a b r i c a C o m p o n e n t e s ) ; break ; default : System . o u t . p r i n t l n ( " F a b r i c a n d o u n t u r i s m o j a p o n e s . . . " ) ; v e h i c u l o = new Turismo ( f a b r i c a C o m p o n e n t e s ) ; break ; } return v e h i c u l o ; }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

244
33

CAP ITULO 16. PATRONES DE DISENO

Listado 16.29: Una FabricaVehiculosJaponesa es una especializaci on de una FabricaVehiculos que crea los distintos tipos de Veh culos japoneses

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

public c l a s s F a b r i c a V e h i c u l o s E u r o p e a implements F a b r i c a V e h i c u l o s { @Override public V e h i c u l o c r e a V e h i c u l o ( T i p o V e h i c u l o t i p o V e h i c u l o ) { FabricaComponentes f a b r i c a C o m p o n e n t e s = new FabricaComponentesEuropea () ; Vehiculo v e h i c u l o = null ; switch ( t i p o V e h i c u l o ) { case TURISMO: System . o u t . p r i n t l n ( " F a b r i c a n d o u n t u r i s m o e u r o p e o . . . " ) ; v e h i c u l o = new Turismo ( f a b r i c a C o m p o n e n t e s ) ; break ; case BERLINA : System . o u t . p r i n t l n ( " F a b r i c a n d o u n a b e r l i n a e u r o p e a . . . " ) ; v e h i c u l o = new B e r l i n a ( f a b r i c a C o m p o n e n t e s ) ; break ; case DEPORTIVO: System . o u t . p r i n t l n ( " F a b r i c a n d o u n d e p o r t i v o e u r o p e o . . . " ) ; v e h i c u l o = new D e p o r t i v o ( f a b r i c a C o m p o n e n t e s ) ; break ; default : System . o u t . p r i n t l n ( " F a b r i c a n d o u n t u r i s m o e u r o p e o . . . " ) ; v e h i c u l o = new Turismo ( f a b r i c a C o m p o n e n t e s ) ; break ; } return v e h i c u l o ; } }

Listado 16.30: Una FabricaVehiculosEuropea es una especializaci on de una FabricaVehiculos que crea los distintos tipos de Veh culos europeos El detalle de inter es en los listados 16.29 y 16.30 est a en la l nea 5 de ambos listados, es la f abrica de veh culos japonesa la que se provee de componentes japoneses y la f abrica europea la que se provee de componentes europeos. Otro ejemplo que com unmente se utiliza como ejemplo del patr on de dise no Abstract Factory es la programaci on de un framework para componer ventanas en distintos sistemas operativos. Las ventanas que queremos crear est an formadas por el mismo tipo de componentes (botones, listas, combo-box, etc etera), pero quien proporciona los componentes es el sistema operativo sobre el que se vaya a ejecutar la aplicaci on, de modo que, por ejemplo, se crean botones distintos si la f abrica de componentes es, digamos por caso, Mac OS X que si la f abrica es Linux.

16.8.

El patr on de dise no Strategy

Este patr on es el primero, de los dos que vamos a ver, del grupo de patrones de comportamiento.

DE DISENO STRATEGY 16.8. EL PATRON

245

16.8.1.

Situaci on que intenta resolver

Cuando intentamos resolver un problema concreto, en la mayor a de las ocasiones existe m as de un algoritmo para encontrar la soluci on. Piensa por ejemplo en los algoritmos de ordenaci on, el objetivo de todos ellos es el mismo, ordenar una secuencia de elemento teniendo en cuenta cierta funci on de comparaci on entre ellos; tomando un caso sencillo podemos concretar que los elementos son n umeros naturales. Para ordenar una secuencia de n umeros naturales podemos utilizar el algoritmo de intercambio, el algoritmo de la burbuja o el algoritmo quicksort. La codicaci on m as exible ser a aquella que permita intercambiar el algoritmo de ordenaci on con el m nimo, o nulo, impacto sobre la aplicaci on. El patr on Strategy nos dice que debemos encapsular cada algoritmo dentro de una clase y hacer estas clases intercambiables para el cliente que las use de modo transparente.

16.8.2.

Ejemplo de implementaci on

Como ejemplo de implementaci on, supongamos que nuestro algoritmo cuenta n umeros naturales. Existir an casos en los que nos interese contar de modo ascendente y otros casos en los que nos interese contar de modo descendente. Incluso puede que a veces nos interese contar de modo ascendente s olo n umeros pares o s olo n umeros impares. F jate que en los cuatros casos anteriores el algoritmo es el mismo: Contar n umeros. Para hacer los algoritmos intercambiables, todos ellos van a implementar el interface que se muestra en el listado 16.31. Las cuatro implementaciones de contadores particulares se muestran en los Listados 16.32 al 16.35.
1 2 3 4 5 6 7 8

public i n t e r f a c e Contador { s t a t i c f i n a l i n t ASCENDENTE = 1 ; s t a t i c f i n a l i n t DESCENDENTE = 2 ; s t a t i c f i n a l i n t PARES = 3 ; s t a t i c f i n a l i n t IMPARES = 4 ; S t r i n g cuenta ( ) ; }

Listado 16.31: Este es el comportamiento com un a todos los algoritmo: Contar

1 2 3 4 5 6 7 8 9 10 11

public c l a s s C o n t a d o r A s c e n d e n t e implements Contador { @Override public S t r i n g c u e n t a ( ) { S t r i n g cuenta = " " ; for ( int i = 0 ; i < 10; c u e n t a += i + " , " ; return c u e n t a ; } } i ++)

Listado 16.32: Implementaci on de un contador ascendente.

1 2 3

public c l a s s C o n t a d o r D e s c e n d e n t e implements Contador { @Override

246
4 5 6 7 8 9 10 11 12 13

CAP ITULO 16. PATRONES DE DISENO

public S t r i n g c u e n t a ( ) { S t r i n g cuenta = " " ; f o r ( i n t i = 9 ; i >= 0 ; i ) c u e n t a += i + " , " ; return c u e n t a ; } }

Listado 16.33: Implementaci on de un contador descendente.

1 2 3 4 5 6 7 8 9 10 11 12 13

public c l a s s C o n t a d o r I m p a r e s implements Contador { @Override public S t r i n g c u e n t a ( ) { S t r i n g cuenta = " " ; for ( int i = 1 ; i < 10; c u e n t a += i + " , " ; return c u e n t a ; } } i += 2 )

Listado 16.34: Implementaci on de un contador de n umeros impares.

1 2 3 4 5 6 7 8 9 10 11 12 13

public c l a s s C o n t a d o r P a r e s implements Contador { @Override public S t r i n g c u e n t a ( ) { S t r i n g cuenta = " " ; for ( int i = 0 ; i < 10; c u e n t a += i + " , " ; return c u e n t a ; } } i += 2 )

Listado 16.35: Implementaci on de un contador de n umeros pares. Ahora escribamos un cliente que pueda utilizar estos algoritmos de modo intercambiable, tal y como muestra el Listado 16.36:
1 2 3 4 5 6 7 8 9 10 11 12 13

import c o n t a r . Contador ; public c l a s s C l i e n t e { private Contador c o n t a d o r ; public void c u e n t a ( ) { contador . cuenta ( ) ; } public void s e t C o n t a d o r ( Contador c o n t a d o r ) { this . contador = contador ; } }

Listado 16.36: Un cliente que puede utilizar cualquiera de los anteriores algoritmo para contar.

DE DISENO OBSERVER 16.9. EL PATRON

247

Esta clase Cliente tiene una caracter stica interesante para poder utilizar cualquier tipo de algoritmo para contar, el algoritmo particular se le inyecta a trav es del m etodo public void setContador(Contador contador). A esta t ecnica de relaci on entre clase se le llama Inyecci on de Dependencias o Inversi on de Control. Finalmente, y por completar el ejemplo, el Listado 16.37 muestra c omo utilizar la clase Cliente inyect andole los cuatro tipos de contadores.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

import import import import

contar contar contar contar

. ContadorAscendente ; . ContadorDescendente ; . ContadorImpares ; . ContadorPares ;

public c l a s s P r i n c i p a l { private void e j e c u t a ( ) { C l i e n t e c l i e n t e = new C l i e n t e ( ) ; c l i e n t e . s e t C o n t a d o r ( new C o n t a d o r A s c e n d e n t e ( ) ) ; c l i e n t e . cuenta ( ) ; c l i e n t e . s e t C o n t a d o r ( new C o n t a d o r D e s c e n d e n t e ( ) ) ; c l i e n t e . cuenta ( ) ; c l i e n t e . s e t C o n t a d o r ( new C o n t a d o r P a r e s ( ) ) ; c l i e n t e . cuenta ( ) ; c l i e n t e . s e t C o n t a d o r ( new C o n t a d o r I m p a r e s ( ) ) ; c l i e n t e . cuenta ( ) ; } public s t a t i c void main ( S t r i n g [ ] new P r i n c i p a l ( ) . e j e c u t a ( ) ; } } args ) {

Listado 16.37: Podemos cambiar din amicamente el tipo de contador que utiliza la clase Cliente.

16.9.

El patr on de dise no Observer

Observer es otro patr on de dise no de comportamiento. Lo hemos utilizado ampliamente en el cap tulo 11 dedicado a la programaci on de interfaces gr acos de usuario.

16.9.1.

Situaci on que intenta resolver

Cuando intentamos monitorizar el estado, por ejemplo, de un atributo en una determinada instancia, la opci on directa, y altamente ineciente, es interrogar cada cierto tiempo por el estado de ese atributo. La ineciencia de este m etodo se debe a que aunque no cambie el estado del atributo, estamos consumiendo tiempo en averiguar si lo ha hecho. Es m as sencillo que sea la propia instancia quien nos avise de que su atributo ha cambiado de valor, no es el cliente el que consulta, si no que espera a que la instancia monitorizada le avise del cambio. Este comportamiento se conoce como Principio de Hollywood que se puede resumir en la frase No me llames, ya te llamar e yo. Recuerda c omo program abamos, en Swing, los escuchadores para un componente gr aco, por ejemplo un bot on. Lo que hac amos era escribir un escuchador que era noticado cada vez que ocurr a un evento sobre el bot on. Y este es, precisamente, el patr on de dise no Observer.

248

CAP ITULO 16. PATRONES DE DISENO

16.9.2.

Ejemplo de implementaci on

Como ejemplo, escribamos una peque na novela de esp as basada en la guerra fr a, cuando a un exist a el KGB. El KGB tiene esp as que informan de todos sus movimientos a sus superiores. Un mismo esp a puede trabajar para m as de un superior, el mismo esp a puede estar trabajando al mismo tiempo para su jefe directo y para el ministerio del interior. Quien genera los informes (eventos) es el esp a, el esp a est a siendo observado; y quien recibe los informes son sus superiores que act uan como observadores. Para que los superiores de un esp a reciban mensajes establecen el mecanismo de que el esp a haga una llamada al m etodo informaObservadores(String accion) de sus jefes. Y un esp a conoce en todo momento quienes son los jefes para los que trabaja, los mantiene en un lista donde puede a nadir nuevos jefes para informar cuando inicia una misi on, o eliminarlos cuando acaba su misi on. Los Listados 16.38 y 16.39 muestran el c odigo referente al esp a.
1 2 3 4 5

public i n t e r f a c e Observado { public void addObservador ( O b s e r v a d o r o b s e r v a d o r ) ; public void removeObservador ( O b s e r v a d o r o b s e r v a d o r ) ; public void i n f o r m a O b s e r v a d o r e s ( S t r i n g a c c i o n ) ; }

Listado 16.38: Esta interface es una abstracci on del comportamiento com un a todos los esp as.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

import j a v a . u t i l . A r r a y L i s t ; import j a v a . u t i l . L i s t ; public c l a s s EspiaKGB implements Observado { private S t r i n g nombre ; private L i s t <Observador > o b s e r v a d o r e s = new A r r a y L i s t <Observador > () ; public EspiaKGB ( S t r i n g nombre ) { super ( ) ; t h i s . nombre = nombre ; } @Override public void addObservador ( O b s e r v a d o r o b s e r v a d o r ) { o b s e r v a d o r e s . add ( o b s e r v a d o r ) ; } @Override public void removeObservador ( O b s e r v a d o r o b s e r v a d o r ) { o b s e r v a d o r e s . remove ( o b s e r v a d o r ) ; } @Override public void i n f o r m a O b s e r v a d o r e s ( S t r i n g a c c i o n ) { for ( Observador observador : o b s e r v a d o r e s ) o b s e r v a d o r . i n f o r m e ( nombre + " : " + a c c i o n ) ; } }

Listado 16.39: Esta es la clase que representa a un esp a. Es a trav es del m etodo informaObservadores(String accion) por el que el esp a env a los informes a todos y cada uno de sus jefes. Los Listados 16.40 y 16.41 muestran el c odigo relativo a los jefes.

DE DISENO DECORATOR 16.10. EL PATRON

249

1 2 3

public i n t e r f a c e O b s e r v a d o r { public void i n f o r m e ( S t r i n g e v e n t o ) ; }

Listado 16.40: Esta interface es una abstracci on del comportamiento com un a todos los jefes.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

public c l a s s JefeEspiasKGB implements O b s e r v a d o r { private S t r i n g nombre ; public JefeEspiasKGB ( ) { super ( ) ; nombre = " A n onimo " ; } public JefeEspiasKGB ( S t r i n g nombre ) { super ( ) ; t h i s . nombre = nombre ; } @Override public void i n f o r m e ( S t r i n g e v e n t o ) { System . o u t . p r i n t l n ( nombre + " r e c i b e d e " + e v e n t o ) ; } }

Listado 16.41: Esta es la clase que representa a un jefe de esp a. Y nalmente en el Listado 16.42 escribimos una sencill sima novela de esp as.
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public c l a s s P r u e b a E s p i a s { private void e j e c u t a ( ) { Observado e s p i a B o r i s = new EspiaKGB ( " B o r i s " ) ; Obs e r v a d o r j e f e B o r i s K G B = new JefeEspiasKGB ( " J e f e d e B o r i s " ) ; e s p i a B o r i s . addObservador ( j e f e B o r i s K G B ) ; Obs e r v a d o r m i n i s t r o D e f e n s a U R S S = new JefeEspiasKGB ( " M i n i s t e r i o d e l interior " ) ; e s p i a B o r i s . addObservador ( m i n i s t r o D e f e n s a U R S S ) ; e s p i a B o r i s . informaObservadores ( " Estoy s i g u i e n d o al topo " ) ; } public s t a t i c void main ( S t r i n g [ ] new P r u e b a E s p i a s ( ) . e j e c u t a ( ) ; } } args ) {

Listado 16.42: Cada vez que Boris realiza una acci on informa a sus superiores. Y este es el resultado de su ejecuci on: Jefe de Boris recibe de Boris: Estoy siguiendo al topo Ministerio del interior recibe de Boris: Estoy siguiendo al topo

16.10.

El patr on de dise no Decorator

Este es el u nico patr on de dise no estructural que vamos a ver. Igual que el patr on Observer, este patr on es ampliamente utilizado Java, en este caso en el paquete java java.io de entrada/salida en Java.

250

CAP ITULO 16. PATRONES DE DISENO

16.10.1.

Situaci on que intenta resolver

Hemos visto algunos ejemplos de este patr on en las clases de entrada/salida, cuando bamos recubriendo una clase de partida con nuevas clases hasta alcanzar una clase con la funcionalidad que necesit abamos. En algunos casos necesitamos a nadir nuevas caracter sticas a nuestras clases pero el uso directo de la herencia dispara exponencialmente el n umero de clases que tendr amos que implementar. Ve amoslo con un ejemplo. Supongamos que estamos desarrollando una aplicaci on para un concesionario de coches. El coche b asico de cada serie tiene un precio, y este precio se incrementa cuando el comprador va a nadiendo nuevos extras al coche. Por ejemplo, sobre el coche b asico podemos elegir pintura metalizada o aire acondicionado. Si cada extra estuviese codicado como una nueva clase que extiende a la clase que representa el coche b asico deber amos escribir una nueva clase para el coche b asico con aire acondicionado, otra para el coche b asico con pintura metalizada, y como no, una tercera clase hija para el caso en que alg un cliente quiera a nadir aire acondicionado y pintura metalizada al coche b asico. Claramente el uso de la herencia no es buena idea en este caso. Lo que necesitamos es que cada uno de los extras se a nada sobre la clase base de manera independiente del resto de los extras, y f jate que no por ello dejamos de tener un veh culo. La idea del patr on de dise no Decorator es tomar una clase base e ir a nadi endole nuevas caracter sticas sin utilizar exclusivamente la herencia.

16.10.2.

Ejemplo de implementaci on

Como ejemplo de implementaci on vamos a utilizar una peque na modicaci on del ejemplo expuesto en la secci on anterior, un concesionario que quiere calcular el precio nal de los veh culos que vende pudiendo ser estos coches o camiones. Al veh culo b asico el comprador le puede a nadir extras, tales como aire acondicionado o pintura metalizada. Abstraigamos la idea de un Veh culo como una clase abstracta de la que Coche y Cami on ser an dos clases concretas, tal y como muestran los Listados 16.43 a 16.45.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public abstract c l a s s V e h i c u l o { private S t r i n g d e s c r i p c i o n ; private f l o a t p r e c i o ; public V e h i c u l o ( ) { super ( ) ; } public V e h i c u l o ( S t r i n g d e s c r i p c i o n , super ( ) ; this . d es cri pc ion = d esc ri pci on ; this . precio = precio ; } public S t r i n g g e t D e s c r i p c i o n ( ) { return d e s c r i p c i o n ; } public f l o a t g e t P r e c i o ( ) { return p r e c i o ; } float precio ) {

DE DISENO DECORATOR 16.10. EL PATRON


23

251

Listado 16.43: Abstracci on de un Veh culo.

1 2 3 4 5

public c l a s s Coche extends V e h i c u l o { public Coche ( S t r i n g d e s c r i p c i o n , f l o a t p r e c i o ) { super ( d e s c r i p c i o n , p r e c i o ) ; } }

Listado 16.44: Un Coche como clase concreta que extiende a Veh culo.

1 2 3 4 5

public c l a s s Camion extends V e h i c u l o { public Camion ( S t r i n g d e s c r i p c i o n , f l o a t p r e c i o ) { super ( d e s c r i p c i o n , p r e c i o ) ; } }

Listado 16.45: Un Cami on como clase concreta que extiende a Veh culo. La clase que decora a Veh culo es VehiculoConExtras y es ella quien resuelve de una manera elegante el problema, por una parte extiende a Veh culo ya que un VehiculoConExtras sigue siendo un Veh culo, y por otra parte contiene una referencia al Veh culo que decora para poder delegar la llamada a sus m etodos. En los Listados 16.46 a 16.48 se muestran las clases que a naden extras a los Veh culos base.
1 2 3 4 5 6 7 8

public abstract c l a s s V e h i c u l o C o n E x t r a s extends V e h i c u l o { protected V e h i c u l o v e h i c u l o ; public V e h i c u l o C o n E x t r a s ( V e h i c u l o v e h i c u l o ) { super ( ) ; this . vehiculo = vehiculo ; } }

Listado 16.46: Abstraci on de un Veh culo que a nade equipamiento extra.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public c l a s s V e h i c u l o C o n A i r e A c o n d i c i o n a d o extends V e h i c u l o C o n E x t r a s { public V e h i c u l o C o n A i r e A c o n d i c i o n a d o ( V e h i c u l o v e h i c u l o ) { super ( v e h i c u l o ) ; } @Override public S t r i n g g e t D e s c r i p c i o n ( ) { return v e h i c u l o . g e t D e s c r i p c i o n ( ) + " , a i r e a c o n d i c i o n a d o " ; } @Override public f l o a t g e t P r e c i o ( ) { return v e h i c u l o . g e t P r e c i o ( ) + 3 0 0 . 6 7 f ; } }

Listado 16.47: Un Veh culo que a nade el extra aire acondicionado.

1 2 3

public c l a s s V e h i c u l o C o n P i n t u r a M e t a l i z a d a extends V e h i c u l o C o n E x t r a s { public V e h i c u l o C o n P i n t u r a M e t a l i z a d a ( V e h i c u l o v e h i c u l o ) { super ( v e h i c u l o ) ;

252
4 5 6 7 8 9 10 11 12 13 14 15

CAP ITULO 16. PATRONES DE DISENO

} @Override public S t r i n g g e t D e s c r i p c i o n ( ) { return v e h i c u l o . g e t D e s c r i p c i o n ( ) + " , p i n t u r a m e t a l i z a d a " ; } @Override public f l o a t g e t P r e c i o ( ) { return v e h i c u l o . g e t P r e c i o ( ) + 6 0 0 . 4 5 f ; } }

Listado 16.48: Un Veh culo que a nade el extra pintura metalizada. Y nalmente el Listado 16.49 muestra c omo utilizar las clases decoradoras para un par de Veh culos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

public c l a s s P r u e b a D e c o r a t o r { private void e j e c u t a ( ) { V e h i c u l o v e h i c u l o = new Coche ( " B e r l i n a " , 2 0 0 0 0 ) ; v e h i c u l o = new V e h i c u l o C o n A i r e A c o n d i c i o n a d o ( v e h i c u l o ) ; v e h i c u l o = new V e h i c u l o C o n P i n t u r a M e t a l i z a d a ( v e h i c u l o ) ; System . o u t . p r i n t l n ( " E l p r e c i o d e e s t e c o c h e e s : " + v e h i c u l o . g e t P r e c i o () ) ; System . o u t . p r i n t l n ( v e h i c u l o . g e t D e s c r i p c i o n ( ) ) ; v e h i c u l o = new Camion ( " T r a n s p o r t e " , 1 0 0 0 0 0 ) ; v e h i c u l o = new V e h i c u l o C o n A i r e A c o n d i c i o n a d o ( v e h i c u l o ) ; System . o u t . p r i n t l n ( " E l p r e c i o d e e s t e c a m i o n es : " + v e h i c u l o . getPrecio () ) ; System . o u t . p r i n t l n ( v e h i c u l o . g e t D e s c r i p c i o n ( ) ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { new P r u e b a D e c o r a t o r ( ) . e j e c u t a ( ) ; } }

Listado 16.49: Ejemplo de creaci on de un par de Veh culos con algunos extras. El resultado de la ejecuci on de este ejemplo es: El precio de este coche es: 20901.12 Berlina, aire acondicionado, pintura metalizada El precio de este cami on es: 100300.67 Transporte, aire acondicionado Tanto el precio como la descripci on de cada veh culo se obtienen a partir de la clase base y las clases que van decorando a esta clase base.

Ejercicios.
1. Escribe una aplicaci on donde sea posible intercambiar de forma sencilla cada uno de los algoritmos de ordenaci on. 2. Escribe una aplicaci on para calcular el precio de un caf e. Al caf e se le puede a nadir una pizca de leche, leche condesada o el toque de alg un licor. Evidentemente, el precio nal depende del n umero de antojos a nadidos al caf e base.

DE DISENO DECORATOR 16.10. EL PATRON

253

Lecturas recomendadas.
El libro de referencia para los patrones de dise no es el escrito por The gang of four del que existe traducci on al espa nol [8]. De nuevo, los libro de la colecci on Head rst son de una muy clara exposici on, la manera de presentar los contenidos es muy did actica y los ejemplos utilizados claros y representativos. En particular la referencia [9] sobre patrones de dise nos es casi de obligada lectura.

254

CAP ITULO 16. PATRONES DE DISENO

Ap endice A

build.xml
< 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 " d e f a u l t=" t e s t "> < ! 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 " /> 18 < ! 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 > 19 < 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 " /> 20 < ! 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 > 21 < 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 " /> 22 < ! 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 o n > 23 < 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 " /> 24 < ! D i r e c t o r i o p a r a e l f i c h e r o empaquetado > 25 < 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 " /> 26 < ! Nombre d e l f i c h e r o empaquetado > 27 < 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 " />
1 2 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

< p r o p e r t y name=" j u n i t . d i r " l o c a t i o n=" / U s e r s / o s c a r / O s c a r / S o f t w a r e / e c l i p s e H e l i o s S E 6 4 / p l u g i n s / o r g . j u n i t _ 4 . 8 . 1 . v 4 _ 8 _ 1 _ v 2 0 1 0 0 4 2 7 - 1 1 0 0 " /> <path i d=" j u n i t "> < f i l e s e t d i r=" $ { j u n i t . d i r } " i n c l u d e s=" * . j a r " /> < f i l e s e t d i r=" / U s e r s / o s c a r / O s c a r / S o f t w a r e / e c l i p s e H e l i o s S E 6 4 / p l u g i n s " i n c l u d e s=" o r g . h a m c r e s t . c o r e _ 1 . 1 . 0 . v 2 0 0 9 0 5 0 1 0 7 1 0 0 0 . j a r " /> <f i l e l i s t > </ f i l e l i s t > </ path> < ! Path p a r a c o m p i l a r l a s c l a s e s de prueba > <path i d=" t e s t . c o m p i l e . c l a s s p a t h "> < f i l e s e t d i r=" $ { l i b } " i n c l u d e s=" * . j a r " /> < 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 } " /> </ path> < ! Path p a r a e j e c u t a r l a s c l a s e s de prueba > <path i d=" t e s t . c l a s s p a t h "> <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 " /> < 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 } " /> </ path>

255

256
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

APENDICE A. BUILD.XML

< 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 "> < d e l e t e d i r=" $ { d i s t . d i r } " /> < d e l e t e d i r=" $ { b u i l d . d i r } " /> < d e l e t e d i r=" $ { r e p o r t s . d i r } " /> </ t a r g e t> < 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 "> <mkdir d i r=" $ { b u i l d . c l a s s e s . d i r } " /> < j a v a c s r c d i r=" $ { s r c . d i r } " d e s t d i r=" $ { b u i l d . c l a s s e s . d i r } " /> </ t a r g e t> < t a r g e t name=" c o m p i l e - t e s t s " depends=" 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 l o s t e s t s . "> <mkdir d i r=" $ { t e s t . c l a s s e s . d i r } " /> < j a v a c s r c d i r=" $ { t e s t . d i r } " d e s t d i r=" $ { t e s t . c l a s s e s . d i r } "> < 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 " /> < c l a s s p a t h r e f i d=" j u n i t " /> </ j a v a c> </ t a r g e t> < t a r g e t name=" t e s t " depends=" c o m p i l e - t e s t s " 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 "> <mkdir d i r=" $ { r e p o r t s . d i r } " /> <mkdir d i r=" $ { r e p o r t s . t x t . d i r } " /> <mkdir d i r=" $ { r e p o r t s . x m l . d i r } " /> < j u n i t printsummary=" t r u e " h a l t o n f a i l u r e=" f a l s e " 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 "> < 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 " /> < c l a s s p a t h r e f i d=" j u n i t " /> < f o r m a t t e r t y p e=" p l a i n " /> < t e s t name=" t e s t . A l l T e s t s " t o d i r=" $ { r e p o r t s . t x t . d i r } " /> </ j u n i t > </ t a r g e t> < t a r g e t name=" t e s t . x m l " depends=" c o m p i l e - t e s t s " 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 "> <mkdir d i r=" $ { r e p o r t s . d i r } " /> <mkdir d i r=" $ { r e p o r t s . x m l . d i r } " /> < j u n i t printsummary=" t r u e " h a l t o n f a i l u r e=" f a l s e " 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 "> < 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 " /> < c l a s s p a t h r e f i d=" j u n i t " /> < f o r m a t t e r t y p e=" x m l " /> < 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 } "> < f i l e s e t d i r=" $ { t e s t . c l a s s e s . d i r } "> < i n c l u d e name=" * * / T e s t * . c l a s s " /> </ f i l e s e t > </ b a t c h t e s t> </ j u n i t > </ t a r g e t> < t a r g e t name=" t e s t . r e p o r t s " depends=" t e s t " 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 "> < 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 } "> < f i l e s e t d i r=" $ { r e p o r t s . x m l . d i r } "> < i n c l u d e name=" T E S T - * . x m l " /> </ f i l e s e t > < r e p o r t f o r m a t=" f r a m e s " t o d i r=" $ { r e p o r t s . h t m l . d i r } " /> </ j u n i t r e p o r t > < f a i l i f =" t e s t . f a i l u r e s " 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 . " /> </ t a r g e t> < t a r g e t name=" j a v a d o c "

257
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

depends=" c o m p i l e " 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 . "> < j a v a d o c s o u r c e p a t h=" $ { s r c . d i r } " d e s t d i r=" $ { r e p o r t s . j a v a d o c } " a u t h o r=" t r u e " version=" t r u e " u s e=" t r u e " a c c e s s=" p r i v a t e " 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 " 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 } "> < c l a s s p a t h> < 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 } " /> < 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 } " /> </ c l a s s p a t h> </ j a v a d o c> </ t a r g e t> < t a r g e t name=" p a c k a g e " depends=" c o m p i l e " 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 " > <mkdir d i r=" $ { d i s t . d i r } " /> < 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 } "> < 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 } " /> < m a n i f e s t> <a t t r i b u t e name=" M a i n - C l a s s " v a l u e=" c o n v e r s o r . P r i n c i p a l " /> </ m a n i f e s t> </ j a r > </ t a r g e t>

< t a r g e t name=" e x e c u t e " depends=" p a c k a g e " 153 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 . "> 154 <j a v a 155 j a r=" $ { d i s t . d i r } / $ { d i s t . n a m e } " 156 f o r k=" t r u e " /> 157 </ t a r g e t> 158 </ p r o j e c t >

Listado A.1: Fichero Ant para la construcci on del proyecto de conversi on de temperaturas

258

APENDICE A. BUILD.XML

Ap endice B

Aplicaci on Hipoteca
C odigo fuente de la aplicaci on del c alculo de la cuota mensual de una hipoteca
1 2 3 4 5 6 7 8 9

package g u i . h i p o t e c a . modelo ; import g u i . h i p o t e c a . v i s t a . V i s t a ; public i n t e r f a c e Modelo { public void s e t V i s t a ( V i s t a v i s t a ) ; public void s e t D a t o s ( double c a n t i d a d , i n t tiempo , double i n t e r e s ) ; public double g e t C u o t a ( ) ; }

Listado B.1: interface Modelo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

package g u i . h i p o t e c a . modelo ; import g u i . h i p o t e c a . v i s t a . V i s t a ; public c l a s s ModeloImpl implements Modelo { private V i s t a v i s t a ; private double c a n t i d a d ; private i n t tiempo ; private double i n t e r e s ; private double c u o t a ; public ModeloImpl ( ) { super ( ) ; } @Override public void s e t V i s t a ( V i s t a v i s t a ) { this . v i s t a = v i s t a ; } @Override public synchronized void s e t D a t o s ( double c a n t i d a d , i n t tiempo , double interes ) { this . cantidad = cantidad ; t h i s . tiempo = tiempo ; this . i n t e r e s = i n t e r e s ; calculaCuota () ; vista . cuotaActualizada () ; } private void c a l c u l a C u o t a ( ) { double n = i n t e r e s / 1 2 0 0 ; c u o t a = c a n t i d a d n /(1 (1/Math . pow(1+n , 12 tiempo ) ) ) ;

259

260
33 34 35 36 37 38 39

HIPOTECA APENDICE B. APLICACION

} @Override public synchronized double g e t C u o t a ( ) { return c u o t a ; } }

Listado B.2: Implementaci on del interface Modelo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

package g u i . h i p o t e c a . v i s t a ; import j a v a . awt . C o n t a i n e r ; import g u i . h i p o t e c a . c o n t r o l a d o r . C o n t r o l a d o r ; import g u i . h i p o t e c a . modelo . Modelo ; public i n t e r f a c e V i s t a { public void s e t C o n t r o l a d o r ( C o n t r o l a d o r c o n t r o l a d o r ) ; public void s e t M o d e l o ( Modelo modelo ) ; public C o n t a i n e r g e t C o n t e n e d o r ( ) ; public double g e t C a n t i d a d ( ) ; public i n t getTiempo ( ) ; public double g e t I n t e r e s ( ) ; public void c u o t a A c t u a l i z a d a ( ) ; }

Listado B.3: interface Vista

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

package g u i . h i p o t e c a . v i s t a ; import g u i . h i p o t e c a . c o n t r o l a d o r . C o n t r o l a d o r ; import g u i . h i p o t e c a . modelo . Modelo ; import import import import import import import import import import java java java java java . awt . BorderLayout ; . awt . C o n t a i n e r ; . awt . e v e n t . A c t i o n E v e n t ; . awt . e v e n t . A c t i o n L i s t e n e r ; . lang . r e f l e c t . InvocationTargetException ; . s w i n g . JButton ; . swing . JLabel ; . swing . JPanel ; . swing . JTextField ; . swing . S w i n g U t i l i t i e s ;

javax javax javax javax javax

public c l a s s V i s t a I m p l implements V i s t a { private Modelo modelo ; private C o n t r o l a d o r c o n t r o l a d o r ; // Componentes g r aficos private C o n t a i n e r c o n t e n e d o r ; private J T e x t F i e l d j t f C a n t i d a d ; private J T e x t F i e l d j t f T i e m p o ; private J T e x t F i e l d j t f I n t e r e s ; private J L a b e l j l C u o t a ; public V i s t a I m p l ( ) { super ( ) ; creaGUI ( ) ; } private void creaGUI ( ) { try { S w i n g U t i l i t i e s . invokeAndWait ( new Runnable ( ) { @Override public void run ( ) { c o n t e n e d o r = new C o n t a i n e r ( ) ; c o n t e n e d o r . s e t L a y o u t ( new BorderLayout ( ) ) ; J P a n e l j p D a t o s = new J P a n e l ( ) ;

261
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

j p D a t o s . add ( new J L a b e l ( " C a n t i d a d : " ) ) ; j t f C a n t i d a d = new J T e x t F i e l d ( 8 ) ; j p D a t o s . add ( j t f C a n t i d a d ) ; j p D a t o s . add ( new J L a b e l ( " A ~ nos : " ) ) ; j t f T i e m p o = new J T e x t F i e l d ( 3 ) ; j p D a t o s . add ( j t f T i e m p o ) ; j p D a t o s . add ( new J L a b e l ( " I n t e r e s : " ) ) ; j t f I n t e r e s = new J T e x t F i e l d ( 5 ) ; j p D a t o s . add ( j t f I n t e r e s ) ; JButton j b C a l c u l a = new JButton ( " C a l c u l a " ) ; j b C a l c u l a . 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 ( ) ) ; j p D a t o s . add ( j b C a l c u l a ) ; c o n t e n e d o r . add ( j pD a t o s , BorderLayout .NORTH) ; j l C u o t a = new J L a b e l ( " C u o t a m e n s u a l : " ) ; J P a n e l jpCuota = new J P a n e l ( ) ; jpCuota . add ( j l C u o t a ) ; c o n t e n e d o r . add ( jpCuota ) ; } }) ; } catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } catch ( I n v o c a t i o n T a r g e t E x c e p t i o n e ) { e . printStackTrace () ; } // // // } @Override public C o n t a i n e r g e t C o n t e n e d o r ( ) { return c o n t e n e d o r ; } @Override public void s e t M o d e l o ( Modelo modelo ) { t h i s . modelo = modelo ; } @Override public void s e t C o n t r o l a d o r ( C o n t r o l a d o r c o n t r o l a d o r ) { this . controlador = controlador ; } 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 { @Override 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 ) { controlador . solicitadoCalculo () ; } } @Override public double g e t C a n t i d a d ( ) { return Double . p a r s e D o u b l e ( j t f C a n t i d a d . g e t T e x t ( ) ) ; } @Override public i n t getTiempo ( ) { return I n t e g e r . p a r s e I n t ( j t f T i e m p o . g e t T e x t ( ) ) ; } @Override public double g e t I n t e r e s ( ) { return Double . p a r s e D o u b l e ( j t f I n t e r e s . g e t T e x t ( ) ) ; } @Override public void c u o t a A c t u a l i z a d a ( ) { S t r i n g c u o t a = S t r i n g . f o r m a t ( " C u o t a m e n s u a l : %.2 f " , modelo . g e t C u o t a ( ) ) ; jlCuota . setText ( cuota ) ; } } v e n t a n a . pack ( ) ; 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) ; ventana . s e t V i s i b l e ( t r u e ) ;

262

HIPOTECA APENDICE B. APLICACION

Listado B.4: Implementaci on del interface Vista

1 2 3 4 5 6 7 8 9 10

package g u i . h i p o t e c a . c o n t r o l a d o r ; import g u i . h i p o t e c a . modelo . Modelo ; import g u i . h i p o t e c a . v i s t a . V i s t a ; public i n t e r f a c e C o n t r o l a d o r { public void s e t M o d e l o ( Modelo modelo ) ; public void s e t V i s t a ( V i s t a v i s t a ) ; public void s o l i c i t a d o C a l c u l o ( ) ; }

Listado B.5: interface Controlador

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

package g u i . h i p o t e c a . c o n t r o l a d o r ; import g u i . h i p o t e c a . modelo . Modelo ; import g u i . h i p o t e c a . v i s t a . V i s t a ; public c l a s s C o n t r o l a d o r I m p l implements C o n t r o l a d o r { private Modelo modelo ; private V i s t a v i s t a ; public C o n t r o l a d o r I m p l ( ) { super ( ) ; } @Override public void s e t M o d e l o ( Modelo modelo ) { t h i s . modelo = modelo ; } @Override public void s e t V i s t a ( V i s t a v i s t a ) { this . v i s t a = v i s t a ; } public void s o l i c i t a d o C a l c u l o ( ) { double c a n t i d a d = v i s t a . g e t C a n t i d a d ( ) ; i n t tiempo = v i s t a . getTiempo ( ) ; double i n t e r e s = v i s t a . g e t I n t e r e s ( ) ; modelo . s e t D a t o s ( c a n t i d a d , tiempo , i n t e r e s ) ; } }

Listado B.6: Implementaci on del interface Controlador

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

package g u i . h i p o t e c a ; import import import import import import gui gui gui gui gui gui . . . . . . hipoteca hipoteca hipoteca hipoteca hipoteca hipoteca . controlador . Controlador ; . controlador . ControladorImpl ; . modelo . Modelo ; . modelo . ModeloImpl ; . v i s t a . Vista ; . v i s t a . VistaImpl ;

import j a v a x . s w i n g . JFrame ; public f i n a l c l a s s H i p o t e c a 2 { private H i p o t e c a 2 ( ) { super ( ) ; } private void e j e c u t a ( ) {

263
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

V i s t a v i s t a = new V i s t a I m p l ( ) ; Modelo modelo = new ModeloImpl ( ) ; 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 ( ) ; modelo . s e t V i s t a ( v i s t a ) ; vista . setControlador ( controlador ) ; v i s t a . s e t M o d e l o ( modelo ) ; c o n t r o l a d o r . s e t M o d e l o ( modelo ) ; controlador . setVista ( vista ) ; JFrame v e n t a n a = new JFrame ( " C a l c u l o de la cuota m e n s u a l de una hipoteca " ) ; ventana . setContentPane ( v i s t a . getContenedor ( ) ) ; v e n t a n a . pack ( ) ; 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) ; v e n t a n a . s e t V i s i b l e ( true ) ; } public s t a t i c void main ( S t r i n g [ ] new H i p o t e c a 2 ( ) . e j e c u t a ( ) ; } } args ) {

Listado B.7: Programa principal

264

HIPOTECA APENDICE B. APLICACION

Ap endice C

Ejemplo sincronizaci on
El c odigo del Listado C.1 muestra un ejemplo de uso de los Buer denidos en el Cap tulo 14. Descomenta alguna de las l neas 7 u 8 para ver el resultado, que debe ser el mismo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

package b u f f e r ; import j a v a . u t i l . Random ; public f i n a l c l a s s P r i n c i p a l { // Deja una de l o s B u f f e r d e s c o m e n t a d o s p a r a v e r l a e j e c u c i o n private B u f f e r S i n L o c k < I n t e g e r > c o n t e n e d o r = new B u f f e r S i n L o c k < I n t e g e r > (10) ; // p r i v a t e BufferConLock < I n t e g e r > c o n t e n e d o r = new BufferConLock < I n t e g e r > (10) ; private Random a l e a t o r i o = new Random ( 0 ) ; private P r i n c i p a l ( ) { super ( ) ; } private void e j e c u t a ( f i n a l i n t t P r o d u c t o , f i n a l i n t tConsumidor ) { Thread p r o d u c t o r = new Thread ( new Runnable ( ) { @Override public void run ( ) { try { while ( true ) { contenedor . setDato ( a l e a t o r i o . nextInt (100) ) ; Thread . s l e e p ( t P r o d u c t o ) ; } } catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } }) ; Thread c o n s u m i d o r = new Thread ( new Runnable ( ) { @Override public void run ( ) { try { while ( true ) { contenedor . getDato ( ) ; Thread . s l e e p ( tConsumidor ) ; } } catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; } } }) ; productor . s t a r t () ;

265

266
45 46 47 48 49 50 51

APENDICE C. EJEMPLO SINCRONIZACION

consumidor . s t a r t ( ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { new P r i n c i p a l ( ) . e j e c u t a ( I n t e g e r . p a r s e I n t ( a r g s [ 0 ] ) , I n t e g e r . p a r s e I n t ( args [ 1 ] ) ) ; } }

Listado C.1: C odigo de ejemplo que usa los Buer del cap tulo 14

Bibliograf a
[1] Brian Goetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea. Java Concurrency in Practice. Addison Wesley Professional, 2006. [219, 220] [2] Ken Arnold, James Gosling, and David Holmes. El lenguaje de programaci on Java. Pearson Educaci on, 2001. [49, 73, 92, 115, 131, 220] [3] Bert Bates and Kathy Sierra. Head First Java. OReilly & Associates, 2 edition, 2005. [49, 73, 115, 171] [4] Joshua Bloch. Eective Java. Addison Wesley, 2001.
[73, 220]

[5] Daniel Bola nos Alonso, Almudena Sierra Alonso, and Miren Idoia Alarc on Rodr guez. Pruebas de software y JUnit. Perarson, 2007. [103,
152]

[6] Fran Reyes Perdomo y Gregorio Mena Carlos Bl e Jurado, Juan Gu con TDD. Lulu, 1 edition, 2010. [103] ti errez Plaza. Dise no Agil [7] Ben Collins-Sussman, Brian W. Fitzpatrick, and C. Michael Pilato. Version Control with Subversion. OReilly Media, 2008. [86] [8] John Erich Gamma Richard Helm Ralph Johnson Vlissides. Patrones de dise no. Pearson - Addison Wesley, 2003. [171, 232, 253] [9] Eric Freeman and Elisabeth Freeman. Head rst design patterns. OReilly & Associates, 2004. [232, 253] [10] Robert C. Martin. Clean code. Prentice Hall, 2009.
[49, 104]

[11] Ricardo Pe na Mari. De Euclides a Java. Historia de los algoritmos y de los lenguajes de programaci on. Nivola, 2006. [22] [12] Eliotte Rusty Harold. Java Network Programming. OReilly, 2005.
[229] [86,

[13] John Ferguson Smart. Java Power Tools. OReilly & Associates, 2008.
104, 152, 203, 204]

267

Indice alfab etico


Ant Ejecuci o y limpieza, 135 Ant Compilar un proyecto, 130 Denici on de un objetivo, 129 Denici on de un proyecto, 128 Denici on de una tarea, 129 Ejecuci on de Pruebas Unitarias, 132 Empaquetado de la aplicaci on, 135 Estructurs path-like, 131 Generaci on de la documentaci on, 134 Propiedades, 130 Java Collection Framework, v ease Colecciones Ant, 127 Calendar,clase, 115 Date,clase, 114 GregorianCalendar,clase, 115 Math,clase, 115 Random,clase, 116 Scanner,clase, 103 StringBuffer,clase, 107 StringBuilder,clase, 107 abstract, 50 Acoplamiento, 215 Anotaci on Override, 44 Applet, 157 Ciclo de vida, 158 Etiquetas HTML, 159 Autoboxing, 109 AWT(Abstract Window Toolkit), 138 Bugzilla, 172 Clases abstractas, 50 Clases e interface anidados, 58 Clases gen ericas, 119 Clases recubridoras, 108 Cobertura de las pruebas, 88 Colecciones, 109 Comentarios de documentaci on, 33 Constructor por defecto, 47 Control de Versiones, 61 Detecci on de eventos, 141 EclEmma, 89 enum, 55 Enumeraciones, 55 Escuchadores, v ease Detecci on de eventos Excepciones catch, 74 finally, 74 try, 74 delegar una, 76 inidcar que se lanza una, 78 lanzar una, 78 Extensi on de una clase, 40 File,clase, 96 Finalizaci on, 32 Flujos, 92 a cheros, 96 de bytes, 93 de caracteres, 93 Gen ericos Borrado de tipo, 125 Comodines, 123 L mite superior, 123 Genericos Ampliaci on del tipo, 122 Gestores de aspecto, 139 Herencia, 40 Herencia simple, 40 Hilos, 190 Cerrojos, 197 Sincronizaci on, 197 268

INDICE ALFABETICO Subverion commit, 66 Subversion import, 64 JUnit, 58, 79, 81 Integraci on con Eclipse, 70 After, 84 merge, 67 AfterClass, 85 Repositorio, 62 Bater as de prueba, 86 svnserve, 62 Before, 84 super, 46 BeforeClass, 85 Swing, 138 Parameters, 86 JButton, 146 RunWith, 86 JCheckBox, 150 Test, 82 JLabel, 146 Test Suites, 87 JList, 150 JRadioButton, 148 Layout Managers, v ease Gestores de JTextField, 147 aspecto Componentes, 139 Contenedores, 139 M etodos get y set, 29 M etodos gen ericos, 118 Test unitarios, 73 Mock Objects, 80 Threads, v ease Hilos MyLyn, 165 Tipos de datos, 21 implements, 53 import static, 58 interface, 53 Ocultaci on de atributos, 42 Tipos gen ericos, 117

269

URL, 204 package, 56 Paquetes, 56 Vinculaci on din amica, 40, 46 Patr on de dise no Singleton, 215 Modelo/Vista/Controlador, 152 Patrones Abstract Factory, 219 Decorator, 231 Factory Method, 217 Observer, 229 Strategy, 226 Patrones de dise no, 214 Programaci on Orientada a Objetos, 11 Pruebas unitarias, 79 Principios FIRST, 80 Recolector de basura, 31 Serializaci on, 98 Signatura de un m etodo, 20 Sistema de cheros, 96 Sobrescritura de atributos, 42 Socket, 207 TCP, 207 UDP, 209 Streams, v ease Flujos