12.07.2015 Views

Desarrollo de proyectos informáticos con tecnología Java

Desarrollo de proyectos informáticos con tecnología Java

Desarrollo de proyectos informáticos con tecnología Java

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

2Esta obra <strong>de</strong> Óscar Belmonte et al. está bajo una licencia CreativeCommons Re<strong>con</strong>ocimiento-NoComercial-CompartirIgual 3.0 Unported


Índice general1. Introducción 131.1. Origen <strong>de</strong>l lenguaje <strong>de</strong> programación <strong>Java</strong> . . . . . . . . . . . . . 131.2. Característica <strong>de</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . . . 141.3. El entorno <strong>de</strong> <strong>de</strong>sarrollo integrado Eclipse . . . . . . . . . . . . . 151.3.1. Principales características <strong>de</strong>l entorno <strong>de</strong> <strong>de</strong>sarrollo Eclipse 161.3.2. Descarga e instalación <strong>de</strong> Eclipse . . . . . . . . . . . . . . 161.3.3. Configurando el aspecto <strong>de</strong> Eclipse: Perspectivas y Vistas 161.3.4. El primer ejemplo . . . . . . . . . . . . . . . . . . . . . . 181.4. Herramientas <strong>de</strong> <strong>de</strong>sarrollo . . . . . . . . . . . . . . . . . . . . . . 211.4.1. Añadiendo nueva funcionalidad a Eclipse: los plug-ins . . 222. Clases 232.1. Definición <strong>de</strong> una clase . . . . . . . . . . . . . . . . . . . . . . . . 242.2. Miembros <strong>de</strong> una clase . . . . . . . . . . . . . . . . . . . . . . . . 252.2.1. Atributos <strong>de</strong> una clase . . . . . . . . . . . . . . . . . . . . 252.2.2. Métodos <strong>de</strong> una clase. . . . . . . . . . . . . . . . . . . . . 262.2.3. Constructores. . . . . . . . . . . . . . . . . . . . . . . . . 282.2.4. Sobrecarga <strong>de</strong> métodos y <strong>con</strong>structores . . . . . . . . . . . 322.3. Tipos <strong>de</strong> datos en <strong>Java</strong>. . . . . . . . . . . . . . . . . . . . . . . . 332.3.1. Arrays <strong>de</strong> datos en <strong>Java</strong>. . . . . . . . . . . . . . . . . . . . 342.4. Estructuras <strong>de</strong> <strong>con</strong>trol. . . . . . . . . . . . . . . . . . . . . . . . . 362.4.1. Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> repetición. . . . . . . . . . . . . 372.4.2. Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> selección. . . . . . . . . . . . . . 392.5. Modificadores <strong>de</strong> acceso. . . . . . . . . . . . . . . . . . . . . . . . 402.6. Modificadores static y final. . . . . . . . . . . . . . . . . . . . 422.7. El recolector <strong>de</strong> basura. . . . . . . . . . . . . . . . . . . . . . . . 432.8. Finalización. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442.9. Comentarios. Comentarios <strong>de</strong> documentación. . . . . . . . . . . . 453. Herencia e Interfaces 513.1. Herencia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523.2. Extensión <strong>de</strong> una clase. . . . . . . . . . . . . . . . . . . . . . . . 523.2.1. Sobrescribir atributos. . . . . . . . . . . . . . . . . . . . . 543.2.2. Sobrescribir métodos. . . . . . . . . . . . . . . . . . . . . 563.2.3. La palabra reservada super. . . . . . . . . . . . . . . . . . 593.2.4. El <strong>con</strong>structor por <strong>de</strong>fecto y la clase Object. . . . . . . . 593.2.5. El operador instanceof. . . . . . . . . . . . . . . . . . . 603.2.6. El modificador final. . . . . . . . . . . . . . . . . . . . . 613


4 ÍNDICE GENERAL3.2.7. Métodos static. . . . . . . . . . . . . . . . . . . . . . . . 623.3. Clases abstractas. . . . . . . . . . . . . . . . . . . . . . . . . . . . 633.4. Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653.5. Enumeraciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683.6. Paquetes en <strong>Java</strong>. . . . . . . . . . . . . . . . . . . . . . . . . . . . 693.7. Clases e interface anidados . . . . . . . . . . . . . . . . . . . . 714. Subversion 754.1. ¿Qué es un sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones? . . . . . . . . . . . . 764.2. Principales características <strong>de</strong> Subversion . . . . . . . . . . . . . . 764.3. Creación <strong>de</strong> un repositorio . . . . . . . . . . . . . . . . . . . . . . 774.4. Trabajo <strong>con</strong> repositorios . . . . . . . . . . . . . . . . . . . . . . . 784.4.1. Obteniendo información <strong>de</strong>l repositorio . . . . . . . . . . 824.5. Integración <strong>con</strong> Eclipse . . . . . . . . . . . . . . . . . . . . . . . . 845. Excepciones 875.1. ¿Qué es una excepción? . . . . . . . . . . . . . . . . . . . . . . . 875.1.1. Tipos <strong>de</strong> excepciones . . . . . . . . . . . . . . . . . . . . . 885.2. Cómo se gestiona una excepción . . . . . . . . . . . . . . . . . . 885.3. Creación <strong>de</strong> excepciones propias . . . . . . . . . . . . . . . . . . . 916. Pruebas unitarias <strong>con</strong> JUnit 936.1. ¿Qué son las pruebas unitarias? . . . . . . . . . . . . . . . . . . . 946.1.1. Principios FIRST para el diseño <strong>de</strong> pruebas unitarias . . 946.2. Pruebas unitarias <strong>con</strong> JUnit . . . . . . . . . . . . . . . . . . . . . 956.2.1. Creación <strong>de</strong> clases <strong>de</strong> prueba . . . . . . . . . . . . . . . . 956.2.2. La anotación @Test . . . . . . . . . . . . . . . . . . . . . 966.2.3. Las anotaciones @Before y @After . . . . . . . . . . . . . 986.2.4. Las anotaciones @BeforeClass y @AfterClass . . . . . . 996.2.5. Pruebas <strong>con</strong> batería <strong>de</strong> datos <strong>de</strong> entrada . . . . . . . . . . 1006.2.6. Ejecutar varias clases <strong>de</strong> prueba. Test Suites . . . . . . . 1016.3. Cobertura <strong>de</strong> las pruebas . . . . . . . . . . . . . . . . . . . . . . 1026.3.1. EclEmma y su plug-in para Eclipse . . . . . . . . . . . . . 1037. Entrada y Salida 1057.1. Flujos (Streams) . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067.2. Flujos <strong>de</strong> bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1077.3. Flujos <strong>de</strong> caracteres . . . . . . . . . . . . . . . . . . . . . . . . . 1087.4. Conexión entre flujos <strong>de</strong> bytes y <strong>de</strong> caracteres . . . . . . . . . . . 1097.5. El sistema <strong>de</strong> ficheros y flujos a ficheros . . . . . . . . . . . . . . 1107.5.1. El sistema <strong>de</strong> ficheros . . . . . . . . . . . . . . . . . . . . 1107.5.2. Flujos a ficheros . . . . . . . . . . . . . . . . . . . . . . . 1107.6. Serialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1128. Algunas clases <strong>de</strong> utilidad <strong>de</strong>l paquete estándar 1178.1. La clase Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . 1188.2. Trabajo <strong>con</strong> ca<strong>de</strong>nas <strong>de</strong> caracteres . . . . . . . . . . . . . . . . . 1208.2.1. La clase String . . . . . . . . . . . . . . . . . . . . . . . 1208.2.2. Las clases StringBuffer y StringBuil<strong>de</strong>r . . . . . . . . 1218.3. Clases recubridoras . . . . . . . . . . . . . . . . . . . . . . . . . . 122


ÍNDICE GENERAL 58.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1248.5. Trabajo <strong>con</strong> fechas . . . . . . . . . . . . . . . . . . . . . . . . . . 1288.5.1. La clase Date . . . . . . . . . . . . . . . . . . . . . . . . . 1288.5.2. Las clases Calendar y GregorianCalendar . . . . . . . . 1298.6. Matemáticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1298.6.1. La clase Math . . . . . . . . . . . . . . . . . . . . . . . . . 1298.6.2. La clase Random . . . . . . . . . . . . . . . . . . . . . . . 1309. Programación <strong>con</strong> genéricos 1339.1. ¿Qué son los tipos <strong>de</strong> datos genéricos? . . . . . . . . . . . . . . . 1339.2. Métodos genéricos . . . . . . . . . . . . . . . . . . . . . . . . . . 1349.3. Clases genéricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1359.4. Ampliación <strong>de</strong>l tipo genérico . . . . . . . . . . . . . . . . . . . . 1389.4.1. Tipos genéricos <strong>con</strong> límite superior . . . . . . . . . . . . . 1399.4.2. Comodines . . . . . . . . . . . . . . . . . . . . . . . . . . 1399.5. Borrado <strong>de</strong> tipo y compatibilidad <strong>con</strong> código heredado . . . . . . 14110.Construcción <strong>de</strong> <strong>proyectos</strong> <strong>con</strong> Ant 14310.1. Qué es Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14410.2. Definición <strong>de</strong>l proyecto . . . . . . . . . . . . . . . . . . . . . . . . 14410.2.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . 14510.2.2. Tareas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14510.3. Compilar el código fuente <strong>de</strong> un proyecto . . . . . . . . . . . . . 14610.4. Propieda<strong>de</strong>s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14610.5. Estructuras path-like . . . . . . . . . . . . . . . . . . . . . . . . 14710.6. Ejecución <strong>de</strong> las Pruebas Unitarias . . . . . . . . . . . . . . . . . 14810.7. Generación <strong>de</strong> la documentación . . . . . . . . . . . . . . . . . . 15010.8. Empaquetado <strong>de</strong> la aplicación . . . . . . . . . . . . . . . . . . . . 15110.9. Ejecución y limpieza . . . . . . . . . . . . . . . . . . . . . . . . . 15111.Interfaces gráficas <strong>de</strong> usuario 15311.1. APIs para la programación <strong>de</strong> interfaces gráficos <strong>de</strong> usuario en<strong>Java</strong>: AWT y Swing . . . . . . . . . . . . . . . . . . . . . . . . . 15411.2. Contenedores y Componentes . . . . . . . . . . . . . . . . . . . . 15511.3. Gestores <strong>de</strong> Aspecto (Layout Managers) . . . . . . . . . . . . . . 15511.4. Detección <strong>de</strong> eventos: Escuchadores . . . . . . . . . . . . . . . . . 15711.5. Algunos componentes Swing . . . . . . . . . . . . . . . . . . . . . 16211.5.1. JLabel, muestra texto o i<strong>con</strong>os . . . . . . . . . . . . . . . 16211.5.2. JButton, botones que el usuario pue<strong>de</strong> pulsar . . . . . . . 16211.5.3. JTextField, campos <strong>de</strong> introducción <strong>de</strong> texto . . . . . . . 16311.5.4. JRadioButton, botones <strong>de</strong> opciones . . . . . . . . . . . . 16411.5.5. JCheckBox, botones <strong>de</strong> selección múltiple . . . . . . . . . 16611.5.6. JList, listas <strong>de</strong> selección . . . . . . . . . . . . . . . . . . 16611.6. El patrón <strong>de</strong> diseño Mo<strong>de</strong>lo/Vista/Controlador . . . . . . . . . . 16812.Applets 17312.1. ¿Qué son los Applets? . . . . . . . . . . . . . . . . . . . . . . . . 17312.2. Ciclo <strong>de</strong> vida <strong>de</strong> un Applet . . . . . . . . . . . . . . . . . . . . . 17412.3. Código HTML para <strong>con</strong>tener un Applet . . . . . . . . . . . . . . 17512.4. Lectura <strong>de</strong> parámetros <strong>de</strong> la página HTML . . . . . . . . . . . . 176


6 ÍNDICE GENERAL12.5. Convertir una aplicación Swing en un Applet . . . . . . . . . . . 17612.6. Comunicación entre Applets . . . . . . . . . . . . . . . . . . . . . 17713.Control <strong>de</strong> errores <strong>con</strong> MyLyn y Bugzilla 18113.1. Sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> tareas MyLyn . . . . . . . . . . . . . . . . 18213.1.1. Cual es el objetivo <strong>de</strong> MyLyn . . . . . . . . . . . . . . . . 18213.1.2. Trabajar <strong>con</strong> MyLyn . . . . . . . . . . . . . . . . . . . . . 18213.2. Sistema <strong>de</strong> gestión <strong>de</strong> errores Bugzilla . . . . . . . . . . . . . . . 18813.2.1. Cual es el objetivo <strong>de</strong> Bugzilla . . . . . . . . . . . . . . . 18813.2.2. Instalación <strong>de</strong> Bugzilla . . . . . . . . . . . . . . . . . . . . 18813.2.3. Trabajar <strong>con</strong> Bugzilla . . . . . . . . . . . . . . . . . . . . 19513.3. Acceso a Bugzilla <strong>de</strong>s<strong>de</strong> MyLyn y Eclipse . . . . . . . . . . . . . 19913.3.1. Beneficios <strong>de</strong> la combinación <strong>de</strong> Bugzilla y MyLyn <strong>de</strong>s<strong>de</strong>Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20113.3.2. Trabajo <strong>con</strong> MyLyn y Bugzilla <strong>de</strong>s<strong>de</strong> Eclipse . . . . . . . 20114.Programación <strong>con</strong>currente <strong>con</strong> Hilos 20714.1. ¿Qué es un hilo? Utilida<strong>de</strong>s. Consi<strong>de</strong>raciones sobre el uso <strong>de</strong> hilos 20814.2. Creación <strong>de</strong> hilos en <strong>Java</strong> . . . . . . . . . . . . . . . . . . . . . . 20914.2.1. Creación <strong>de</strong> un Hilo extendiendo a la clase Thread . . . . 20914.2.2. Creación <strong>de</strong> un Hilo mediante una clase interna . . . . . . 21014.2.3. Creación <strong>de</strong> un Hilo mediante una clase interna anónima . 21114.3. Ciclo <strong>de</strong> vida <strong>de</strong> un hilo . . . . . . . . . . . . . . . . . . . . . . . 21214.4. Control <strong>de</strong> hilos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21314.5. Sincronización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21514.5.1. Sincronizacón utilizando los cerrojos intrínsecos . . . . . . 21514.5.2. Sincronización utilizando el interface Lock . . . . . . . 21815.Programación para la Red 22115.1. Trabajo <strong>con</strong> URLs . . . . . . . . . . . . . . . . . . . . . . . . . . 22215.1.1. ¿Qué es una URL? . . . . . . . . . . . . . . . . . . . . . . 22215.1.2. Leer <strong>de</strong>s<strong>de</strong> una URL . . . . . . . . . . . . . . . . . . . . . 22315.1.3. Escribir a una URL . . . . . . . . . . . . . . . . . . . . . 22315.2. Trabajo <strong>con</strong> Sockets . . . . . . . . . . . . . . . . . . . . . . . . . 22515.2.1. ¿Qué es un Socket? . . . . . . . . . . . . . . . . . . . . . . 22515.2.2. Sockets bajo el protocolo TCP . . . . . . . . . . . . . . . 22515.2.3. Sockets bajo el protocolo UDP . . . . . . . . . . . . . . . 22716.Patrones <strong>de</strong> diseño 23116.1. Principios <strong>de</strong> POO . . . . . . . . . . . . . . . . . . . . . . . . . . 23216.2. ¿Qué son los patrones <strong>de</strong> diseño? . . . . . . . . . . . . . . . . . . 23316.3. ¿Qué es el acoplamiento entre clases y por qué hay que evitarlo? 23316.4. Grupos <strong>de</strong> patrones <strong>de</strong> diseño . . . . . . . . . . . . . . . . . . . . 23316.5. El patrón <strong>de</strong> diseño Singleton . . . . . . . . . . . . . . . . . . . . 23316.5.1. Situación que intenta resolver . . . . . . . . . . . . . . . . 23416.5.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . . . . 23416.6. El patrón <strong>de</strong> diseño Factory Method . . . . . . . . . . . . . . . . 23516.6.1. Situación que intenta resolver . . . . . . . . . . . . . . . . 23516.6.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . . . . 23616.7. El patrón <strong>de</strong> diseño Abstract Factory . . . . . . . . . . . . . . . . 238


ÍNDICE GENERAL 716.7.1. Situación que intenta resolver . . . . . . . . . . . . . . . . 23816.7.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . . . . 23816.8. El patrón <strong>de</strong> diseño Strategy . . . . . . . . . . . . . . . . . . . . . 24416.8.1. Situación que intenta resolver . . . . . . . . . . . . . . . . 24516.8.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . . . . 24516.9. El patrón <strong>de</strong> diseño Observer . . . . . . . . . . . . . . . . . . . . 24716.9.1. Situación que intenta resolver . . . . . . . . . . . . . . . . 24716.9.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . . . . 24816.10.El patrón <strong>de</strong> diseño Decorator . . . . . . . . . . . . . . . . . . . . 24916.10.1.Situación que intenta resolver . . . . . . . . . . . . . . . . 25016.10.2.Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . . . . 250A. build.xml 255B. Aplicación Hipoteca 259C. Ejemplo sincronización 265


8 ÍNDICE GENERAL


PrefacioLa escritura <strong>de</strong> un libro es una tarea ingente. La motivación para abordarla <strong>de</strong>beser, al menos, tan gran<strong>de</strong> como la tarea que se <strong>de</strong>sea acometer. Para nosotros,la motivación ha <strong>con</strong>sistido en escribir un libro que se distinguiera <strong>de</strong>l resto <strong>de</strong>libros que abordan el aprendizaje <strong>de</strong>l lenguaje <strong>de</strong> programación <strong>Java</strong>.Por un lado, existen excelentes libros que muestran cómo programar en <strong>Java</strong>.Por otro lado existen excelentes libros, en número inferior, que muestran cómoutilizar herramientas <strong>de</strong> ayuda y soporte al <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> en <strong>Java</strong>.Pensamos que, entre ellos, existía cabida para escribir un libro que abordase elaprendizaje <strong>de</strong> <strong>Java</strong> al mismo tiempo que las herramientas imprescindibles <strong>de</strong>ayuda al <strong>de</strong>sarrollo.Dentro <strong>de</strong> nuestra Universidad, la Jaume I, hemos impartido, y seguimoshaciéndolo, cursos sobre el lenguaje <strong>de</strong> programación <strong>Java</strong> para todo tipo <strong>de</strong>alumnado: <strong>de</strong>s<strong>de</strong> alumnos <strong>de</strong> las distintas titulaciones <strong>de</strong> informática, alumnosextranjeros en el Master Europeo Erasmus Mundus sobre tecnologías Geoespaciales,hasta profesionales que quieren mantener al día su <strong>con</strong>ocimiento ymejorar sus expectativas laborales. Esta experiencia nos ha dado la <strong>con</strong>fianzasuficiente como para animarnos a escribir el presente libro.Y, a pesar <strong>de</strong>l <strong>con</strong>tacto casi diario <strong>con</strong> <strong>Java</strong> y sus tecnologías, re<strong>con</strong>ocemosque aún nos queda mucho por apren<strong>de</strong>r, que el mundo que brinda el aprendizaje<strong>de</strong> <strong>Java</strong> es inmenso y que se renueva <strong>con</strong>stantemente. Esto último es síntoma<strong>de</strong> que la comunidad alre<strong>de</strong>dor <strong>de</strong> esta tecnología está viva y posee un granentusiasmo.Objetivos <strong>de</strong>l libroDos son los objetivos principales <strong>de</strong>l este libro:Presentar el lenguaje <strong>de</strong> programación <strong>Java</strong>.Presentar algunas <strong>de</strong> las herramientas <strong>de</strong> <strong>de</strong>sarrollo que ayudan en el <strong>de</strong>sarrollo<strong>de</strong> <strong>proyectos</strong> utilizando <strong>Java</strong>.Con un poco más <strong>de</strong> <strong>de</strong>talle, en el primer objetivo hemos pretendido no sólopresentar el lenguaje <strong>de</strong> programación, a<strong>de</strong>más indicamos unas directrices paracrear código <strong>de</strong> calidad, código que sea fácil leer, fácil mantener y que se pue<strong>de</strong>probar <strong>de</strong> manera automática.El segundo <strong>de</strong> los objetivos es casi una necesidad imperiosa a los equipos <strong>de</strong><strong>de</strong>sarrollo que siguen utilizando como herramienta <strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones undirectorio compartido. O a aquellos equipos <strong>de</strong> <strong>de</strong>sarrollo que siguen probando9


10 ÍNDICE GENERALsus aplicaciones <strong>de</strong> manera manual. O para aquellos equipos <strong>de</strong> <strong>de</strong>sarrollo queutilizan como sistema <strong>de</strong> seguimiento <strong>de</strong> errores el correo electrónico. Y un largoetcétera <strong>de</strong> prácticas <strong>de</strong>sa<strong>con</strong>sejadas.Cómo está organizado este libroLa Figura 1 muestra la organización en capítulos <strong>de</strong>l presente libro. Cada uno<strong>de</strong> los recuadros representa un capítulo. Los capítulos se han agrupado en dosgran<strong>de</strong>s bloques. En el primero <strong>de</strong> ellos <strong>Java</strong> básico hemos agrupado los capítulosque <strong>con</strong>si<strong>de</strong>ramos introductorios, y que representan el núcleo <strong>de</strong> la programaciónorientada a objetos en <strong>Java</strong>. En el segundo grupo <strong>Java</strong> avanzado aparecen loscapítulos que <strong>con</strong>si<strong>de</strong>ramos aspectos avanzados <strong>de</strong>l lenguaje <strong>con</strong> respecto a loscapítulos <strong>de</strong>l primer grupo.En ambos grupos hay capítulos que no aparecen en la línea principal <strong>de</strong>lflujo, estos capítulos son los que presentan herramientas que <strong>con</strong>si<strong>de</strong>ramos <strong>de</strong>gran utilidad en el <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> informáticos utilizando tecnologías<strong>Java</strong>. El or<strong>de</strong>n <strong>de</strong> introducción <strong>de</strong> estas herramientas ha sido fuente <strong>de</strong> largas<strong>con</strong>versaciones: ¿Es <strong>con</strong>veniente introducir al principio la herramienta JUnitsiguiendo una orientación hacia el <strong>de</strong>sarrollo guiado por pruebas? ¿Debemos<strong>de</strong>legar hasta el segundo bloque <strong>de</strong> capítulos el <strong>de</strong>dicado a la <strong>con</strong>strucción <strong>de</strong><strong>proyectos</strong> <strong>con</strong> Ant? Hemos optado por seguir un or<strong>de</strong>n quizás más <strong>con</strong>servado ymenos arriesgado, intentando presentar las herramientas en el momento en que<strong>con</strong>ceptualmente se entienda cual es la necesidad que vienen a cubrir. Esperamosque esta or<strong>de</strong>nación haga el tránsito suave entre el aprendizaje <strong>de</strong> <strong>Java</strong> comolenguaje <strong>de</strong> programación y las herramientas <strong>de</strong> ayuda al <strong>de</strong>sarrollo.Quien <strong>de</strong>bería leer este libroEl publico objetivo <strong>de</strong> este libro son los <strong>de</strong>sarrolladores que quieran apren<strong>de</strong>rel lenguaje <strong>de</strong> programación <strong>Java</strong> y ya posean <strong>con</strong>ocimientos <strong>de</strong> programaciónestructurada y orientación a objetos. Los <strong>con</strong>ceptos <strong>de</strong>l lenguaje son presentados<strong>de</strong>s<strong>de</strong> la base, suponiendo que es la primera vez que el lector se aproxima allenguaje <strong>de</strong> programación <strong>Java</strong>.Pero este libro también está pensado para aquellas personas que <strong>con</strong>ocen ellenguaje <strong>de</strong> programación <strong>Java</strong> y aún no han <strong>de</strong>scubierto la gran cantidad <strong>de</strong>herramientas <strong>de</strong> ayuda que existen en el <strong>de</strong>sarrollo <strong>de</strong> proyecto.Agra<strong>de</strong>cimientosLa sección <strong>de</strong> agra<strong>de</strong>cimientos es posiblemente una <strong>de</strong> las más complicadas <strong>de</strong>escribir. Debe tener un equilibrio entre el espacio <strong>de</strong>dicado a ella y el re<strong>con</strong>ocimientoa todas las personas, que <strong>de</strong> un modo u otro han <strong>con</strong>tribuido a que unlibro tenga su forma final.Para no <strong>de</strong>jarnos por citar el nombre <strong>de</strong> nadie, preferimos ampliar nuestroagra<strong>de</strong>cimiento a colectivos. En primer lugar a nuestro alumnos, por que a fin<strong>de</strong> cuentas es a ellos a los que va dirigido este libro. Con sus preguntas, apreciaciones,comentarios y dudas nos han ayudado a darnos cuenta <strong>de</strong> don<strong>de</strong> estabanlos escollos en la lectura <strong>de</strong> este libro.


ÍNDICE GENERAL 11Figura 1: Organización <strong>de</strong>l libro.


12 ÍNDICE GENERALTambién a nuestros compañeros <strong>de</strong> la Universidad, porque <strong>con</strong> sus comentariosy rectificaciones nos han ayudado a eliminar errores en los <strong>con</strong>tenidos.Y finalmente a nuestros amigos por su ánimo <strong>con</strong>stante para que esta laborllegase a buen puerto.A todos ellos gracias.


Capítulo 1IntroducciónContenidos1.1. Origen <strong>de</strong>l lenguaje <strong>de</strong> programación <strong>Java</strong> . . . . 131.2. Característica <strong>de</strong> <strong>Java</strong> . . . . . . . . . . . . . . . . . 141.3. El entorno <strong>de</strong> <strong>de</strong>sarrollo integrado Eclipse . . . . 151.3.1. Principales características <strong>de</strong>l entorno <strong>de</strong> <strong>de</strong>sarrolloEclipse . . . . . . . . . . . . . . . . . . . . . . . . . . 161.3.2. Descarga e instalación <strong>de</strong> Eclipse . . . . . . . . . . . 161.3.3. Configurando el aspecto <strong>de</strong> Eclipse: Perspectivas yVistas . . . . . . . . . . . . . . . . . . . . . . . . . . 161.3.4. El primer ejemplo . . . . . . . . . . . . . . . . . . . 181.4. Herramientas <strong>de</strong> <strong>de</strong>sarrollo . . . . . . . . . . . . . 211.4.1. Añadiendo nueva funcionalidad a Eclipse: los plug-ins 22IntroducciónEn este capítulo <strong>de</strong> introducción se muestran los orígenes <strong>de</strong>l lenguaje <strong>de</strong> programación<strong>Java</strong>, sus principales características y las diferencias <strong>con</strong> respecto aC++, lenguaje <strong>de</strong>l que hereda gran parte <strong>de</strong> la sintaxis.En la segunda parte <strong>de</strong>l capítulo se presenta en Entorno <strong>de</strong> <strong>Desarrollo</strong> IntegradoEclipse, los fundamentos para crear <strong>proyectos</strong> <strong>Java</strong> utilizando este Entornoy cómo se pue<strong>de</strong> enriquecer su funcionalidad mediante la instalación <strong>de</strong> plug-ins.1.1. Origen <strong>de</strong>l lenguaje <strong>de</strong> programación <strong>Java</strong>El lenguaje <strong>de</strong> programación <strong>Java</strong> tiene sus orígenes en un lenguaje <strong>de</strong> programaciónanterior, llamado Oak (roble en inglés), que nació <strong>de</strong> un proyecto internoen Sun Microsystems en el año 1991 llamado Green project.Oak fue creado <strong>con</strong> el objetivo <strong>de</strong> ser el lenguaje <strong>de</strong> programación <strong>con</strong> elque programar dispositivos electrónicos domésticos, en particular aparatos <strong>de</strong>televisión inteligentes e interactivos. Oak tenía, entre otras, las siguientes características<strong>de</strong> interés:13


14CAPÍTULO 1. INTRODUCCIÓNOrientado a objetos y <strong>de</strong> propósito general.Robusto.Sintaxis parecida a C++.In<strong>de</strong>pendiente <strong>de</strong>l hardware.El proyecto <strong>de</strong> televisión inteligente e interactiva nunca se materializó. Demodo simultáneo, a principios <strong>de</strong> la década <strong>de</strong> los 90 surgió Internet y <strong>con</strong>ella, la aparición <strong>de</strong> los primeros navegadores web. Los lí<strong>de</strong>res <strong>de</strong>l Green projectfueron <strong>con</strong>scientes <strong>de</strong> la importancia que iba a tener Internet y orientaron sulenguaje <strong>de</strong> programación Oak para que programas escritos en este lenguaje <strong>de</strong>programación se pudiesen ejecutar <strong>de</strong>ntro <strong>de</strong>l navegador web Mozilla. Y este fueel inicio <strong>de</strong> <strong>Java</strong>, así llamado porque cuando se intentó registrar el nombre Oakeste ya estaba registrado.Las nuevas características generales que se añadieron son:Seguridad, ya que los programas que se ejecutan en un navegador se <strong>de</strong>scargan<strong>de</strong>s<strong>de</strong> Internet.Potencia, ya no se tenía la restricción <strong>de</strong> la ejecución en dispositivos <strong>de</strong>electrónica <strong>de</strong> <strong>con</strong>sumo.<strong>Java</strong> se ha <strong>con</strong>solidado como lenguaje <strong>de</strong> programación gracias a que sucurva <strong>de</strong> aprendizaje es relativamente suave para programadores provenientes<strong>de</strong> C++. A<strong>de</strong>más, la ventaja <strong>de</strong> que un programa escrito en <strong>Java</strong> se pue<strong>de</strong>ejecutar en una gran cantidad <strong>de</strong> plataformas ha hecho <strong>de</strong> él un interesantelenguaje <strong>de</strong> programación por su ✭✭universalidad✮✮.1.2. Característica <strong>de</strong> <strong>Java</strong><strong>Java</strong> es un lenguaje <strong>de</strong> programación orientado a objetos y <strong>de</strong> propósito generalque toma <strong>de</strong> otros lenguajes <strong>de</strong> programación algunas i<strong>de</strong>as fundamentales,en particular toma <strong>de</strong> Smalltalk el hecho <strong>de</strong> que los programas <strong>Java</strong> se ejecutansobre una máquina virtual. Y <strong>de</strong>l lenguaje <strong>de</strong> programación C++ toma susintaxis.El uso <strong>de</strong> la máquina virtual garantiza la in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> la plataforma en<strong>Java</strong>. Si disponemos <strong>de</strong> una máquina virtual para nuestra plataforma, podremosejecutar el mismo programa escrito en <strong>Java</strong> sin necesidad <strong>de</strong> volverlo a compilar.En el proceso <strong>de</strong> compilación <strong>de</strong> un programa en <strong>Java</strong>, se genera un códigointermedio, llamado byteco<strong>de</strong>, que la máquina virtual interpreta y traduce allamadas nativas <strong>de</strong>l sistema sobre el que se ejecuta la máquina virtual. Así,una máquina virtual para una plataforma Windows 7 <strong>de</strong> 64 bits, traducirá losbyteco<strong>de</strong>s a código nativo para esta plataforma, y otra máquina virtual para unaplataforma Linux <strong>de</strong> 64 bits traducirá los mismos byteco<strong>de</strong>s a código nativo paraesta otra plataforma. Los byteco<strong>de</strong>s son los mismos en ambos casos, las máquinasvirtuales sobre las que se ejecutan son nativas <strong>de</strong> la plataforma correspondiente.Pue<strong>de</strong> parecer que este paso <strong>de</strong> traducir los byteco<strong>de</strong>s a código nativo <strong>de</strong> laplataforma suponga una pérdida <strong>de</strong> rendimiento en la ejecución <strong>de</strong> los programasen <strong>Java</strong>, pero esto no es así gracias a la introducción <strong>de</strong> la tecnología JIT (Just


1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE 15In Time compilation). La i<strong>de</strong>a básica <strong>de</strong> esta tecnología es que la primera vezque se llama a un método, este se interpreta generando código nativo <strong>de</strong> laplataforma sobre la que se ejecuta la máquina virtual, pero una vez generadoeste código nativo, se almacena, <strong>de</strong> tal modo que la siguiente vez que se llamaal mismo método no es necesaria su interpretación ya que el código nativo paraese método se almacenó previamente.Otras características generales <strong>de</strong> <strong>Java</strong> son:Seguridad <strong>de</strong>s<strong>de</strong> el punto <strong>de</strong> vista <strong>de</strong>l programador:• Comprobación estricta <strong>de</strong> tipos.• Gestión <strong>de</strong> excepciones.• No existen punteros.• Recolector <strong>de</strong> basura.Seguridad <strong>de</strong>s<strong>de</strong> el punto <strong>de</strong> vista <strong>de</strong>l usuario <strong>de</strong> aplicaciones:• Los programas se ejecutan sobre una máquina virtual.• Espacio <strong>de</strong> nombre.Soporta programación <strong>con</strong>currente <strong>de</strong> modo nativo.Los tipos <strong>de</strong> datos están estandarizados.Sólo se admite herencia simple.1.3. El entorno <strong>de</strong> <strong>de</strong>sarrollo integrado EclipseUn entorno integrado <strong>de</strong> <strong>de</strong>sarrollo o IDE <strong>de</strong> sus siglas en inglés (emphIntegratedDevelop Environment) nos permite escribir código <strong>de</strong> un modo cómodo. Lacomodidad resi<strong>de</strong> en que los entornos <strong>de</strong> <strong>de</strong>sarrollo integrados son mucho másque un simple editor <strong>de</strong> textos. Algunas características comunes a los IDE son:Coloreado <strong>de</strong> la sintaxis.Herramientas <strong>de</strong> búsqueda.Asistentes para la escritura <strong>de</strong> código.Ejecución <strong>de</strong> aplicaciones sin abandonar el entorno.Herramientas <strong>de</strong> <strong>de</strong>puración <strong>de</strong> código.Junto a estas características, los mo<strong>de</strong>rnos IDE poseen algunas otras realmenteespectaculares como por ejemplo:Conexión <strong>con</strong> sistemas <strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones.Conexión <strong>con</strong> sistema <strong>de</strong> seguimiento <strong>de</strong> errores.Facilida<strong>de</strong>s para la creación <strong>de</strong> tareas.Herramientas avanzadas para el análisis <strong>de</strong> código.


16CAPÍTULO 1. INTRODUCCIÓNHerramientas <strong>de</strong> análisis <strong>de</strong> rendimiento.Conexión a gestores <strong>de</strong> bases <strong>de</strong> datos.Eclipse es un IDE orientado al <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> <strong>con</strong> tecnología <strong>Java</strong>,aunque no es el único lenguaje <strong>de</strong> programación al que da soporte. Eclipse esuna herramienta <strong>de</strong> software libre, mantenido por la Eclipse Foundation.1.3.1. Principales características <strong>de</strong>l entorno <strong>de</strong> <strong>de</strong>sarrolloEclipseEclipse reúne todas las características comunes a los mo<strong>de</strong>rnos IDE enumeradasmás arriba. A<strong>de</strong>más posee un sistema <strong>de</strong> plug-ins <strong>con</strong> los que se pue<strong>de</strong>n añadirnuevas funcionalida<strong>de</strong>s. Por ejemplo, mediante un plug-in nos po<strong>de</strong>mos <strong>con</strong>ectaral sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones Subversion1.3.2. Descarga e instalación <strong>de</strong> EclipseEclipse se pue<strong>de</strong> <strong>de</strong>scargar <strong>de</strong>s<strong>de</strong> el sitio web http://www.eclipse.org. Existenversiones para las principales plataformas y sistemas operativos.Una particularidad <strong>de</strong> Eclipse es que no necesita instalación. Una vez <strong>de</strong>scargadoel fichero comprimido, lo único que <strong>de</strong>bemos hacer para empezar autilizarlo es <strong>de</strong>scomprimirlo en algún directorio y seguidamente ejecutar el binariocorrespondiente a nuestra plataforma. La Figura 1.1 muestra la página<strong>de</strong> inicio <strong>de</strong> Eclipse. Los i<strong>con</strong>os que muestra esta pantalla son enlaces a sitios<strong>de</strong> información sobre Eclipse. La pantalla <strong>de</strong> inicio se pue<strong>de</strong> cerrar pulsando elaspa que se encuentra a la <strong>de</strong>recha <strong>de</strong> la leyenda Welcome.1.3.3. Configurando el aspecto <strong>de</strong> Eclipse: Perspectivas yVistasEl interface gráfico <strong>de</strong> usuario <strong>de</strong> Eclipse cuenta <strong>con</strong> dos <strong>con</strong>ceptos fundamentales:las Perspectivas y las Vistas.Una Perspectiva es un <strong>con</strong>tenedor <strong>de</strong> Vistas. En una misma Perspectivapo<strong>de</strong>mos agrupar más <strong>de</strong> una Vista.Las Vistas por su lado, son componentes visual que pue<strong>de</strong>n mostrar <strong>de</strong>s<strong>de</strong> uneditor <strong>de</strong> código, hasta un árbol <strong>de</strong> jerarquía <strong>de</strong> clases en <strong>Java</strong>. En la figura 1.2se muestra el aspecto <strong>de</strong> Eclipse al mostrar la Perspectiva por <strong>de</strong>fecto orientadaal <strong>de</strong>sarrollo <strong>de</strong> aplicaciones <strong>Java</strong>.Esta perspectiva <strong>con</strong>tiene varias vistas, por ejemplo la vista Package Explorerdon<strong>de</strong> se muestra información <strong>de</strong> la <strong>con</strong>figuración <strong>de</strong> nuestros <strong>proyectos</strong>. La vistaProblems muestra un listado <strong>con</strong> los errores y avisos presentes en el código <strong>de</strong>nuestro proyecto. Cada una <strong>de</strong> estas vistas está orientada a presentar un tipo<strong>de</strong> información <strong>de</strong> nuestro proyecto o tareas relacionadas <strong>con</strong> él.El aspecto <strong>de</strong> las perspectivas se pue<strong>de</strong> <strong>con</strong>figurar. Te habrás dado cuentaque cada una <strong>de</strong> estas vistas está etiquetada <strong>con</strong> un nombre <strong>de</strong>ntro <strong>de</strong> unasolapa, si haces click sobre una <strong>de</strong> estas solapas sin soltar el botón <strong>de</strong>l ratón,pue<strong>de</strong>s trasladar la vista a cualquier otra posición <strong>de</strong>ntro <strong>de</strong> la perspectiva. Estote permite organizar las vistas <strong>de</strong>ntro <strong>de</strong> las perspectivas según tus preferencias.Existe una gran cantidad <strong>de</strong> vistas, pue<strong>de</strong>s acce<strong>de</strong>r a cualquiera <strong>de</strong> ellas através <strong>de</strong> la opción Show view <strong>de</strong>l menú Window.


1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE 17Figura 1.1: Pantalla inicial <strong>de</strong> EclipseFigura 1.2: Perspectiva inicial orientada al <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> <strong>Java</strong>2 SE.


18CAPÍTULO 1. INTRODUCCIÓNFigura 1.3: Ventana para la creación <strong>de</strong> un proyecto <strong>Java</strong> utilizando Eclipse.Figura 1.4: Estructura mínima <strong>de</strong> un proyecto en Eclipse.1.3.4. El primer ejemploVamos a crear un primer proyecto <strong>Java</strong> <strong>con</strong> Eclipse. Para ello, simplemente hazclick <strong>con</strong> el botón <strong>de</strong>recho en la vista Package Explorer, y sobre el menú emergenteque aparecerá selecciona New → Project, se abrirá una ventana como lamostrada en la Figura 1.3. En esta ventana lo único que vamos a introducir esel nombre <strong>de</strong>l proyecto.Una vez introducido el nombre <strong>de</strong>l proyecto, pulsa el botón Finish, verásque el aspecto <strong>de</strong> Eclipse se actualiza para mostrar el nuevo proyecto reciéncreado. En la vista Package Explorer aparecerá el nombre <strong>de</strong>l nuevo proyectorecién creado. La vista <strong>de</strong> proyecto sigue una estructura <strong>de</strong> árbol, que pue<strong>de</strong>s<strong>de</strong>splegar, el resultado se muestra en la Figura 1.4El siguiente paso que vamos a dar es crear una nueva clase en nuestro proyecto.Esta clase va a ser muy sencilla, y únicamente nos va a servir para <strong>con</strong>ocercual es el procedimiento <strong>de</strong> creación, edición, compilación y ejecución utilizandoEclipse. Para crear una nueva clase, haz click <strong>con</strong> el botón <strong>de</strong>recho <strong>de</strong>l ratónsobre el nombre <strong>de</strong>l proyecto recién creado, se abrirá un menú emergente, seleccionala opción New → Class, presta atención al i<strong>con</strong>o que se dibuja a laizquierda <strong>de</strong> esta opción, y verás que ese mismo i<strong>con</strong>o la encuentras en la barrar<strong>de</strong> herramientas en la parte superior <strong>de</strong> la ventana <strong>de</strong> Eclipse. Otro procedimiento,más rápido, <strong>de</strong> crear una nueva clase en Eclipse es pulsar directamente


1.3. EL ENTORNO DE DESARROLLO INTEGRADO ECLIPSE 19Figura 1.5: Creación <strong>de</strong> una nueva clase <strong>Java</strong> en Eclipse.ese i<strong>con</strong>o en la barra <strong>de</strong> herramientas.Al seleccionar esta opción, se abrirá la nueva ventana mostrada en la Figura1.5. En esta ventana vamos a introducir tres piezas <strong>de</strong> información:Un nombre <strong>de</strong> paquete en minúsculas (en el Capítulo 3 <strong>con</strong>ocerás <strong>con</strong><strong>de</strong>talle el significado <strong>de</strong> los paquetes en <strong>Java</strong>.Un nombre <strong>de</strong> clase <strong>con</strong> la primera letra <strong>de</strong> cada palabra en mayúsculasy sin espacios entre ellas.Selecciona la opción public static void main(String[] args)Esta tres piezas <strong>de</strong> información aparecen en la Figura 1.5. Recuerda introducirel nombre <strong>de</strong>l paquete y <strong>de</strong> la clase utilizando mayúsculas y minúsculastal y como se muestra en la Figura 1.5, en el Capítulo 2 <strong>con</strong>oceremos algunas<strong>de</strong> estas <strong>con</strong>venciones <strong>de</strong> codificación <strong>Java</strong>. Finalmente pulsa el botón Finish.Verás que <strong>de</strong> nuevo se actualiza la estructura <strong>de</strong>l proyecto, ahora podrás ver quese ha creado, bajo el nodo <strong>de</strong>l árbol src un nuevo nodo <strong>con</strong> nombre hola y bajoél el nodo HolaMundo.java. A<strong>de</strong>más, se ha abierto una nueva vista <strong>de</strong>l editor<strong>de</strong> código tal y como muestra la Figura 1.6.Hagamos una pequeña modificación sobre este código. Aña<strong>de</strong> la siguientelínea tal y como se muestra en la Figura 1.7. Esta instrucción sirve para mostraruna ca<strong>de</strong>na <strong>de</strong> texto por <strong>con</strong>sola.Una vez escrita la nueva línea <strong>de</strong> código graba el fichero, para ello pulsa lacombinación <strong>de</strong> teclas Ctrl + S. El siguiente paso va a ser ejecutar el programa,para ello haz click <strong>con</strong> el botón <strong>de</strong>recho <strong>de</strong>l ratón sobre el editor <strong>de</strong> códigoy en el menú emergente que aparecerá selecciona la opción Run As → <strong>Java</strong>Application.


20CAPÍTULO 1. INTRODUCCIÓNFigura 1.6: Aspecto <strong>de</strong>l editor <strong>de</strong> código <strong>Java</strong>.Figura 1.7: El primer programa en <strong>Java</strong> Hola mundo.


1.4. HERRAMIENTAS DE DESARROLLO 21Figura 1.8: Resultado <strong>de</strong> le ejecución <strong>de</strong>l primer programa en <strong>Java</strong> Hola mundo.En la vista Console podrás ver el resultado <strong>de</strong> la ejecución <strong>de</strong>l programa, taly como muestra la Figura 1.8Por <strong>de</strong>fecto, cada vez que haces modificaciones en el código <strong>de</strong> <strong>de</strong>finición<strong>de</strong> una clase y grabas el fichero, Eclipse compila automáticamente el códigomodificado. De este modo la compilación se realiza <strong>de</strong> modo transparente amedida que vamos trabajando en nuestro proyecto.Con estos sencillos pasos hemos creado nuestro primer proyecto en <strong>Java</strong>, yuna única clase en la que hemos introducido una línea <strong>de</strong> código que muestraun mensaje <strong>de</strong> texto por <strong>con</strong>sola. El trabajo <strong>con</strong> Eclipse es realmente sencillo.1.4. Herramientas <strong>de</strong> <strong>de</strong>sarrolloCuando nos planteamos <strong>de</strong>sarrollar un proyecto informático será <strong>de</strong> gran ayudaelegir una serie <strong>de</strong> herramientas que nos faciliten activida<strong>de</strong>s tales como el <strong>con</strong>trol<strong>de</strong> versiones <strong>de</strong> nuestro código o la prueba automática <strong>de</strong> nuestros métodos.En el estado actual <strong>de</strong> madurez <strong>de</strong> las tecnologías <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong>informáticos, resulta impensable iniciar el <strong>de</strong>sarrollo <strong>de</strong> un proyecto sin planificarel <strong>con</strong>trol <strong>de</strong> versiones, la gestión y seguimiento <strong>de</strong> errores, las herramientas <strong>de</strong><strong>de</strong>spliegue <strong>de</strong> la aplicación, y un largo etcétera.Afortunadamente, en la actualidad, <strong>con</strong>tamos <strong>con</strong> excelentes herramientas<strong>de</strong> software libre que cubren este tipo <strong>de</strong> tareas. E incluso, en algunos casos,existe más <strong>de</strong> una solución <strong>de</strong> software libre, <strong>con</strong> lo que po<strong>de</strong>mos evaluar varias<strong>de</strong> ellas y seleccionar la que mejor se adapte a nuestra forma <strong>de</strong> trabajar antes<strong>de</strong> empezar a utilizar.En este libro vamos a presentar algunas <strong>de</strong> estar herramientas. Las hemoselegido porque, a través <strong>de</strong> nuestra propia experiencia, nos han parecido las mása<strong>de</strong>cuadas a nuestro caso, pero como lector no <strong>de</strong>bes seguir ciegamente nuestraelección. Mejor aún, tómala como punto <strong>de</strong> partida que le permita evaluar otrasalternativas. La rápida evolución <strong>de</strong> la tecnología informática permite vaticinarque seguirán apareciendo cada vez más y mejores alternativas.En particular vamos a presentar las siguientes herramientas:Subvesion Es una herramienta para la gestión <strong>de</strong> versiones.JUnit Es un framework <strong>de</strong> pruebas automáticas <strong>de</strong> código.Ant Es una herramienta <strong>de</strong> <strong>con</strong>strucción <strong>de</strong> <strong>proyectos</strong>.MyLyn Es una herramienta <strong>de</strong> gestión <strong>de</strong> tareas.Bugzilla Es una herramienta <strong>de</strong> gestión y seguimiento <strong>de</strong> errores.Cada una <strong>de</strong> las anteriores herramientas cuentan <strong>con</strong> una gran popularidad<strong>de</strong>ntro <strong>de</strong> la comunidad <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> informáticos. Todas ellas


22CAPÍTULO 1. INTRODUCCIÓNcuentan <strong>con</strong> otras excelentes alternativas. Todas ellas se pue<strong>de</strong>n utilizar en <strong>proyectos</strong>que utilicen un lenguaje <strong>de</strong> programación alternativo a <strong>Java</strong>, o existenversiones <strong>de</strong> ellas para otros lenguajes <strong>de</strong> programación.1.4.1. Añadiendo nueva funcionalidad a Eclipse: los pluginsAfortunadamente, <strong>de</strong>s<strong>de</strong> Eclipse se pue<strong>de</strong> interaccionar <strong>con</strong> todas las herramientasexpuestas en la sección anterior.Eclipse cuenta <strong>con</strong> un sistema <strong>de</strong> plug-ins <strong>de</strong> tal modo que po<strong>de</strong>mos aumentarsus ya <strong>de</strong> por sí numerosas y potentes funcionalida<strong>de</strong>s <strong>con</strong> otras nuevas.Así, por ejemplo, po<strong>de</strong>mos instalar un plug-in <strong>de</strong> Eclipse para po<strong>de</strong>r realizarel <strong>con</strong>trol <strong>de</strong> versiones <strong>de</strong> nuestro código sin necesidad <strong>de</strong> abandonar Eclipse. Enel Capítulo 4 se mostrará cómo instalar el plug-in para Eclipse y cómo trabajar<strong>con</strong> él.Lecturas recomendadasUn escueto resumen sobre lo que significa el lenguaje <strong>de</strong> programación<strong>Java</strong> se pue<strong>de</strong> en<strong>con</strong>trar en [11].Una referencia completa sobre el entorno <strong>de</strong> <strong>de</strong>sarrollo Eclipse se pue<strong>de</strong>en<strong>con</strong>trar en la página web http://www.eclipse.org.


Capítulo 2Clases en <strong>Java</strong>Contenidos2.1. Definición <strong>de</strong> una clase . . . . . . . . . . . . . . . . 242.2. Miembros <strong>de</strong> una clase . . . . . . . . . . . . . . . . 252.2.1. Atributos <strong>de</strong> una clase . . . . . . . . . . . . . . . . . 252.2.2. Métodos <strong>de</strong> una clase. . . . . . . . . . . . . . . . . . 262.2.3. Constructores. . . . . . . . . . . . . . . . . . . . . . 282.2.4. Sobrecarga <strong>de</strong> métodos y <strong>con</strong>structores . . . . . . . . 322.3. Tipos <strong>de</strong> datos en <strong>Java</strong>. . . . . . . . . . . . . . . . . 332.3.1. Arrays <strong>de</strong> datos en <strong>Java</strong>. . . . . . . . . . . . . . . . . 342.4. Estructuras <strong>de</strong> <strong>con</strong>trol. . . . . . . . . . . . . . . . . 362.4.1. Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> repetición. . . . . . . . . . 372.4.1.1. El bucle for . . . . . . . . . . . . . . . . . 372.4.1.2. El bucle while . . . . . . . . . . . . . . . . 382.4.1.3. El bucle do...while . . . . . . . . . . . . . 382.4.2. Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> selección. . . . . . . . . . . 392.4.2.1. Bifurcaciones <strong>con</strong> la sentencia if...else. . 392.4.2.2. Múltiples caminos <strong>con</strong> la sentencia switch 392.5. Modificadores <strong>de</strong> acceso. . . . . . . . . . . . . . . . 402.6. Modificadores static y final. . . . . . . . . . . . . 422.7. El recolector <strong>de</strong> basura. . . . . . . . . . . . . . . . . 432.8. Finalización. . . . . . . . . . . . . . . . . . . . . . . 442.9. Comentarios. Comentarios <strong>de</strong> documentación. . . 45IntroducciónLas clases son la piedra angular <strong>de</strong> los lenguaje <strong>de</strong> programación orientados a objetos(POO). Las clases son abstracciones <strong>de</strong> entida<strong>de</strong>s <strong>de</strong> la realidad que sirvencomo plantillas para la creación <strong>de</strong> ejemplares <strong>de</strong> la clase. A estos ejemplares enPOO se les llama objetos o instancias <strong>de</strong> la clase. El proceso <strong>de</strong> abstracción <strong>de</strong>pen<strong>de</strong><strong>de</strong>l <strong>con</strong>texto en el que se utilizarán los ejemplares, es <strong>de</strong>cir, no es lo mismoabstraer la entidad <strong>de</strong>l mundo real ✭✭Persona✮✮ para utilizarla en una aplicación23


24CAPÍTULO 2. CLASES<strong>de</strong> gestión <strong>de</strong> los clientes <strong>de</strong> una clínica, que para utilizarla en una aplicación<strong>de</strong> seguros o <strong>de</strong> banca. En general, las características <strong>de</strong> la entidad real que nosinterese utilizar en la aplicación y las operaciones que podamos realizar sobreestas abstracciones serán diferentes.<strong>Java</strong> es un lenguaje <strong>de</strong> programación orientado a objetos, y aunque comoveremos posee tipos básicos para po<strong>de</strong>r manejar enteros o caracteres, todo en<strong>Java</strong> es un objeto. De hecho en <strong>Java</strong> existen dos gran<strong>de</strong>s tipos <strong>de</strong> datos: tipos<strong>de</strong> datos primitivos y tipos <strong>de</strong> datos referencia.En este capítulo vamos a ver cómo, a partir <strong>de</strong> una abstracción, po<strong>de</strong>mostranscribirla a código <strong>Java</strong> para crear una clase, y cómo a partir <strong>de</strong> una clasepo<strong>de</strong>mos crear instancias <strong>de</strong> esa clase.Finalmente avanzaremos la i<strong>de</strong>a <strong>de</strong> reutilización <strong>de</strong> una clase. En POO lareutilización implica escribir una nueva clase sin partir <strong>de</strong> cero, sino tomandocomo base otra clase cuyo comportamiento ampliamos o modificamos. Los<strong>de</strong>talles <strong>de</strong> cómo ampliar o exten<strong>de</strong>r el comportamiento <strong>de</strong> una clase base seintroducirán en el Capítulo 3.2.1. Definición <strong>de</strong> una claseSupongamos que queremos programar una aplicación <strong>de</strong> agenda telefónica. Elobjetivo <strong>de</strong> nuestra agenda telefónica es gestionar una serie <strong>de</strong> <strong>con</strong>tactos. Cadauno <strong>de</strong> estos <strong>con</strong>tactos representa a una Persona. Dicho <strong>de</strong> otro modo cadauno <strong>de</strong> los <strong>con</strong>tactos <strong>de</strong> la agenda está creado a partir <strong>de</strong> la misma plantillaPersona, que es la abstracción <strong>de</strong> una persona <strong>de</strong>l mundo real en el <strong>con</strong>texto <strong>de</strong>la aplicación <strong>de</strong> la agenda telefónica.¿Qué necesitamos especificar para crear un objeto o ejemplar <strong>de</strong> la clasePersona? Cada uno <strong>de</strong> los objetos creados a partir <strong>de</strong> esta clase <strong>con</strong>tendrá unaserie <strong>de</strong> valores que lo i<strong>de</strong>ntifican, como el nombre y los apellidos <strong>de</strong>l <strong>con</strong>tactoy su número <strong>de</strong> teléfono. El <strong>con</strong>junto <strong>de</strong> todos los valores <strong>de</strong> un objeto va a<strong>de</strong>terminar su estado en un momento <strong>con</strong>creto. Por otro lado, sobre cada uno <strong>de</strong>los objetos vamos a po<strong>de</strong>r llevar a cabo un <strong>con</strong>junto <strong>de</strong> operaciones <strong>de</strong>finidas enla clase. Volviendo al ejemplo <strong>de</strong> la agenda telefónica, cada una <strong>de</strong> las ✭✭Persona✮✮<strong>de</strong> la agenda va a tener una serie <strong>de</strong> datos <strong>de</strong> interés, que pue<strong>de</strong>n o no variara lo largo <strong>de</strong>l tiempo (un <strong>con</strong>tacto <strong>de</strong> mi agenda pue<strong>de</strong> cambiar <strong>de</strong> número <strong>de</strong>teléfono, pero no es probable que cambie <strong>de</strong> apellidos), y me va a ofrecer unaserie <strong>de</strong> operaciones que puedo realizar sobre ella, como por ejemplo <strong>con</strong>sultarsu nombre.DefiniciónAl <strong>con</strong>junto <strong>de</strong> valores <strong>de</strong>finidos en la clase se le llama atributos <strong>de</strong> la clase. Al<strong>con</strong>junto <strong>de</strong> operaciones que <strong>de</strong>fine una clase se le llama métodos <strong>de</strong> la clase.Cuando hablamos <strong>de</strong> miembros <strong>de</strong> una clase hacemos referencia tanto a losatributos como a los métodos <strong>de</strong> la clase.La <strong>de</strong>finición <strong>de</strong> una clase en <strong>Java</strong> empieza <strong>con</strong> la palabra reservada class, yel <strong>con</strong>junto <strong>de</strong> atributos y métodos <strong>de</strong> la clase se <strong>de</strong>fine en un bloque <strong>de</strong>limitadopor llaves, <strong>de</strong>l siguiente modo


2.2. MIEMBROS DE UNA CLASE 251 c l a s s Persona {2 // D e c l a r a c i ó n <strong>de</strong> a t r i b u t o s3 // D e f i n i c i ó n <strong>de</strong> métodos4 }2.2. Miembros <strong>de</strong> una clase2.2.1. Atributos <strong>de</strong> una claseAhora que ya sabemos que <strong>de</strong>bemos abstraer una ✭✭Persona✮✮ <strong>de</strong>l mundo realen el <strong>con</strong>texto <strong>de</strong> nuestra aplicación la siguiente pregunta es: ¿Cuales son lascaracterísticas, o datos, <strong>de</strong> una persona relevantes en el <strong>con</strong>texto <strong>de</strong> una agendatelefónica? Sin duda uno <strong>de</strong> estos datos es el número <strong>de</strong> teléfono <strong>de</strong> la persona;cada <strong>con</strong>tacto <strong>de</strong> mi agenda tiene, <strong>de</strong> manera simplificada, un número <strong>de</strong>teléfono. ¿Qué otros datos pue<strong>de</strong>n ser <strong>de</strong> interés almacenar en una agenda telefónica?,parece evi<strong>de</strong>nte que, al menos, el nombre y los apellidos <strong>de</strong> cada uno<strong>de</strong> los <strong>con</strong>tactos.Representemos gráficamente lo que tenemos hasta ahora✗Persona posee:→Nombre;→Apellidos;→Teléfono;✖✔✕¿Cómo se <strong>de</strong>finen estos atributos en la clase? De cada uno <strong>de</strong> los atributos<strong>de</strong>bemos especificar su tipo, por ejemplo, en el caso <strong>de</strong>l Nombre, utilizaremos unaca<strong>de</strong>na <strong>de</strong> caracteres; en el caso <strong>de</strong>l Telefono po<strong>de</strong>mos optar entre representarlocomo un número entero o una ca<strong>de</strong>na <strong>de</strong> caracteres; si queremos almacenar losnúmeros <strong>de</strong> teléfono en formato internacional (Ej: (+34) 555 555 555) optaremospor representarlos como ca<strong>de</strong>nas <strong>de</strong> caracteres.Los atributos los <strong>de</strong>clararemos <strong>de</strong> este modo:1 c l a s s Persona {2 S t r i n g nombre ;3 S t r i n g a p e l l i d o s ;4 S t r i n g t e l e f o n o ;5 // D e f i n i c i ó n <strong>de</strong> métodos6 }Fíjate que, al escribir el nombre <strong>de</strong> la clase hemos empezado la palabrapor una letra mayúscula, y que al empezar el nombre <strong>de</strong> un atributo lo hemosempezado por minúscula. Esta es una <strong>con</strong>vención <strong>de</strong> codificación en <strong>Java</strong> que<strong>con</strong>viene seguir puesto que está ampliamente extendida entre los <strong>de</strong>sarrolladores<strong>Java</strong>. Veremos más reglas <strong>de</strong> <strong>con</strong>vención en la Sección 2.9. Fíjate también quehemos <strong>de</strong>finido cada atributo en un línea distinta y que cada línea acaba <strong>con</strong> elcaracter ;.


26CAPÍTULO 2. CLASESReglas <strong>de</strong> <strong>con</strong>venciónSegún las reglas <strong>de</strong> <strong>con</strong>vención más extendidas en <strong>Java</strong>, al <strong>de</strong>finir una clase,el nombre <strong>de</strong> la clase se <strong>de</strong>be escribir <strong>con</strong> la primera letra en mayúscula y losnombres <strong>de</strong> los atributos y métodos <strong>de</strong>ben empezar por una letra en minúscula.Si estos nombres están formados por más <strong>de</strong> una palabra, la segunda y siguientespalabras que <strong>con</strong>stituyen el nombre se escriben <strong>con</strong> su primera letra enmayúscula. Por ejemplo: numeroTelefono.Veamos ahora cómo <strong>de</strong>finir las operaciones que podremos realizar sobre lasinstancias <strong>de</strong> la clase Persona.2.2.2. Métodos <strong>de</strong> una clase.Una vez hemos creado una instancia <strong>de</strong> la clase Persona, ¿Cómo po<strong>de</strong>mos recuperara partir <strong>de</strong> ella su nombre?, ¿Cómo po<strong>de</strong>mos recuperar el nombre quealmacenamos en un <strong>con</strong>tacto <strong>de</strong> nuestra agenda?.Una posibilidad es simplemente leer el valor <strong>de</strong>l atributo, pero como veremosen la sección 2.5 el acceso directo a los atributos <strong>de</strong> una clase está <strong>de</strong>sa<strong>con</strong>sejado.La respuesta es: a través <strong>de</strong> una llamada a un método que <strong>de</strong>vuelva el nombre<strong>de</strong>l <strong>con</strong>tacto. En el caso <strong>de</strong> la recuperación <strong>de</strong>l nombre, el tipo <strong>de</strong> dato <strong>de</strong>retorno es una ca<strong>de</strong>na class String. Un método que cumple este objetivo esel siguiente:1 S t r i n g getPersona ( ) {2 return nombre ;3 }SintaxisLa sintaxis <strong>de</strong> <strong>de</strong>claración <strong>de</strong> un método es:{modificadores} tipoRetorno nombre(tipo argumento1, tipo argumento2, ...) {Bloque <strong>de</strong> <strong>de</strong>finición <strong>de</strong>l método;}En estas pocas líneas <strong>de</strong> código hay varias noveda<strong>de</strong>s, veámoslas:1. Un método tiene un nombre que lo i<strong>de</strong>ntifica, en este caso getNombre.2. Delante <strong>de</strong>l nombre <strong>de</strong>l método escribimos el tipo <strong>de</strong>l valor <strong>de</strong> retorno, ennuestro caso, como lo que el método <strong>de</strong>vuelve es el nombre cuyo tipo esun String, este será el tipo <strong>de</strong>l valor retorno.3. Detrás <strong>de</strong>l nombre <strong>de</strong>l método aparecen unos paréntesis sin nada en su interior.Dentro <strong>de</strong> los paréntesis se <strong>de</strong>fine la lista <strong>de</strong> argumentos <strong>de</strong>l método.Si estás familiarizado <strong>con</strong> las matemáticas, los argumentos <strong>de</strong> los métodostienen el mismo significado que los argumentos <strong>de</strong> las funciones matemáticas,por ejemplo seno(45 o ) significa que queremos utilizar el cálculo <strong>de</strong>lseno sobre el argumento 45 grados. En nuestro caso la lista está vacía, loque indica que no necesito especificar ningún argumento para po<strong>de</strong>r haceruso <strong>de</strong>l método.


2.2. MIEMBROS DE UNA CLASE 274. La <strong>de</strong>finición <strong>de</strong>l método va <strong>de</strong>ntro <strong>de</strong> las llaves .5. Para <strong>de</strong>volver un valor utilizamos la palabra reservada return.Como ves, muchos <strong>con</strong>ceptos nuevos en tan sólo tres líneas <strong>de</strong> código. Y unanueva <strong>con</strong>vención <strong>de</strong> codificación, si un método <strong>de</strong>vuelve el valor <strong>de</strong> un atributoempieza por la palabra inglesa get, <strong>de</strong> ahí que hayamos escrito getNombre().Con lo que ya hemos visto, es sencillo escribir dos nuevos método que <strong>de</strong>vuelvanlos apellidos y el número <strong>de</strong> teléfono <strong>de</strong> una Persona. Aquí tienes elcódigo <strong>de</strong> la clase:1 c l a s s Persona {2 S t r i n g nombre ;3 S t r i n g a p e l l i d o s ;4 S t r i n g t e l e f o n o ;56 S t r i n g getPersona ( ) {7 return nombre ;8 }910 S t r i n g g e t A p e l l i d o s ( ) {11 return a p e l l i d o s ;12 }1314 S t r i n g g e t T e l e f o n o ( ) {15 return t e l e f o n o ;16 }17 }Listado 2.1: Código <strong>de</strong> la clase PersonaDe nuevo, fíjate que si un método no recibe argumentos su lista <strong>de</strong> argumentosestá vacía. Pero si un método no <strong>de</strong>vuelve ningún parámetro, hay queindicarlo explícitamente utilizando la palabra reservada void. Por ejemplo, elsiguiente método no <strong>de</strong>vuelve ningún valor:1 void nada ( ) {2 // Código d e l método3 }En muchas ocasiones resulta interesante po<strong>de</strong>r modificar el valor <strong>de</strong> los atributos.Como ya hemos comentado anteriormente, un <strong>con</strong>tacto <strong>de</strong> mi agendapodría cambiar <strong>de</strong> número <strong>de</strong> teléfono, luego parece buena i<strong>de</strong>a que la clasePersona me proporcione un método que permita modificar el valor <strong>de</strong>l atributotelefono, como el que se muestra en el siguiente ejemplo:1 void s e t T e l e f o n o ( S t r i n g nuevoTelefono ) {2 t e l e f o n o = nuevoTelefono ;3 }Listado 2.2: Método para modificar el valor <strong>de</strong>l teléfono.De nuevo, hemos seguido una <strong>con</strong>vención <strong>de</strong> codificación:Regla <strong>de</strong> <strong>con</strong>venciónLos métodos que modifican el valor <strong>de</strong> los atributos <strong>de</strong> una clase se nombranempezando <strong>con</strong> la palabra inglesa set seguida por el nombre <strong>de</strong>l atributo, cuyaprimera letra se escribe en mayúsculas.


28CAPÍTULO 2. CLASESDe modo análogo, po<strong>de</strong>mos añadir a la clase Persona dos nuevos métodospara po<strong>de</strong>r modificar el valor <strong>de</strong> los atributos nombre y apellidos, tal y comose muestra a <strong>con</strong>tinuación:1 void setNombre ( S t r i n g nuevoNombre ) {2 nombre = nuevoNombre ;3 }45 void s e t A p e l l i d o s ( S t r i n g n u e v o s A p e l l i d o s ) {6 a p e l l i d o s = n u e v o s A p e l l i d o s ;7 }Listado 2.3: Métodos para modificar el nombre y los apellidos <strong>de</strong> una PersonaYa tenemos escritos métodos que nos permiten leer y modificar los atributos<strong>de</strong> la clase Persona. Ahora queremos crear ejemplares <strong>de</strong> esta clase, para ellonecesitamos escribir métodos especiales que nos sirvan para crear instancias <strong>de</strong>la clase, a estos métodos especiales se les llama <strong>con</strong>structores <strong>de</strong> la clase.2.2.3. Constructores.Para crear un ejemplar <strong>de</strong> una clase utilizamos métodos especiales llamados<strong>con</strong>structores <strong>de</strong> la clase. En las siguientes líneas <strong>de</strong> código se muestra cómo se<strong>de</strong>fine un <strong>con</strong>structor <strong>de</strong> la clase Persona:1 Persona ( 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 ) {2 t h i s . nombre = nombre ;3 t h i s . a p e l l i d o s = a p e l l i d o s ;4 t h i s . t e l e f o n o = t e l e f o n o ;5 }Listado 2.4: Constructor <strong>con</strong> parámetros <strong>de</strong> la clase PersonaVolvemos a tener nuevos <strong>con</strong>ceptos en estas líneas <strong>de</strong> código, veámoslo:1. Un <strong>con</strong>structor es un método cuyo nombre coinci<strong>de</strong> <strong>con</strong> el <strong>de</strong> la clase,en nuestro caso el nombre <strong>de</strong>l método es Persona que es precisamente elnombre <strong>de</strong> la clase.2. Como cualquier otro método, tiene un lista <strong>de</strong> argumentos que en estecaso no está vacía, si no que indica que va a recibir tres argumentos y lostres <strong>de</strong> tipo String.3. Fíjate que los nombres <strong>de</strong> los tres argumentos coinci<strong>de</strong>n <strong>con</strong> los nombres <strong>de</strong>los atributos; la clase tiene <strong>de</strong>clarado un atributo <strong>de</strong> tipo String llamadonombre y el primer argumento <strong>de</strong>l <strong>con</strong>structor también se llama nombrey es <strong>de</strong> tipo String. ¿Cómo resolvemos la ambigüedad entre el nombre<strong>de</strong>l atributo y el nombre <strong>de</strong>l argumento?, utilizando la palabra reservadathis; si escribimos this.nombre estamos haciendo referencia al atributo,si sólo escribimos nombre, estamos haciendo referencia al argumento <strong>de</strong>lmétodo. Veremos <strong>con</strong> más <strong>de</strong>talle el significado <strong>de</strong> la palabra reservadathis en la sección 2.3.4. Un <strong>con</strong>structor no <strong>de</strong>vuelve ningún valor <strong>de</strong> retorno, ya que estos métodosespeciales nos sirven para crear objetos.


2.2. MIEMBROS DE UNA CLASE 29Escribamos otro <strong>con</strong>structor para la clase Persona:1 Persona ( ) { }Más noveda<strong>de</strong>s <strong>con</strong>ceptuales en estas líneas <strong>de</strong> código:1. La lista <strong>de</strong> argumentos <strong>de</strong> este <strong>con</strong>structor está vacía.2. No hemos escrito ninguna línea <strong>de</strong> código entre las llaves.A este <strong>con</strong>structor tan particular se le llama Constructor por <strong>de</strong>fecto y hablaremosmás sobre él en el capítulo 3 <strong>de</strong>dicado a la herencia en <strong>Java</strong>. De momentoquédate <strong>con</strong> la i<strong>de</strong>a <strong>de</strong> que es importante que tus clases <strong>de</strong>finan el <strong>con</strong>structorpor <strong>de</strong>fecto, <strong>de</strong> hecho, todas tus clases <strong>de</strong>berían <strong>de</strong>finirlo. Si tu clase no proporcionaningún <strong>con</strong>structor, como en el caso <strong>de</strong>l Listado 2.5, el compilador <strong>de</strong><strong>Java</strong> crea el <strong>con</strong>structor por <strong>de</strong>fecto para la clase, <strong>de</strong> modo que puedas crearinstancias a partir <strong>de</strong> ella.1 c l a s s S i n C o n s t r u c t o r e s {2 private int a ;34 int getA ( ) {5 return a ;6 }7 }Listado 2.5: Una clase sin ningún <strong>con</strong>structor. El compilador <strong>de</strong> <strong>Java</strong> creará el<strong>con</strong>structor por <strong>de</strong>fecto por nosotros.Veamos todo el código que hemos escrito para la clase Persona:1 package agenda ;23 public c l a s s Persona {4 S t r i n g nombre ;5 S t r i n g a p e l l i d o s ;6 S t r i n g t e l e f o n o ;78 Persona ( ) { }910 Persona ( 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 ) {11 t h i s . nombre = nombre ;12 t h i s . a p e l l i d o s = a p e l l i d o s ;13 t h i s . t e l e f o n o = t e l e f o n o ;14 }1516 S t r i n g getNombre ( ) {17 return nombre ;18 }1920 S t r i n g g e t A p e l l i d o s ( ) {21 return a p e l l i d o s ;22 }2324 S t r i n g g e t T e l e f o n o ( ) {25 return t e l e f o n o ;26 }27 }Listado 2.6: Código <strong>de</strong> la clase PersonaEn el Listado 2.6 hemos agrupado los métodos get/set para cada uno <strong>de</strong>los atributos, a<strong>de</strong>más hemos modificado la <strong>de</strong>finición <strong>de</strong> los métodos set para


30CAPÍTULO 2. CLASES<strong>de</strong>shacer la ambigüedad entre el nombre <strong>de</strong> los atributos y <strong>de</strong> los argumentos,tal y como hemos hecho en el caso <strong>de</strong>l <strong>con</strong>structor <strong>con</strong> argumentos.Antes <strong>de</strong> pasar a<strong>de</strong>lante, escribamos nuestra primera pequeña aplicación en<strong>Java</strong> para probar todo lo que hemos visto hasta ahora. Vamos a utilizar para elloel entorno integrado <strong>de</strong> <strong>de</strong>sarrollo Eclipse, inicia pues esta aplicación. Hay variasopciones para crear un nuevo proyecto en Eclipse, a través <strong>de</strong>l menú pue<strong>de</strong>selegir File → New → <strong>Java</strong> Project, o bien pue<strong>de</strong>s pulsar el botón <strong>de</strong> creación<strong>de</strong> <strong>proyectos</strong>. Eclipse te solicitará un nombre para el proyecto, introduce unoa<strong>de</strong>cuado (por ejemplo ✭✭AgendaTelefonica✮✮), y ya pue<strong>de</strong>s pulsar directamentela tecla Finish. Verás que en la columna izquierda <strong>de</strong> Eclipse, don<strong>de</strong> se muestrala vista Package Explorer te aparece una carpeta <strong>con</strong> el mismo nombre que elproyecto recién creado. Eclipse organiza los <strong>proyectos</strong> en carpetas, el código <strong>de</strong>tu proyecto, ficheros <strong>de</strong> bibliotecas y recursos necesarios estarán en la carpeta<strong>de</strong>l proyecto.Para crear un nueva clase en Eclipse pue<strong>de</strong>s hacerlo a través <strong>de</strong>l menú File→ New → Class, o bien pulsando directamente el botón <strong>de</strong> creación <strong>de</strong> unanueva clase. Se abrirá una ventana <strong>de</strong> diálogo solicitándote un nombre parala nueva clase y el paquete don<strong>de</strong> se incluirá. Es muy recomendable que cadaclase esté <strong>de</strong>ntro <strong>de</strong> un paquete (veremos <strong>con</strong> más <strong>de</strong>talle el significado <strong>de</strong> lospaquetes en <strong>Java</strong> en la Sección 3.6). Según las <strong>con</strong>venciones <strong>de</strong> <strong>Java</strong>, los nombres<strong>de</strong> paquetes se escriben en minúscula. Escribe, por ejemplo, para el nombre <strong>de</strong>lpaquete agenda, y para el nombre <strong>de</strong> la clase Persona. Verás que se abre lavista <strong>de</strong>l editor <strong>de</strong> código en Eclipse y que si <strong>de</strong>spliegas la carpeta <strong>de</strong> proyectote aparece el fichero <strong>de</strong> clase Persona.java. Escribe la <strong>de</strong>finición <strong>de</strong> la clasesegún el Listado 2.6.Lo siguiente que vamos a hacer es escribir una clase para probar nuestra clasePersona, para ello crea en el proyecto una nueva clase y llámala PruebaPersonay como nombre <strong>de</strong> paquete introduce agenda, y en el cuadro <strong>de</strong> diálogo <strong>de</strong> creación<strong>de</strong> la clase marca la casilla public static void main(String[] args),<strong>con</strong> ello Eclipse creará <strong>de</strong> manera automática el método principal main. Escribeel resto <strong>de</strong> código que aparece en el Listado 2.7.1 package agenda ;23 public c l a s s PruebaPersona {45 /∗∗6 ∗ @param a r g s7 ∗/8 public s t a t i c void main ( S t r i n g [ ] a r g s ) {9 // TODO Auto−g e n e r a t e d method stub10 Persona unaPersona = new Persona ( " Ó s c a r " , " B e l m o n t e " , " 1234 " ) ;11 System . out . p r i n t l n ( " M u e s t r a i n f o r m a c i ó n a c c e d i e n d o d i r e c t a m e n t e a losc a m p o s . " ) ;12 System . out . p r i n t l n ( " N o m b r e : " + unaPersona . nombre ) ;13 System . out . p r i n t l n ( " A p e l l i d o s : " + unaPersona . a p e l l i d o s ) ;14 System . out . p r i n t l n ( " T e l é f o n o : " + unaPersona . t e l e f o n o ) ;1516 System . out . p r i n t l n ( " M u e s t r a i n f o r m a c i ó n l l a m a n d o a los m é t o d o s <strong>de</strong> lac l a s e . " ) ;17 System . out . p r i n t l n ( " N o m b r e : " + unaPersona . getNombre ( ) ) ;18 System . out . p r i n t l n ( " A p e l l i d o s : " + unaPersona . g e t A p e l l i d o s ( ) ) ;19 System . out . p r i n t l n ( " T e l é f o n o : " + unaPersona . g e t T e l e f o n o ( ) ) ;20 }2122 }


2.2. MIEMBROS DE UNA CLASE 31Listado 2.7: Código <strong>de</strong> la clase PrincipalLa clase Principal está repleta <strong>de</strong> noveda<strong>de</strong>s. Esta clase tiene un único métodopublic static void main(String[] args), este método es el punto <strong>de</strong>entrada a la ejecución <strong>de</strong> un programa <strong>Java</strong>. En las siguientes secciones veremosel significado <strong>de</strong> todos los modificadores que tiene este método <strong>de</strong>lante <strong>de</strong> sunombre que es main. En la línea número 10, vemos cómo se usa el operador newpara crear una instancia <strong>de</strong> la clase, escribimos tras new un <strong>con</strong>structor <strong>de</strong> laclase, en este caso Persona("Óscar", "Belmonte", "1234"),new utilizará el<strong>con</strong>structor <strong>con</strong> tres argumentos <strong>de</strong> la clase Persona para crear una nueva instancia.Fíjate que a la izquierda <strong>de</strong> new tenemos Persona unaPersona =, estoindica que nos guardamos lo que el operador new <strong>de</strong>vuelve en la variable <strong>de</strong>tipo referencia a Persona que llamamos unaPersona, en las siguientes seccionesveremos <strong>con</strong> más <strong>de</strong>talle qué significa el <strong>con</strong>cepto variable <strong>de</strong> tipo referencia, <strong>de</strong>momento la i<strong>de</strong>a es que, para po<strong>de</strong>r usar la instancia a la Persona recién creadautilizaremos la variable <strong>de</strong> referencia unaPersona.Reglas <strong>de</strong> <strong>con</strong>venciónLos nombre <strong>de</strong> los paquetes y subpaquetes se escriben en minúsculas.En las líneas 12-14 recuperamos la información a partir <strong>de</strong> la variable <strong>de</strong>tipo referencia a Persona accediendo directamente a sus atributos (nombre,apellidos, telefono); mientras que en las líneas 17-19 acce<strong>de</strong>mos a la mismainformación haciendo uso <strong>de</strong> los métodos <strong>de</strong>finidos en la clase (getNombre(),getApellidos(), getTelefono()).Finalmente, para mostrar información en forma <strong>de</strong> texto por <strong>con</strong>sola utilizamosSystem.out.println("Texto").Ejecutemos este primer programa para ver cual es el resultado, para ellohaz click <strong>con</strong> el botón <strong>de</strong>recho sobre el nombre <strong>de</strong> la clase Principal.javaque tienes en la columna <strong>de</strong> la <strong>de</strong>recha en Eclipse (Package Explorer) y enel menú emergente que te aparecerá selecciona Run as → <strong>Java</strong> Application;en la parte inferior <strong>de</strong> Eclipse se abrirá una nueva solapa (Console) don<strong>de</strong> semostrará el resultado <strong>de</strong> la ejecución que <strong>de</strong>be ser:Muestra información accediendo directamente a los campos.Nombre: ÓscarApellidos:BelmonteTeléfono: 1234Muestra información llamando a los métodos <strong>de</strong> la clase.Nombre: ÓscarApellidos:BelmonteTeléfono: 1234Como has comprobado, el trabajo <strong>de</strong> edición en Eclipse es realmente sencilloy te irás dando cuenta que este entorno <strong>de</strong> programación <strong>Java</strong> es muy potente.


32CAPÍTULO 2. CLASESPreguntaEn el ejemplo anterior estamos recuperando la información almacenada en unainstancia <strong>de</strong> la clase Persona <strong>de</strong> dos modos: accediendo directamente a susatributos, o llamando a los métodos <strong>de</strong> la clase. ¿Qué sentido tiene <strong>de</strong>clararmétodos <strong>de</strong> acceso a los atributos <strong>de</strong> una clase si puedo acce<strong>de</strong>r directamente aellos?.2.2.4. Sobrecarga <strong>de</strong> métodos y <strong>con</strong>structoresDos o más métodos pue<strong>de</strong>n tener el mismo nombre siempre que su número<strong>de</strong> argumentos sea distinto. En caso <strong>de</strong> que los dos métodos tengan el mismonúmero <strong>de</strong> argumentos, serán distintos si al menos un tipo <strong>de</strong> sus argumentoses distinto. Por ejemplo en el siguiente Listado los dos métodos unMetodo estánsobrecargados y son distintos.1 p u b l i c void unMetodo ( i n t e n t e r o ) {2 // D e f i n i c i ó n d e l método3 }45 p u b l i c void unMetodo ( f l o a t r e a l ) {6 // D e f i n i c i ó n d e l método7 }De modo análogo, los <strong>con</strong>structores también pue<strong>de</strong>n estar sobrecargados,<strong>de</strong> hecho hemos sobrecargado el <strong>con</strong>structor <strong>de</strong> la clase Persona en el Listado2.6, esta clase tiene dos <strong>con</strong>structores Persona() y Persona(String nombre,String apellidos, String telefono).Un <strong>de</strong>talle muy importante en la sobrecarga <strong>de</strong> métodos es que el tipo <strong>de</strong>retorno no sirve para distinguir dos métodos. Si dos métodos tienen el mismonúmero <strong>de</strong> argumentos y sus tipos son los mismos, no los podremos sobrecargarhaciendo que el tipo <strong>de</strong> sus valores <strong>de</strong> retorno sean distintos.DefiniciónEl nombre <strong>de</strong> un método junto <strong>con</strong> su lista <strong>de</strong> argumentos forman la signatura<strong>de</strong>l método. El tipo <strong>de</strong>l valor <strong>de</strong> retorno no forma parte <strong>de</strong> la signatura <strong>de</strong> unmétodo.En el siguiente Listado se muestra un error al intentar sobrecargar dos métodosque se distinguen únicamente por su tipo <strong>de</strong> retorno.1 // ESTE LISTADO CONTIENE UN ERROR.2 // LOS MÉTODOS NO SE PUEDEN SOBRECARGAR POR EL TIPO DE RETORNO.3 p u b l i c void unMetodo ( ) {4 // D e f i n i c i ó n d e l método5 }67 p u b l i c i n t unMetodo ( ) {8 // D e f i n i c i ó n d e l método9 }Los dos métodos tienen el mismo nombre y ningún argumento, el primero <strong>de</strong>ellos no retorna nada void, y el segundo <strong>de</strong> ellos retorna un int. El compilador es


2.3. TIPOS DE DATOS EN JAVA. 33Tipo Tamaño(bits) Definiciónboolean 1 true o falsechar 16 Carácter Unico<strong>de</strong>byte 8 Entero en complemento a dos <strong>con</strong> signoshort 16 Entero en complemento a dos <strong>con</strong> signoint 32 Entero en complemento a dos <strong>con</strong> signolong 64 Entero en complemento a dos <strong>con</strong> signofloat 32 Real en punto flotante según la norma IEEE 754double 64 Real en punto flotante según la norma IEEE 754Tabla 2.1: Tipos <strong>de</strong> datos primitivos en <strong>Java</strong> y sus tamaños en memoria.incapaz <strong>de</strong> distinguirlos y <strong>de</strong>vuelve un error que indica que estamos intentando<strong>de</strong>finir el mismo método dos veces.Pero volvamos a <strong>Java</strong> y vemos qué significa el término tipo <strong>de</strong> dato referencia.2.3. Tipos <strong>de</strong> datos en <strong>Java</strong>.En <strong>Java</strong> existen dos gran<strong>de</strong>s grupos <strong>de</strong> tipos <strong>de</strong> datos, los tipos <strong>de</strong> datos primitivosy los tipos <strong>de</strong> datos referencia.Los tipos <strong>de</strong> datos primitivos sirven para representar tipos <strong>de</strong> datos talescomo números enteros, caracteres, números reales, booleanos, etcétera. Se lesllama primitivos porque nos permiten manejar elementos <strong>de</strong> información básicoscomo letras y números. Una variable <strong>de</strong> tipo primitivo nos permite almacenaren ella un tipo primitivo como por ejemplo un valor numérico.Por otro lado, los tipos <strong>de</strong> datos referencia nos permiten indicar que vamos atrabajar <strong>con</strong> instancias <strong>de</strong> clases, no <strong>con</strong> tipos primitivos. Una variable <strong>de</strong> tiporeferencia establece una <strong>con</strong>exión hacia un objeto, y a través <strong>de</strong> esta <strong>con</strong>exiónpodremos acce<strong>de</strong>r a sus atributos y métodos.Cuando hablamos <strong>de</strong> variables, es muy importante asimilar la diferencia entrevariables <strong>de</strong> tipo primitivo y variables <strong>de</strong> tipo referencia. En una variable<strong>de</strong> tipo primitivo po<strong>de</strong>mos almacenar valores <strong>de</strong> tipo primitivo (números, caracteres);pero el las variables <strong>de</strong> tipo referencia no almacenamos valores sonla puerta <strong>de</strong> entrada hacia los objetos. Son los objetos, las instancias <strong>de</strong> clases,las que almacenan información y me permiten trabajar <strong>con</strong> ellos a través <strong>de</strong>llamadas a sus métodos.ConceptoLas variables <strong>de</strong> tipo primitivo nos permiten almacenar valores <strong>de</strong> tipo primitivocomo números y caracteres. Las variables <strong>de</strong> tipo referencia no almacenanvalores, sino que nos permiten acce<strong>de</strong>r a los atributos y métodos <strong>de</strong> los objetos.En <strong>Java</strong>, el tamaño en memoria <strong>de</strong> los tipos <strong>de</strong> datos primitivos está estandarizado.Los tipos <strong>de</strong> datos primitivos y sus tamaños son los que aparecen enla Tabla 2.1.


34CAPÍTULO 2. CLASESSintaxisLas variables <strong>de</strong> tipo primitivo se <strong>de</strong>claran <strong>de</strong> este modo:tipo nombre [ = valor inicial];Ejemplo 1 : int hojas;Ejemplo 2 : float pi = 3.14f; //fíjate en la f al final <strong>de</strong>l númeroComo ya hemos visto, las referencias en <strong>Java</strong> son la puerta <strong>de</strong> entrada a losobjetos, las referencias me permiten acce<strong>de</strong>r a los atributos y métodos <strong>de</strong> losobjetos, el tipo <strong>de</strong> una referencia <strong>de</strong>be ser compatible <strong>con</strong> el tipo <strong>de</strong>l objeto alque se refiere. En el capítulo 3 <strong>de</strong>dicado a la herencia veremos qué quiere <strong>de</strong>cir✭✭compatible✮✮.SintaxisLas variables <strong>de</strong> tipo referencia se <strong>de</strong>claran <strong>de</strong> este modo:tipoReferencia nombre [ = valor referencia inicial];Ejemplo 1 : Persona persona;Ejemplo 2 : Persona persona = new Persona("Óscar", "Pérez", "123");En múltiples ocasiones, nos interesa trabajar <strong>con</strong> más <strong>de</strong> un único valor<strong>de</strong> un <strong>de</strong>terminado tipo, en vez <strong>de</strong> trabajar <strong>con</strong> una única Persona queremostrabajar <strong>con</strong> un grupo <strong>de</strong> personas. Veamos cómo po<strong>de</strong>mos <strong>de</strong>clarar <strong>con</strong>juntos<strong>de</strong> elementos <strong>de</strong>l mismo tipo en <strong>Java</strong>.2.3.1. Arrays <strong>de</strong> datos en <strong>Java</strong>.Hasta el momento, hemos aprendido cómo <strong>de</strong>clarar variables <strong>de</strong> tipos <strong>de</strong> datosprimitivos y <strong>de</strong> tipos <strong>de</strong> datos referencia. Esto nos sirve para crear una únicavariable que <strong>con</strong>tendrá bien un tipo <strong>de</strong> datos primitivo a una referencia a unobjeto, pero a veces nos interesa po<strong>de</strong>r manejar <strong>con</strong>juntos <strong>de</strong> elementos <strong>de</strong>lmismo tipo, por ejemplo, en alguna circunstancia nos pue<strong>de</strong> interesar <strong>de</strong>clararuna variable <strong>con</strong> la que po<strong>de</strong>r acce<strong>de</strong>r a un grupo <strong>de</strong> 10 enteros o 100 objetos<strong>de</strong> la clase Persona.En <strong>Java</strong> utilizaremos arrays <strong>de</strong> elementos cuando necesitemos manejar más<strong>de</strong> un elemento <strong>de</strong>l mismo tipo. Para <strong>de</strong>clarar un array en <strong>Java</strong> utilizamos loscorchetes según la siguiente sintaxis:SintaxisLos arrays <strong>de</strong> tipos primitivos se <strong>de</strong>claran:Ejemplo 1 : int array[]; // Array <strong>de</strong>claradoEjemplo 2 : int arrayEnteros[] = new int[10]; // Array iniciadoLos arrays <strong>de</strong> tipos referencia se <strong>de</strong>claran:Ejemplo 3 : Persona grupo[]; // Array <strong>de</strong>claradoEjemplo 4 : Persona grupo = new Persona[10]; // Array iniciadoAunque la sintaxis <strong>de</strong> la <strong>de</strong>claración <strong>de</strong> arrays <strong>de</strong> tipo primitivo y <strong>de</strong> tiporeferencia es la misma, el resultado es radicalmente distinto en los dos casos.


2.3. TIPOS DE DATOS EN JAVA. 35Analicémoslo. En el Ejemplo 2 <strong>de</strong>l recuadro <strong>de</strong> sintaxis anterior se está <strong>de</strong>finiendoun array capaz <strong>de</strong> albergar 10 enteros (<strong>con</strong> índice 0 para el primer elemento eíndice 9 para el último), <strong>de</strong>ntro <strong>de</strong> cada una <strong>de</strong> las posiciones <strong>de</strong>l array po<strong>de</strong>mosalmacenar un entero.En el caso <strong>de</strong>l Ejemplo 4, estamos <strong>de</strong>finiendo un array capaz <strong>de</strong> albergar 10✭✭referencias✮✮ <strong>de</strong> tipo Persona. En este caso, lo que tenemos en cada una <strong>de</strong> lasposiciones <strong>de</strong>l array no es un objeto <strong>de</strong> tipo Persona, si no una referencia a unobjeto <strong>de</strong> tipo Persona. Dicho <strong>de</strong> otro modo No se ha creado ningún objeto<strong>de</strong> la clase Persona, sólo referencias a objetos <strong>de</strong> ese tipo.La diferencia entre arrays <strong>de</strong> tipo primitivo y tipo referencia es muy importante.Mientras que en el caso <strong>de</strong> los arrays <strong>de</strong> tipo primitivo, una vez creadosya tenemos disponible en cada una <strong>de</strong> sus posiciones espacio para albergar unelemento <strong>de</strong>l tipo correspondiente, en los arrays <strong>de</strong> tipo referencia no se hacreado ninguna instancia <strong>de</strong> la clase correspondiente, lo único que se ha creadoes un <strong>con</strong>junto <strong>de</strong> referencias que podremos <strong>con</strong>ectar a objetos <strong>de</strong> la clasecorrespondiente, y estos objetos los habremos creado en otro lugar <strong>de</strong> nuestroprograma.Veamos esta diferencia <strong>con</strong> el siguiente ejemplo1 package agenda ;23 public c l a s s Arrays {45 /∗∗6 ∗ @param a r g s7 ∗/8 public s t a t i c void main ( S t r i n g [ ] a r g s ) {9 // TODO Auto−g e n e r a t e d method stub10 int a r r a y E n t e r o s [ ] = new int [ 1 0 ] ;11 Persona grupoPersonas [ ] = new Persona [ 1 0 ] ;12 // La s i g u i e n t e s e n t e n c i a e s v a l i d a13 System . out . p r i n t l n ( " V a l o r en a r r a y E n t e r o s [5]: " + a r r a y E n t e r o s [ 5 ] ) ;14 // Se produce un e r r o r , no hay nada en l a p o s i c i ó n [ 5 ]15 System . out . p r i n t l n ( " N o m b r e en p o s i c i ó n g r u p o P e r s o n a s [5]: " +grupoPersonas [ 5 ] . nombre ) ;1617 }1819 }Listado 2.8: Diferencia entre arrays <strong>de</strong> tipos primitivos y arrays <strong>de</strong> tiposreferenciaSi creas una nueva clase <strong>con</strong> el código <strong>de</strong>l Listado 2.8 y lo ejecutas (recuerda:botón <strong>de</strong>recho sobre el nombre <strong>de</strong> la clase en el Package Explorer, y luego Runas → <strong>Java</strong> Applications), obtendrás el siguiente error:Valor en arrayEnteros[5]: 0Exception in thread "main" java.lang.NullPointerExceptionat hola.Arrays.main(Arrays.java:15)En la posición 5 <strong>de</strong>l array <strong>de</strong> enteros tenemos un valor por <strong>de</strong>fecto, pero lareferencia que tenemos en la posición 5 <strong>de</strong>l array <strong>de</strong> tipo Persona es el valorpor <strong>de</strong>fecto null que en <strong>Java</strong> tiene el significado <strong>de</strong> Referencia no asignada.


36CAPÍTULO 2. CLASESSintaxisA los elementos <strong>de</strong> un array se acce<strong>de</strong> mediante el operador []. Dentro <strong>de</strong> esteoperador indicamos la posición <strong>de</strong>l elemento a la que <strong>de</strong>seamos acce<strong>de</strong>r.¿Cómo po<strong>de</strong>mos resolver el error anterior?. Simplemente asignando a la referenciaen la posición 5 <strong>de</strong>l array grupoPersonas una referencia a un objetoque haya sido creado:1 package agenda ;23 public c l a s s Arrays2 {45 /∗∗6 ∗ @param a r g s7 ∗/8 public s t a t i c void main ( S t r i n g [ ] a r g s ) {9 // TODO Auto−g e n e r a t e d method stub10 int a r r a y E n t e r o s [ ] = new int [ 1 0 ] ;11 Persona grupoPersonas [ ] = new Persona [ 1 0 ] ;12 grupoPersonas [ 5 ] = new Persona ( " J a m e s " , " G o s s l i n g " , " 555 123 456 " ) ;13 // La s i g u i e n t e s e n t e n c i a e s v a l i d a14 System . out . p r i n t l n ( " V a l o r en a r r a y E n t e r o s [5]: " + a r r a y E n t e r o s [ 5 ] ) ;15 // Se produce un e r r o r , no hay nada en l a p o s i c i ó n [ 5 ]16 System . out . p r i n t l n ( " N o m b r e en p o s i c i ó n g r u p o P e r s o n a s [5]: " +grupoPersonas [ 5 ] . nombre ) ;1718 }1920 }Si ejecutas el código <strong>con</strong> la modificación obtendrás el siguiente resultado:Valor en arrayEnteros[5]: 0Nombre en posición grupoPersonas[5]: JamesEn este último caso la referencia en la posición 5 <strong>de</strong>l array grupoPersonassí que hace referencia a un objeto, luego no hay problema al usarla para acce<strong>de</strong>ra su atributo nombre.Ya sabemos cómo acce<strong>de</strong>r a los elementos <strong>de</strong> un array, la pregunta que nossurge es ¿Cómo puedo recorrer todos los elementos <strong>de</strong> un array?. La respuestaes: ✭✭Usando estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> repetición✮✮2.4. Estructuras <strong>de</strong> <strong>con</strong>trol.<strong>Java</strong> es un lenguaje <strong>de</strong> programación estructurado, esto significa que <strong>Java</strong> proporcionaestructuras <strong>de</strong> <strong>con</strong>trol para <strong>de</strong>cidir el flujo <strong>de</strong> ejecución <strong>de</strong> nuestrosprogramas.Existen dos gran<strong>de</strong>s grupos <strong>de</strong> estructuras <strong>de</strong> <strong>con</strong>trol:Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> repetición: Nos permiten indicar si un <strong>de</strong>terminadobloque <strong>de</strong> código se <strong>de</strong>be ejecutar mas <strong>de</strong> una vez.Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> selección: Nos permiten especificar mas <strong>de</strong> unadirección <strong>de</strong> flujo <strong>de</strong>pendiendo <strong>de</strong> alguna <strong>con</strong>dición.


2.4. ESTRUCTURAS DE CONTROL. 372.4.1. Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> repetición.En <strong>Java</strong> existen tres estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> repetición:Bucle for.Bucle while.Bucle do...while.Las estructuras <strong>de</strong> repetición sirven para repetir una <strong>de</strong>terminada tareamientras se cumpla cierta <strong>con</strong>dición. En el caso <strong>de</strong> un array nos sirven pararecorrer los elementos almacenados en el array secuencialmente, para, por ejemplo,mostrar sus valores. Veamos como se usa cada una <strong>de</strong> estas estructuras <strong>de</strong>repetición.2.4.1.1. El bucle forSi <strong>con</strong>ocemos cual es el primer elementos y el último sobre los que queremositerar el bucle for es la manera más cómoda <strong>de</strong> recorrerlos todos. Su sintaxises la siguiente:SintaxisLa sintaxis <strong>de</strong>l bucle for es:for(inicio; <strong>con</strong>dición: incremento)Ejemplo 1: for(int i = 0; i


38CAPÍTULO 2. CLASES10 a r r a y E n t e r o s [ i ] = i ;11 // Lo r e c o r r e m o s y extraemos l a i n f o r m a c i ó n almacenada12 for ( int i : a r r a y E n t e r o s )13 System . out . p r i n t l n ( " a r r a y E n t e r o s [ " + i + " ] = " + a r r a y E n t e r o s [ i ] ) ;14 }1516 }El resultado <strong>de</strong> la ejecución <strong>de</strong> este código es el siguiente:arrayEnteros[0] = 0arrayEnteros[1] = 1arrayEnteros[2] = 2arrayEnteros[3] = 3arrayEnteros[4] = 4El primer bubcle for itera sobre las posiciones <strong>de</strong>l array almacenando losnúmeros 0 a 4 en las posiciones 0 a 4 <strong>de</strong>l array. El segundo bucle itera sobre lasposiciones <strong>de</strong>l array y muestra el valor almacenado en cada una <strong>de</strong> ellas.2.4.1.2. El bucle whileEn el caso <strong>de</strong>l bucle while, la <strong>con</strong>dición <strong>de</strong> parada se comprueba antes <strong>de</strong> cadaiteración y, si la <strong>con</strong>dición se cumple, se ejecuta el bloque <strong>de</strong>l bucle while.La sintaxis <strong>de</strong>l bucle while es:while(<strong>con</strong>dición) {Bloque <strong>de</strong> código}Sintaxis2.4.1.3. El bucle do...whileEn el caso <strong>de</strong>l bucle do...while la <strong>con</strong>dición se comprueba <strong>de</strong>spués <strong>de</strong> haberseejecutado al menos una vez el cuerpo <strong>de</strong>l bucle. La <strong>con</strong>dición se comprueba alfinal <strong>de</strong>l bucle.La sintaxis <strong>de</strong>l bucle do...while es:do {Bloque <strong>de</strong> código} while(<strong>con</strong>dición);SintaxisEstas tres estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> repetición son intercambiables, se pue<strong>de</strong>sustituir una por otra <strong>con</strong> pequeñas modificaciones. Elegir una u otra <strong>de</strong>pen<strong>de</strong><strong>de</strong> cada caso: si <strong>con</strong>ocemos el intervalo sobre el que queremos iterar el buclefor es el mas comodo <strong>de</strong> utilizar; si la <strong>con</strong>dición <strong>de</strong> parada no involucra el valor<strong>de</strong> una posicion po<strong>de</strong>mos utilizar el bucle while si necesitamos comprobar la<strong>con</strong>dición antes, o bien el bucle do...while si queremos ejecutar al menos unavez el bloque <strong>de</strong> código que encierra el bucle.


2.4. ESTRUCTURAS DE CONTROL. 392.4.2. Estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> selección.Las estructuras <strong>de</strong> <strong>con</strong>trol <strong>de</strong> selección nos permiten especificar mas <strong>de</strong> un posiblecamino a seguir por el flujo <strong>de</strong> ejecución <strong>de</strong> nuestro programa. La direcciónfinal que seguirá el flujo <strong>de</strong>pen<strong>de</strong>rá <strong>de</strong> si se cumple o no cierta <strong>con</strong>dición.2.4.2.1. Bifurcaciones <strong>con</strong> la sentencia if...else.La sentencia if nos permite ejecutar un bloque <strong>de</strong> código, o no, <strong>de</strong>pendiendo<strong>de</strong> cierta <strong>con</strong>dición. La <strong>con</strong>dición <strong>de</strong>be evaluarse a un valor booleano, es <strong>de</strong>cir,a true o false, como en el siguiente ejemplo <strong>de</strong> código:1 int e n t e r o ;2 i f ( e n t e r o % 2 == 0) System . out . p r i n t l n ( " El n ú m e r o es par . " ) ;3 e l s e System . out . p r i n t l n ( " El n ú m e r o es i m p a r . " ) ;Dentro <strong>de</strong>l bloque <strong>de</strong> código correspondiente al else po<strong>de</strong>mos añadir unanueva sentencia if, se dice entonces que las sentencias if están enca<strong>de</strong>nadas,como en el siguiente ejemplo:1 i f ( primeraCondicion ) {2 \\ Bloque <strong>de</strong> c ó d i g o3 } e l s e i f ( segundaCondicion ) {4 \\ Bloque <strong>de</strong> c ó d i g o5 } e l s e {6 \\ Bloque <strong>de</strong> c ó d i g o7 }Esto nos permite especificar más <strong>de</strong> un posible camino a seguir por el flujo<strong>de</strong> ejecución <strong>de</strong> nuestro código.2.4.2.2. Múltiples caminos <strong>con</strong> la sentencia switchExiste una <strong>con</strong>strucción <strong>de</strong>l lenguaje que nos permite especificar múltiples caminosa seguir por el flujo <strong>de</strong> ejecución <strong>de</strong> nuestro código: la sentencia switch. Eneste caso el camino a seguir se selecciona basándose en el valor <strong>de</strong> una expresiónque se evalua a un valor entero, como en el siguiente ejemplo:1 int mes = 1 ; // Correspon<strong>de</strong> a l mes <strong>de</strong> Enero2 swith ( mes ) {3 case 1 :4 System . out . p r i n t l n ( " El mes es E n e r o . " ) ;5 break ;6 case 2 :7 System . out . p r i n t l n ( " El mes es F e b r e r o . " ) ;8 break ;9 case 3 :10 System . out . p r i n t l n ( " El mes es M a r z o . " ) ;11 break ;12 <strong>de</strong>fault :13 System . out . p r i n t l n ( " N i n g u n o <strong>de</strong> los m e s e s a n t e r i o r e s . " ) ;14 break ;15 }En el ejemplo anterior, se evalua el valor <strong>de</strong> la variable mes, y se prueba cadauna <strong>de</strong> las opciones expresadas por un case. Cuando coinci<strong>de</strong>n los valores, seejecuta el código correspondiente al case hasta que se encuentra la sentenciabreak en cuyo momento se avandona el bloque <strong>de</strong> la sentencia switch. Exite una


40CAPÍTULO 2. CLASESopción Por <strong>de</strong>fecto etiquetada como <strong>de</strong>fault que es opcional y cuyo código seejecutará si la expresión entera no coinci<strong>de</strong> <strong>con</strong> ninguno <strong>de</strong> los case anteriores.Es importante hacer notar que una vez que se encuentra una coinci<strong>de</strong>nciaentre la expresión entera y un case, se ejecuta su código correspondiente hastaen<strong>con</strong>trar la sentencia break. Esto nos permite obviar esta sentencia si queremosque varios case distintos ejecuten el mismo segmento <strong>de</strong> código, como en elsiguiente ejemplo:1 i n t mes = 1 ; // Correspon<strong>de</strong> a l mes <strong>de</strong> Enero2 s w i t c h ( mes ) {3 c a s e 1 :4 c a s e 3 :5 c a s e 5 :6 c a s e 7 :7 c a s e 8 :8 c a s e 1 0 :9 c a s e 1 2 :10 System . out . p r i n t l n (” El mes t i e n e 31 d í a s . ” ) ;11 break ;12 c a s e 4 :13 c a s e 6 :14 c a s e 9 :15 c a s e 1 1 :16 System . out . p r i n t l n (” El mes t i e n e 30 d í a s . ” ) ;17 break :18 d e f a u l t :19 System . out . p r i n t l n (” El mes e s Febrero . ” ) ;20 break ;21 }En el ejemplo anterior los meses cuyo ordinal es 1, 3, 5, 7, 8, 10 o 12 tienen31 días, todos los case correspondientes, excepto el <strong>de</strong> valor 12, no incluye lasentencia break por lo que en todos los casos, al seleccionar uno <strong>de</strong> ellos seejecutar la sentencia <strong>de</strong> la línea 10. Lo mismo ocurrirá si el ordinal <strong>de</strong>l mes es4, 6, 9 u 11, en todos los casos se ejecutará la sentencia <strong>de</strong> la línea 16.2.5. Modificadores <strong>de</strong> acceso.Ahora ya estamos en situación <strong>de</strong> volver a la pregunta: ¿Qué sentido tiene <strong>de</strong>clararmétodos <strong>de</strong> acceso a los atributos <strong>de</strong> una clase si puedo acce<strong>de</strong>r directamentea ellos? La repuesta es que, como regla general, nunca <strong>de</strong>bemos hacer visibleslos atributos <strong>de</strong> nuestras clases, sólo <strong>de</strong>ben ser visibles <strong>de</strong>s<strong>de</strong> el interior <strong>de</strong> lasclases. Como resultado, para acce<strong>de</strong>r a los valores <strong>de</strong> los atributos utilizaremosmétodos. Esta regla es una manera <strong>de</strong> expresar el <strong>con</strong>cepto <strong>de</strong> Encapsulación,una <strong>de</strong> las piezas centrales <strong>de</strong> la programación orientada a objetos.ConceptoLas clases encapsulan atributos y métodos <strong>de</strong> tal modo que sólo se hace visibleuna parte <strong>de</strong> esos atributos y métodos, los estrictamente necesarios para quepodamos trabajar <strong>con</strong> las instancias <strong>de</strong> esa clase.La respuesta a la pregunta anterior hace surgir una nueva: ¿Cómo restrinjola visibilidad <strong>de</strong> los atributos <strong>de</strong> una clase?, la respuesta es: mediante losModificadores <strong>de</strong> acceso.


2.5. MODIFICADORES DE ACCESO. 41DefiniciónLos modificadores <strong>de</strong> acceso son palabras reservadas <strong>de</strong> <strong>Java</strong> mediante las cualesrestringimos la visibilidad <strong>de</strong> los atributos y métodos <strong>de</strong> una clase.En <strong>Java</strong> un modificador <strong>de</strong> acceso está representado por una palabra reservadaque me permite <strong>de</strong>finir la visibilidad <strong>de</strong> un atributo o un método <strong>de</strong> laclase. Los cuatro modificadores <strong>de</strong> acceso que po<strong>de</strong>mos utilizar en <strong>Java</strong> son:private.protected.✭✭Vacío✮✮ (no escribimos nada).public.De momento, y hasta que veamos el capítulo <strong>de</strong> herencia, vamos a ver elsignificado <strong>de</strong> los dos más sencillos: private y public. Los modificadores <strong>de</strong>acceso se escriben antes <strong>de</strong>l tipo <strong>de</strong>l atributo o antes <strong>de</strong>l tipo <strong>de</strong> retorno <strong>de</strong>lmétodo. Veamos cómo quedaría nuestra clase Persona asignando la visibilidada<strong>de</strong>cuada a cada uno miembros:1 package agenda ;23 public c l a s s Persona {4 S t r i n g nombre ;5 S t r i n g a p e l l i d o s ;6 S t r i n g t e l e f o n o ;78 Persona ( ) { }910 Persona ( 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 ) {11 t h i s . nombre = nombre ;12 t h i s . a p e l l i d o s = a p e l l i d o s ;13 t h i s . t e l e f o n o = t e l e f o n o ;14 }1516 S t r i n g getNombre ( ) {17 return nombre ;18 }1920 S t r i n g g e t A p e l l i d o s ( ) {21 return a p e l l i d o s ;22 }2324 S t r i n g g e t T e l e f o n o ( ) {25 return t e l e f o n o ;26 }27 }En este caso estamos restringiendo la visibilidad <strong>de</strong> los atributos <strong>de</strong> la clasePersona <strong>de</strong> modo que únicamente son visibles <strong>de</strong>s<strong>de</strong> el interior <strong>de</strong> la propia clasedon<strong>de</strong> se han <strong>de</strong>finido (modificador private). Por otro lado, estamos haciendovisibles los métodos <strong>de</strong> la clase a cualquier otra clase que los quiera utilizar(modificador public).


42CAPÍTULO 2. CLASESBuenas prácticas y <strong>con</strong>vencionesEn general, se <strong>con</strong>si<strong>de</strong>ra una buena práctica <strong>de</strong>clarar los atributos <strong>de</strong> una clasecomo privados (private) y si necesitamos acce<strong>de</strong>r a ellos para leer sus valores omodificarlos utilizaremos los métodos get o set. En caso <strong>de</strong> que el tipo <strong>de</strong>l valor<strong>de</strong>vuelto sea boolean se utilizará is en vez <strong>de</strong> set, por ejemplo isNuevo() envez <strong>de</strong> getNuevo() si el valor que se retorna es un boolean (true o false).A<strong>de</strong>más <strong>de</strong> los modificadores que nos permiten <strong>de</strong>finir la visibilidad <strong>de</strong> atributosy métodos <strong>de</strong> una clase, en <strong>Java</strong> existen otros modificadores que tambiénse pue<strong>de</strong>n aplicar sobre la <strong>de</strong>finición <strong>de</strong> atributos y métodos: static y final.2.6. Modificadores static y final.Un atributo <strong>de</strong> una clase se pue<strong>de</strong> modificar <strong>con</strong> la palabra reservada static,<strong>con</strong> ello indicamos que el atributo no pertenece a las instancias <strong>de</strong> la clase si noa la propia clase. ¿Qué quiere <strong>de</strong>cir esto?, pues que no existe una copia <strong>de</strong> eseatributo en cada uno <strong>de</strong> los objetos <strong>de</strong> la clase, si no que existe una única copiaque es compartida por todos los objetos <strong>de</strong> la clase. Por ello, a los atributosstatic se les llama atributos <strong>de</strong> la clase.Una <strong>con</strong>secuencia <strong>de</strong> lo anterior es que para acce<strong>de</strong>r a los atributos static<strong>de</strong> una clase no necesitamos crear una instancia <strong>de</strong> la clase, po<strong>de</strong>mos acce<strong>de</strong>r aellos a través <strong>de</strong>l nombre <strong>de</strong> la clase.De igual modo, po<strong>de</strong>mos modificar los métodos <strong>de</strong> una clase <strong>con</strong> la palabrareserva static. A estos métodos se les llama métodos <strong>de</strong> la clase, y, al igual que<strong>con</strong> los atributos static, po<strong>de</strong>mos usarlos a través <strong>de</strong>l nombre <strong>de</strong> la clase, sinnecesidad <strong>de</strong> crear ninguna instancia <strong>de</strong> la clase. Pero existe una restricción, losmétodos estáticos <strong>de</strong> una clase sólo pue<strong>de</strong>n acce<strong>de</strong>r a atributos estáticos u otrosmétodos estáticos <strong>de</strong> la clase, pero nunca a atributos o métodos que no lo sean.¿Ves porqué? ¿Qué ocurriría si <strong>de</strong>s<strong>de</strong> un método estático y usando el nombre<strong>de</strong> la clase intentases acce<strong>de</strong>r a un atributo <strong>de</strong> instancia <strong>de</strong> la clase?En el siguiente ejemplo <strong>de</strong> código hemos añadido un <strong>con</strong>tador para saber elnúmero <strong>de</strong> instancias <strong>de</strong> la clase Persona que se han creado:1 package t i p o s ;23 public c l a s s Persona implements Contacto {4 private S t r i n g nombre ;5 private S t r i n g a p e l l i d o s ;6 private S t r i n g t e l e f o n o ;7 private s t a t i c int n I n s t a n c i a s ;89 public Persona ( ) {10 super ( ) ;11 i n i c i a A t r i b u t o s ( ) ;12 }1314 public s t a t i c int g e t N I n s t a n c i a s ( ) {15 return n I n s t a n c i a s ;16 }Fíjate que el método getNInstancias() que acce<strong>de</strong> al atributo nInstanciases estático. En el siguiente ejemplo <strong>de</strong> código se está utilizando este métodoestático a través <strong>de</strong>l nombre <strong>de</strong> la clase y a través <strong>de</strong> una instancia <strong>con</strong>creta:


2.7. EL RECOLECTOR DE BASURA. 431 public f i n a l c l a s s P r i n c i p a l {2 private P r i n c i p a l ( ) {3 super ( ) ;4 }56 private void e j e c u t a ( ) {7 Persona unaPersona = new Persona ( ) ;8 // Acce<strong>de</strong>mos a l método a t r a v é s <strong>de</strong> l a c l a s e9 System . out . p r i n t l n ( " N ú m e r o <strong>de</strong> p e r s o n a s c r e a d a s : " + Persona .g e t N I n s t a n c i a s ( ) ) ;10 Persona o t r a P e r s o n a = new Persona ( " J a m e s " , " G o s s l i n g " , " 555 123 456 " ) ;11 // Acce<strong>de</strong>mos a l método a t r a v é s <strong>de</strong> una i n s t a n c i a c o n c r e t aCuando un atributo <strong>de</strong> una clase los modificamos en su <strong>de</strong>finición <strong>con</strong> lapalabra reservada final, estamos indicando que ese atributo no pue<strong>de</strong> cambiar<strong>de</strong> valor, por ejemplo:1 private f i n a l S t r i n g a utor = " Ó s c a r " ;Una vez <strong>de</strong>finido, este atributo no pue<strong>de</strong> cambiar <strong>de</strong> valor, si lo intentásemoscambiar el compilador nos daría un error.Muchas veces los modificadores static y final se utilizan en combinaciónpara <strong>de</strong>finir <strong>con</strong>stantes, como en el siguiente ejemplo:1 public c l a s s Constantes {2 public s t a t i c f i n a l double PI = 3 . 1 4 1 5 9 2 ;3 . . .4 }De este modo, la <strong>con</strong>stante es accesible <strong>de</strong>s<strong>de</strong> cualquier otra clase (al serpublic) y po<strong>de</strong>mos leerla a través <strong>de</strong>l nombre <strong>de</strong> la clase <strong>de</strong> este modoConstantes.PI, pero si por <strong>de</strong>scuido intentamos modificarla, el compilador <strong>de</strong><strong>Java</strong> nos dará un error.Regla <strong>de</strong> <strong>con</strong>venciónLos nombre <strong>de</strong> las <strong>con</strong>stantes se escriben en mayúsculas.El modificador final también se pue<strong>de</strong> usar sobre un método o sobre laclase. Veremos <strong>con</strong> <strong>de</strong>talle lo que esto significa en el Capítulo 3 <strong>de</strong>dicado a laherencia en <strong>Java</strong>.2.7. El recolector <strong>de</strong> basura.Hemos visto que para crear instancias <strong>de</strong> una clase utilizamos el operador new.Cuando ya no necesitamos una instancia: ¿Cómo liberamos el espacio en memoriaque está ocupando? En <strong>Java</strong> no existe ningún operador especial para eliminar<strong>de</strong> la memoria las instancias que no vamos a seguir utilizando. Para liberar lamemoria existe un mecanismo mucho más potente, el Recolector <strong>de</strong> basura.Como ya sabes, el modo <strong>de</strong> acce<strong>de</strong>r a los objetos en <strong>Java</strong> es mediante las variables<strong>de</strong> tipo referencia. El recolector <strong>de</strong> basura <strong>con</strong>oce en todo momento todaslas referencia que una instancia posee, y <strong>de</strong> igual modo <strong>con</strong>oce cuando una instanciaha perdido todas las referencias que apuntaban a ella. Si un objeto pier<strong>de</strong>


44CAPÍTULO 2. CLASEStodas la referencias que apuntan a él y las referencias son el único mecanismoque tenemos <strong>de</strong> acce<strong>de</strong>r a los objetos, significa que ya no podremos acce<strong>de</strong>r aese objeto, <strong>de</strong> modo que el recolector <strong>de</strong> basura pue<strong>de</strong> hacer su trabajo: liberarla memoria ocupada por la instancia.¿Cómo po<strong>de</strong>mos marcar un objeto para que sea borrado <strong>de</strong> memoria? Unatécnica sencilla es eliminar todas las referencias que apuntan a él como en elsiguiente ejemplo:1 Persona u n a R e f e r e n c i a = new Persona ( ) ; // Esta Persona t i e n e unar e f e r e n c i a h a c i a e l l a2 Persona o t r a R e f e r e n c i a = u n a R e f e r e n c i a ; // Ahora t i e n e dos3 u n a R e f e r e n c i a = null ; // Le <strong>de</strong>s<strong>con</strong>ectamos l a primera r e f e r e n c i a4 o t r a R e f e r e n c i a = null ; // Le <strong>de</strong>s<strong>con</strong>ectamos l a segunda r e f e r e n c i a5 // El r e c o l e c t o r <strong>de</strong> basura ya pue<strong>de</strong> h a c e r su t r a b a j o2.8. Finalización.En la sección anterior hemos visto cual es el mecanismo que utiliza <strong>Java</strong> parair liberando <strong>de</strong> la memoria los objetos que ya no son accesibles. Todos los objetosposeen un método <strong>con</strong> la siguiente signatura protected void finalize()throws Throwable, en los capítulos siguientes veremos <strong>con</strong> <strong>de</strong>talle el significado<strong>de</strong> las palabras reservadas protected y throws, así como la clase Throwable,lo que nos interesa en este momento es saber que este es el último método <strong>de</strong>cualquier objeto que se llama antes <strong>de</strong> que el recolector <strong>de</strong> basura elimine lainstancia <strong>de</strong> la memoria. Dicho <strong>de</strong> otro modo, el método finalize es la últimaoportunidad que tenemos como programadores para que nuestras instanciasacaben limpiamente. Veamos qué quiere <strong>de</strong>cir esto <strong>con</strong> más <strong>de</strong>talle.Supón que has escrito un clase que abre un fichero para lectura (veremosacceso a ficheros en el capítulo 7), y que por cualquier motivo una instancia<strong>de</strong> esta clase pier<strong>de</strong> todas las referencias que apuntan a ella. Cuando actuaseel recolector <strong>de</strong> basura, eliminaría esta instancia <strong>de</strong> la memoria y el resultadocolateral sería que el fichero quedaría abierto. Para que esto no ocurra, en elmétodo finalize po<strong>de</strong>mos escribir el código que cierre los ficheros que estánabiertos, ya que sabemos que este método será llamado antes <strong>de</strong> eliminar lainstancia <strong>de</strong> memoria.Pero no es tan inmediato, el problema que <strong>con</strong>lleva <strong>de</strong>legar al métodofinalize estas tareas <strong>de</strong> limpieza segura antes <strong>de</strong> acabar es que no sabemoscuando el recolector <strong>de</strong> basura va a hacer su trabajo, sabemos que lo hará, perono sabemos cuando. Y aunque po<strong>de</strong>mos ✭✭forzar✮✮ la actuación <strong>de</strong>l recolector <strong>de</strong>basura <strong>de</strong> este modo:1 Runtime r = Runtime . getRuntime ( ) ;2 r . gc ( ) ; // S o l i c i t a m o s que e l r e c o l e c t o r <strong>de</strong> basura e n t r e en a c c i ó n .no se garantiza que el recolector <strong>de</strong> basura vaya a ser invocado inmediatamente.Luego, como norma general:


2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACIÓN. 45Buenas prácticasNo <strong>de</strong>bemos <strong>de</strong>legar en el recolector <strong>de</strong> basura la limpieza que han <strong>de</strong> realizarnuestras clases cuando sus instancias son eliminadas <strong>de</strong> memoria.2.9. Comentarios. Comentarios <strong>de</strong> documentación.Todos los programadores son <strong>con</strong>scientes <strong>de</strong> la importancia <strong>de</strong> documentar sutrabajo. Una tarea <strong>de</strong> documentación es incluir comentarios en el propio códigopara que otros programadores puedan <strong>con</strong>ocer en el momento <strong>de</strong> la lectura <strong>de</strong>código los <strong>de</strong>talles <strong>de</strong> implementación.Para realizar tareas <strong>de</strong> documentación <strong>Java</strong> nos proporciona tres tipos <strong>de</strong>comentarios:1. Comentarios <strong>de</strong> una única línea.2. Comentarios <strong>de</strong> más <strong>de</strong> una línea.3. Comentarios <strong>de</strong> documentación.Los comentarios <strong>de</strong> una única línea empiezan <strong>con</strong>y el texto <strong>de</strong>l comentario restringe su extensión a una única línea.Los comentarios <strong>de</strong> más <strong>de</strong> una línea empiezan <strong>con</strong> /*, el texto <strong>de</strong>l comentariopue<strong>de</strong> ocupar cuantas líneas necesitamos, pero es necesario indicar queel comentario acaba insertando al final */. En el siguiente ejemplo pue<strong>de</strong>s vercómo se usan ambos tipos <strong>de</strong> comentario:1 public c l a s s Persona {2 // e s t o e s un comentario <strong>de</strong> una ú n i c a l í n e a3 private S t r i n g nombre ;4 /∗ Este comentario ocupa5 más <strong>de</strong> una l í n e a6 <strong>de</strong> c ó d i g o ∗/7 private S t r i n g a p e l l i d o s ;Pero, sin duda, los comentarios <strong>de</strong> documentación son una herramienta realmentepotente en <strong>Java</strong>. Los comentarios <strong>de</strong> documentación se incluyen en elcódigo y nos sirven para, a partir <strong>de</strong> ellos, crear documentación <strong>de</strong> nuestro códigoen formato html. En los comentarios <strong>de</strong> documentación po<strong>de</strong>mos añadir etiquetasque nos permiten enriquecer la documentación generada. Veamos cómose introducen los comentarios <strong>de</strong> documentación y las etiquetas que tenemosdisponibles.Un comentario <strong>de</strong> documentación siempre <strong>de</strong>be empezar por /**, nota quetras la barra se escriben dos asteriscos, y <strong>de</strong>be acabar por */, como los comentarios<strong>de</strong> más <strong>de</strong> una línea.Dentro <strong>de</strong> los comentarios <strong>de</strong> documentación po<strong>de</strong>mos utilizar etiquetas paraañadir información que enriquezca la documentación generada. Por ejemplo,po<strong>de</strong>mos utilizar la etiqueta para indicar quien es el autor <strong>de</strong>l código <strong>de</strong> unaclase, como en el siguiente ejemplo:1 /∗∗ Implementación <strong>de</strong> l a c l a s e Persona


46CAPÍTULO 2. CLASES2 ∗ Esta c l a s e d e s c r i b e a un nuevo c o n t a c t o3 ∗ en una agenda <strong>de</strong> t e l é f o n o s4 ∗ @author Óscar Belmonte Fernán<strong>de</strong>z5 ∗ @version 1 . 06 ∗/78 public c l a s s Persona {9 private S t r i n g nombre ;otros comentarios <strong>de</strong> documentación:@version Indicamos la versión <strong>de</strong>l código.@param nombre Descripción <strong>de</strong>l parámetro.@return Significado <strong>de</strong>l valor <strong>de</strong> retorno.@<strong>de</strong>precated Razón <strong>de</strong> por qué este método está obsoleto.@see #metodo() Referencia cruzada al método.@exception Excepción que se pue<strong>de</strong> producir en el método@throws Excepción no gestionadaA<strong>de</strong>más, en los comentarios <strong>de</strong> documentación po<strong>de</strong>mos incluir código HTML.En el listado 2.9 tienes la clase Persona documentada.1 package persona . c o m e n t a r i o s ;23 /∗∗ Implementación <strong>de</strong> l a c l a s e Persona4 ∗ Esta c l a s e d e s c r i b e a un nuevo c o n t a c t o5 ∗ en una agenda <strong>de</strong> t e l é f o n o s6 ∗ @author Óscar Belmonte Fernán<strong>de</strong>z7 ∗ @version 1 . 08 ∗/910 public c l a s s Persona {11 private S t r i n g nombre ;12 private S t r i n g a p e l l i d o s ;13 private S t r i n g t e l e f o n o ;14 private s t a t i c int n I n s t a n c i a s = 0 ;1516 /∗∗17 ∗ C o n s t r u c t o r por d e f e c t o18 ∗/19 public Persona ( ) {20 n I n s t a n c i a s ++;21 }2223 /∗∗24 ∗ C o n s t r u c t o r <strong>con</strong> parámetros .25 ∗ En nuevas 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 á n26 ∗ inmutables , no e x i s t i r á n métodos para c a m o b i a r l o s27 ∗ @param nombre Nombre d e l nuevo c o n t a c t o28 ∗ @param a p e l l i d o s A p e l l i d o s d e l nuevo c o n t a c t o29 ∗ @param t e l e f o n o T e l é f o n o d e l nuevo c o n t a c t o30 ∗/31 public Persona ( S t r i n g nombre , S t r i n g a p e l l i d o s , S t r i n g t e l e f o n o ) {32 t h i s . nombre = nombre ;33 t h i s . a p e l l i d o s = a p e l l i d o s ;34 t h i s . t e l e f o n o = t e l e f o n o ;35 n I n s t a n c i a s ++;36 }3738 /∗∗39 ∗ Devuelve e l número <strong>de</strong> i n s t a n c i a s c r e a d a s40 ∗ @return El número <strong>de</strong> i n s t a n c i a s


2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACIÓN. 4741 ∗/42 public s t a t i c int g e t N I n s t a n c i a s ( ) {43 return n I n s t a n c i a s ;44 }4546 /∗∗47 ∗ Devuelve e l nombre d e l c o n t a c t o48 ∗ @return Nombre d e l c o n t a c t o49 ∗/50 public S t r i n g getNombre ( ) {51 return nombre ;52 }5354 /∗∗55 ∗ Devuelve l o s a p e l l i d o s d e l c o n t a c o t56 ∗ @return A p e l l i d o s d e l c o n t a c t o57 ∗/58 public S t r i n g g e t A p e l l i d o s ( ) {59 return a p e l l i d o s ;60 }6162 /∗∗63 ∗ Devuelve e l número <strong>de</strong> t e l é f o n o d e l c o n t a c t o64 ∗ @return Número <strong>de</strong> t e l é f o n o d e l c o n t a c t o65 ∗/66 public S t r i n g g e t T e l e f o n o ( ) {67 return t e l e f o n o ;68 }6970 /∗∗71 ∗ Cambia e l nombre d e l c o n t a c t o72 ∗ @param nombre El nuevo nombre d e l c o n t a c t o73 ∗ @<strong>de</strong>precated Este método s e e l i m i n a r á en v e r s i o n e s f u t u r a s74 ∗ @see Persona ( S t r i n g nombre , S t r i n g a p e l l i d o s , S t r i n g t e l e f o n o )75 ∗/76 public void setNombre ( S t r i n g nombre ) {77 t h i s . nombre = nombre ;78 }7980 /∗∗81 ∗ Cambia l o s a p e l l i d o s d e l c o n t a c t o82 ∗ @param a p e l l i d o s Los nuevos a p e l l i d o s d e l c o n t a c t o83 ∗ @<strong>de</strong>precated Este método s e e l i m i n a r á en v e r s i o n e s f u t u r a s84 ∗ @see #Persona ( S t r i n g nombre , S t r i n g a p e l l i d o s , S t r i n g t e l e f o n o )85 ∗/86 public void s e t A p e l l i d o s ( S t r i n g a p e l l i d o s ) {87 t h i s . a p e l l i d o s = a p e l l i d o s ;88 }8990 /∗∗91 ∗ Cambia e l número <strong>de</strong> t e l é f o n o d e l c o n t a c t o92 ∗ @param t e l e f o n o El nuevo número <strong>de</strong> t e l é f o n o d e l c o n t a c t o93 ∗/94 public void s e t T e l e f o n o ( S t r i n g t e l e f o n o ) {95 t h i s . t e l e f o n o = t e l e f o n o ;96 }97 }Listado 2.9: Código fuente <strong>de</strong> la clase Persona <strong>con</strong> comentarios <strong>de</strong>documentación.El paquete <strong>de</strong> <strong>de</strong>sarrollo <strong>Java</strong> nos proporcionan una herramienta para generarla documentación <strong>de</strong> nuestras clases en formato HTML a partir <strong>de</strong>l código.Esta herramienta se llama javadoc. La generación <strong>de</strong> código se pue<strong>de</strong> realizar<strong>de</strong>s<strong>de</strong> <strong>con</strong>sola <strong>de</strong> este modo:javadoc Persona.java /ruta/a/directorioSi no se especifica la ruta la documentación se generará en el directorio don<strong>de</strong>se encuentre el fichero Persona.java.


48CAPÍTULO 2. CLASESFigura 2.1:javadoc.Comentarios <strong>de</strong> documentación generados <strong>con</strong> la herramientaDes<strong>de</strong> Eclipse también po<strong>de</strong>mos generar la documentación <strong>de</strong> nuestras claseshaciendo click <strong>con</strong> el botón <strong>de</strong>recho <strong>de</strong>l ratón sobre el fichero <strong>de</strong> la clase y <strong>de</strong>spuésseleccionamos Export → <strong>Java</strong> <strong>Java</strong>doc. Se generará toda la documentación <strong>de</strong>nuestra clase, o todo el proyecto si lo hemos seleccionado en vez <strong>de</strong> una claseindividual. En la figura 2.1 se muestra cual es el aspecto <strong>de</strong> la documentacióngenerada cuando se utiliza un navegador web para su visualización.Ejercicios.1. Escribe una clase que abstraiga la i<strong>de</strong>a <strong>de</strong> Empresa. Una empresa pue<strong>de</strong>tener únicamente como atributos su nombre y un teléfono <strong>de</strong> <strong>con</strong>tacto.2. Aña<strong>de</strong> comentarios <strong>de</strong> documentación a la clase Empresa y genera la documentación<strong>de</strong> esta clase.3. Escribe un sencillo programa para gestionar los <strong>con</strong>tactos <strong>de</strong> una agendatelefónica (clase Agenda). Las operaciones básicas que la agenda telefónicaes capaz <strong>de</strong> realizar son:


2.9. COMENTARIOS. COMENTARIOS DE DOCUMENTACIÓN. 49a) Insertar nuevas Personas en la agenda.b) Listar todas las Personas <strong>de</strong> la agenda.c) Buscar una Persona a partir <strong>de</strong> su nombre.Lecturas recomendadas.El excelente libro <strong>de</strong> Arnold y otros [2] es una referencia completa a la<strong>de</strong>finición <strong>de</strong> clases. Comenta <strong>de</strong> modo exhaustivo todos los <strong>de</strong>talles en la<strong>de</strong>finición <strong>de</strong> una clase, los distintos modificadores <strong>de</strong> acceso y su significado.El modo <strong>de</strong> presentar los <strong>con</strong>ceptos <strong>de</strong>l lenguaje <strong>Java</strong> ne la serie <strong>de</strong> libros <strong>de</strong>la colección Head first <strong>Java</strong> es muy interesante. En particular, la referencia[3] presenta <strong>de</strong> una manera muy visual los principales <strong>con</strong>ceptos en la<strong>de</strong>finición <strong>de</strong> clases en <strong>Java</strong>.Finalmente, una lectura siempre recomendable sobre buenas prácticas yescritura <strong>de</strong> código limpia es el libro <strong>de</strong> Robert C. Martin <strong>de</strong> la referencia[10].


50CAPÍTULO 2. CLASES


Capítulo 3Herencia e InterfacesContenidos3.1. Herencia. . . . . . . . . . . . . . . . . . . . . . . . . 523.2. Extensión <strong>de</strong> una clase. . . . . . . . . . . . . . . . . 523.2.1. Sobrescribir atributos. . . . . . . . . . . . . . . . . . 543.2.2. Sobrescribir métodos. . . . . . . . . . . . . . . . . . 563.2.3. La palabra reservada super. . . . . . . . . . . . . . . 593.2.4. El <strong>con</strong>structor por <strong>de</strong>fecto y la clase Object. . . . . 593.2.5. El operador instanceof. . . . . . . . . . . . . . . . . 603.2.6. El modificador final. . . . . . . . . . . . . . . . . . 613.2.7. Métodos static. . . . . . . . . . . . . . . . . . . . . 623.3. Clases abstractas. . . . . . . . . . . . . . . . . . . . 633.4. Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . 653.5. Enumeraciones. . . . . . . . . . . . . . . . . . . . . . 683.6. Paquetes en <strong>Java</strong>. . . . . . . . . . . . . . . . . . . . 693.7. Clases e interface anidados . . . . . . . . . . . . . 71IntroducciónEn el capítulo 2 hemos visto cómo po<strong>de</strong>mos codificar la abstracción <strong>de</strong> unaentidad <strong>de</strong>l mundo real a código <strong>Java</strong>. Hemos visto la sintaxis <strong>de</strong> <strong>Java</strong> para lacreación <strong>de</strong> clases, atributos y métodos, a<strong>de</strong>más <strong>de</strong> algunos <strong>de</strong> los modificadoresque po<strong>de</strong>mos utilizar sobre ellos. También hemos aprendido a crean instanciasa partir <strong>de</strong> una clase <strong>con</strong> el operador new.La situación en la que nos en<strong>con</strong>tramos ahora es que nuestras clases abstraenentida<strong>de</strong>s <strong>de</strong>l mundo real pero, ¿Qué ocurre si quiero añadir más funcionalidada mis clases?, ¿Cómo puedo añadir nuevos atributos a una clase ya existente?,¿Cómo puedo ampliar el <strong>con</strong>junto <strong>de</strong> métodos que proporciona?En POO existe un mecanismo fundamental para añadir funcionalidad a unaclase el <strong>con</strong>cepto es la Herencia, también <strong>con</strong>ocido como Extensión o Derivación.La i<strong>de</strong>a básica <strong>de</strong> la Herencia es crear una nueva clase a partir <strong>de</strong> la <strong>de</strong>finición<strong>de</strong> otra clase ya existente. La Herencia nos permite <strong>con</strong>struir una clase añadien-51


52CAPÍTULO 3. HERENCIA E INTERFACESdo únicamente la nueva funcionalidad a otra clase ya existente. Y también nospermite modificar el comportamiento <strong>de</strong> la clase original sobrescribiendo susmétodos.3.1. Herencia.En esta sección vamos a ver cómo se pue<strong>de</strong> ampliar el comportamiento <strong>de</strong>una clase a través <strong>de</strong> la herencia. Veremos también el <strong>con</strong>cepto, muy importante,<strong>de</strong> la vinculación dinámica para en<strong>con</strong>trar qué método se ha <strong>de</strong> invocar al utilizarreferencias a clases extendidas, así como el uso <strong>de</strong>l operador instanceof.Finalmente veremos el significado <strong>de</strong> los modificadores final y abstract cuandose aplican a la <strong>de</strong>finición <strong>de</strong> un método o una clase.3.2. Extensión <strong>de</strong> una clase.Lo primero que hay que <strong>de</strong>stacar es que en <strong>Java</strong> sólo está permitida la herenciasimple, una nueva clase sólo pue<strong>de</strong> exten<strong>de</strong>r a una única clase base. Dicho <strong>de</strong>otro modo, una clase hija no pue<strong>de</strong> tener más <strong>de</strong> una clase padre.Característica<strong>Java</strong> sólo admite herencia simple. Una clase no pue<strong>de</strong> tener más <strong>de</strong> una clasepadre.Imaginemos que necesitamos ampliar nuestra clase Persona, para añadirlenueva funcionalidad. Queremos que nuestra nueva clase <strong>con</strong>tenga la provincia,la población <strong>de</strong> resi<strong>de</strong>ncia y la edad 1 <strong>de</strong> cada uno <strong>de</strong> nuestros <strong>con</strong>tactos. Paraello tenemos dos alternativas antagónicas:1. Reescribir la clase <strong>de</strong>s<strong>de</strong> cero.2. Aprovechar al máximo el código existente.Si optamos por la primera opción, no tenemos nada nuevo que apren<strong>de</strong>r, <strong>con</strong>lo aprendido hasta ahora po<strong>de</strong>mos resolverlo.Si optamos por la segunda opción estaremos haciendo uso <strong>de</strong>l mecanismo <strong>de</strong>Herencia. Veamos como se utiliza este mecanismo y cual es su sintaxis en <strong>Java</strong>.En <strong>Java</strong> se dice que una clase extien<strong>de</strong> a otra clase cuando aña<strong>de</strong> más funcionalidada una clase ya existente. A la nueva clase se le llama clase hija oextendida, a la clase original se le llama clase padre o base. Para indicar queuna clase extien<strong>de</strong> el comportamiento <strong>de</strong> otra utilizamos la palabra reservadaextends.Supongamos que queremos ampliar la <strong>de</strong>finición <strong>de</strong> una Persona para que<strong>con</strong>tenga datos <strong>de</strong> su lugar <strong>de</strong> resi<strong>de</strong>ncia, como son la Provincia y la Poblacióny también la Edad, y llamemos a esta nueva clase Ciudadano. en <strong>Java</strong> lo hacemos<strong>de</strong> este modo:1 Cuando veamos las clases para manipular fechas en el Capítulo 8 veremos una mejorimplementación para obtener la edad <strong>de</strong> una persona a partir <strong>de</strong> su fecha <strong>de</strong> nacimiento


3.2.EXTENSIÓN DE UNA CLASE. 531 public c l a s s Ciudadano extends Persona {2 // D e f i n i c i ó n <strong>de</strong> l a nueva c l a s e e x t e n d i d a3 }En la <strong>de</strong>finición <strong>de</strong> la nueva clase po<strong>de</strong>mos incluir nuevos atributos y métodos.En el siguiente código <strong>de</strong> ejemplo, a la clase Ciudadano se le han añadido lostres nuevos atributos antes mencionados y los getters y setters para estos nuevosatributos. Nuestra nueva clase Ciudadano posee tanto los nuevos métodos <strong>de</strong>finidosen ella como los métodos <strong>de</strong>finidos en su clase padre (<strong>con</strong> las restricciones<strong>de</strong> accesibilidad que veremos en la sección 3.6.En el Listado 3.1 aparece la <strong>de</strong>finición completa <strong>de</strong> la nueva clase Ciudadano.1 package t i p o s ;23 public c l a s s Ciudadano extends Persona {4 private S t r i n g p o b l a c i o n ;5 private S t r i n g p r o v i n c i a ;6 private int edad ;78 public Ciudadano ( ) {9 super ( ) ;10 i n i c i a A t r i b u t o s ( ) ;11 }1213 @Overri<strong>de</strong>14 protected void i n i c i a A t r i b u t o s ( ) {15 setNombre ( " Un n o m b r e " ) ;16 edad = 0 ;17 }1819 public S t r i n g g e t P o b l a c i o n ( ) {20 return p o b l a c i o n ;21 }2223 public void s e t P o b l a c i o n ( S t r i n g p o b l a c i o n ) {24 t h i s . p o b l a c i o n = p o b l a c i o n ;25 }2627 public S t r i n g g e t P r o v i n c i a ( ) {28 return p r o v i n c i a ;29 }3031 public void s e t P r o v i n c i a ( S t r i n g p r o v i n c i a ) {32 t h i s . p r o v i n c i a = p r o v i n c i a ;33 }3435 public int getEdad ( ) {36 return edad ;37 }3839 public void setEdad ( int edad ) {40 t h i s . edad = edad ;41 }42 }Listado 3.1: Definición <strong>de</strong> la clase Ciudadano¿Cómo hacemos uso <strong>de</strong> los métodos <strong>de</strong> la clase, tanto <strong>de</strong> los <strong>de</strong>finidos enla clase extendida como los <strong>de</strong>finidos en la clase base?, sencillamente como loestábamos haciendo hasta ahora: a través <strong>de</strong> las referencias, como en el siguienteejemplo <strong>de</strong> código:1 Ciudadano ciudadano = new Ciudadano ( " José " , " G a r c í a " , " 555 123 456 " , "A l c o r c ó n " , " M a d r i d " , 4 0 ;2 System . out . p r i n t l n ( " N o m b r e : " + ciudadano . getNombre ( ) ) ;


54CAPÍTULO 3. HERENCIA E INTERFACES3 System . out . p r i n t l n ( " Edad : " + ciudadano . getEdad ( ) ) ;Como vemos en la línea 2 <strong>de</strong>l Listado anterior, hacemos uso <strong>de</strong>l métodogetNombre() <strong>de</strong>finido en la clase padre, a partir <strong>de</strong> una referencia <strong>de</strong> la clasehija, mientras que en la línea 3 hacemos uso <strong>de</strong>l método getEdad() <strong>de</strong>finido enla clase hija.¿Po<strong>de</strong>mos utilizar una referencia a la clase padre para acce<strong>de</strong>r a los mismosmétodos? No, aunque es perfectamente válido asignar una referencia <strong>de</strong> unaclase hija a una referencia a la clase padre, a través <strong>de</strong> la referencia a la clasepadre sólo tendremos acceso a los miembros <strong>de</strong>clarados en ella. En particular,para el ejemplo <strong>de</strong> la clase padre Persona y su clase hija Ciudadano, el siguientecódigo ejemplo <strong>con</strong>tiene un error:1 Ciudadano ciudadano = new Ciudadano ( ) ;2 Pesona persona = ciudadano ; // Perfectamente v á l i d o .3 persona . getNombre ( ) ; // No hay problema , getNombre ( ) e s t á d e f i n i d o enPersona .4 persona . getEdad ( ) ; // Error ! ! ! , getEdad ( ) e s t á d e f i n i d o en Ciudadano .También es un error asignar a una referencia <strong>de</strong> una clase hija una referenciaa la clase padre, el siguiente código <strong>de</strong> ejemplo <strong>con</strong>tiene un error:1 Persona persona = new Persona ( ) ;2 Ciudadano ciudadano = persona ; // Error ! ! !Concepto claveUna referencia <strong>de</strong> una clase padre admite una referencia a cualquiera <strong>de</strong> susclase hijas, pero nunca al <strong>con</strong>trario.Piensa qué ocurriría si no existiese esta prohibición, podríamos asignar a unareferencia a Ciudadano una referencia <strong>de</strong> su clase padre Persona, y a través <strong>de</strong> lareferencia a Ciudadano podríamos invocar a, por ejemplo, el método getEdad(),pero, la clase Persona no posee el atributo int edad;, ¿Qué se <strong>de</strong>bería <strong>de</strong>volveren este caso?3.2.1. Sobrescribir atributos.En algunas circunstancias, po<strong>de</strong>mos vernos en la necesidad <strong>de</strong> <strong>de</strong>finir un atributoen una clase hija <strong>con</strong> el mismo nombre que en su clase padre, como muestra elsiguiente código <strong>de</strong> ejemplo:1 // Esta e s l a c l a s e padre2 public c l a s s D i s t a n c i a {3 f l o a t d i s t a n c i a ;45 public D i s t a n c i a ( ) {6 d i s t a n c i a = 0 ;7 }89 public D i s t a n c i a ( f l o a t d i s t a n c i a ) {10 t h i s . d i s t a n c i a = d i s t a n c i a ;11 }12 // Sigue l a d e f i n i c i ó n <strong>de</strong> e s t a c l a s e .


3.2.EXTENSIÓN DE UNA CLASE. 5513 }1415 // Esta e s l a c l a s e h i j a16 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {17 // Este e s e l a t r i b u t o s o b r e s c r i t o18 double d i s t a n c i a ;1920 public D i s t a n c i a D o b l e P r e c i s i o n ( ) {21 d i s t a n c i a = 0 ;22 }2324 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {25 t h i s . d i s t a n c i a = d i s t a n c i a ;26 }27 // Sigue l a d e f i n i c i ó n <strong>de</strong> e s t a c l a s e .28 }En este caso se dice que el atributo distancia <strong>de</strong> la clase hijaDistanciaDoblePrecision sobrescribe el atributo distancia <strong>de</strong> la clase padreDistancia. Cuando una clase hija sobrescribe algún atributo <strong>de</strong> su clasepadre, el atributo <strong>de</strong> la clase padre queda oculto , <strong>de</strong> modo que si aparece elnombre <strong>de</strong>l atributo en la clase hija se utilizará el atributo <strong>de</strong>finido en esta clasey no el <strong>de</strong>finido en la clase padre. Esto no quiere <strong>de</strong>cir que el atributo <strong>con</strong> elmismo nombre en la clase padre <strong>de</strong>saparezca, sino que para acce<strong>de</strong>r a él tendremosque hacer uso <strong>de</strong> otro mecanismo como veremos más a<strong>de</strong>lante en estasección.¿Cómo acce<strong>de</strong>mos al atributo distancia <strong>de</strong>s<strong>de</strong> fuera <strong>de</strong> la clase? Ya lo sabemos,a través <strong>de</strong> referencias. De acuerdo, entonces, ¿Qué mostrará el siguienteejemplo?:1 D i s t a n c i a d i s t a n c i a = new D i s t a n c i a ( 1 0 0 ) ;2 System . out . p r i n t l n ( " El v a l o r <strong>de</strong> d i s t a n c i a es : " + d i s t a n c i a . d i s t a n c i a ) ;3 D i s t a n c i a d i s t a n c i a D o b l e P r e c i s i o n = new D i s t a n c i a D o b l e P r e c i s i o n ( 2 0 0 ) ;4 System . out . p r i n t l n ( " El v a l o r <strong>de</strong> d i s t a n c i a D o b l e P r e c i s i o n es : " +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 ) ;Lo que mostrará este código es, sorpresa:El valor <strong>de</strong> distancia es: 100.0El valor <strong>de</strong> distancia2 es: 0.0¿Qué ha ocurrido? Nada extraño, simplemente que al acce<strong>de</strong>r al atributoa través <strong>de</strong> la referencia, se ha buscado este valor en la <strong>de</strong>finición <strong>de</strong> la clasecorrespondiente a la referencia, que en los dos casos es Distancia y el atributoque se está iniciando en la línea 3 <strong>de</strong>l código anterior es el <strong>de</strong> la clase hijaDistanciaDoblePrecision pues el objeto que se crea es <strong>de</strong> la clase extendida.Comparemos <strong>con</strong> el resultado <strong>de</strong> la ejecución <strong>de</strong> este otro código ejemplo:1 D i s t a n c i a d i s t a n c i a = new D i s t a n c i a ( 1 0 0 ) ;2 System . out . p r i n t l n ( " El v a l o r <strong>de</strong> d i s t a n c i a es : " + d i s t a n c i a . d i s t a n c i a ) ;3 D i s t a n c i a D o b l e P r e c i s i o n d i s t a n c i a D o b l e P r e c i s i o n = newD i s t a n c i a D o b l e P r e c i s i o n ( 2 0 0 ) ;4 System . out . p r i n t l n ( " El v a l o r <strong>de</strong> d i s t a n c i a D o b l e P r e c i s i o n es : " +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 ) ;Lo que mostrará este código es:El valor <strong>de</strong> distancia es: 100.0El valor <strong>de</strong> distanciaDoblePrecision es: 200.0


56CAPÍTULO 3. HERENCIA E INTERFACESEn este último ejemplo, lo único que ha cambiado es el tipo <strong>de</strong>la referencia distanciaDoblePrecision, que en este caso es <strong>de</strong> tipoDistanciaDoblePrecision, es <strong>de</strong>cir, la clase hija.Concepto claveCuando una clase hija sobrescribe (oculta) un atributo <strong>de</strong> la clase padre, elatributo seleccionado se <strong>de</strong>termina por el tipo <strong>de</strong> la referencia.3.2.2. Sobrescribir métodos.Para introducir el modo <strong>de</strong> sobrescribir métodos imaginemos que hemos añadidoal código <strong>de</strong> la clase Distancia un nuevo método que nos permita incrementarla distancia actual:1 public c l a s s D i s t a n c i a {2 f l o a t d i s t a n c i a ;34 public D i s t a n c i a ( ) {5 d i s t a n c i a = 0 ;6 }78 public D i s t a n c i a ( f l o a t d i s t a n c i a ) {9 t h i s . d i s t a n c i a = d i s t a n c i a ;10 }1112 void i n c r e m e n t a D i s t a n c i a ( f l o a t incremento ) {13 d i s t a n c i a += incremento ;14 }15 // Sigue l a d e f i n i c i ó n <strong>de</strong> e s t a c l a s e .16 }Listado 3.2: Definición <strong>de</strong> la clase DistanciaAhora queremos probar nuestra nueva funcionalidad <strong>con</strong> este ejemplo:1 D i s t a n c i a D o b l e P r e c i s i o n d i s t a n c i a = new D i s t a n c i a D o b l e P r e c i s i o n ( 1 0 0 ) ;2 d i s t a n c i a . i n c r e m e n t a D i s t a n c i a ( 1 0 0 ) ;3 System . out . p r i n t l n ( " El v a l o r <strong>de</strong> d i s t a n c i a es : " + 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 <strong>de</strong> distancia es: 100.0¿Cómo es posible? Estamos intentando incrementar la distancia inicial <strong>de</strong>100 en otros 100, y parece que no lo <strong>con</strong>seguimos. ¿Don<strong>de</strong> está el problema?.Nuestro nuevo método incrementaDistancia(float) está <strong>de</strong>finido en laclase padre, y este método incrementa el valor <strong>de</strong>l atributo que hay <strong>de</strong>finido enella, no en la clase hija. ¿Cómo po<strong>de</strong>mos arreglarlo? La respuesta es sobrescribiendoel método incrementaDistancia(float) en la clase hija <strong>de</strong> este modo:1 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {2 double d i s t a n c i a ;34 public D i s t a n c i a D o b l e P r e c i s i o n ( ) {5 d i s t a n c i a = 0 ;6 }7


3.2.EXTENSIÓN DE UNA CLASE. 578 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {9 t h i s . d i s t a n c i a = d i s t a n c i a ;10 }1112 @Overri<strong>de</strong>13 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 incremento ) {14 d i s t a n c i a += incremento ;15 }16 // Sigue l a d e f i n i c i ó n <strong>de</strong> e s t a c l a s e .17 }Listado 3.3: Definición <strong>de</strong> la clase DistanciaDoblePrecisionaNótese el uso <strong>de</strong> la anotación @Overri<strong>de</strong> que indica al compilador <strong>de</strong> <strong>Java</strong> quese está intentando sobrescribir un método en la clase padre.Ahora sí, el resultado obtenido es:El valor <strong>de</strong> distancia es: 200.0Para que una clase hija sobrescriba un método <strong>de</strong> su clase padre es necesarioque ambos métodos tengan la misma signatura y el mismo tipo <strong>de</strong> retorno, <strong>de</strong>lo <strong>con</strong>trario no se sobrescribe el método, como en el siguiente ejemplo:1 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {2 double d i s t a n c i a ;34 public D i s t a n c i a D o b l e P r e c i s i o n ( ) {5 d i s t a n c i a = 0 ;6 }78 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {9 t h i s . d i s t a n c i a = d i s t a n c i a ;10 }1112 // Intentamos s o b r e s c r i b i r cambiando e l t i p o d e l argumento13 // Se produce un e r r o r14 @Overri<strong>de</strong>15 void i n c r e m e n t a D i s t a n c i a ( double incremento ) {16 d i s t a n c i a += incremento ;17 }18 // Sigue l a d e f i n i c i ó n <strong>de</strong> e s t a c l a s e .19 }Aunque es posible que la clase hija sobrescriba un método <strong>de</strong> la clase padreampliando el modificador <strong>de</strong> acceso, por ejemplo, en el caso <strong>de</strong>l Listado3.2.2 posemos <strong>de</strong>finir el método incrementaDistancia como public voidincrementaDistancia(double incremento), <strong>de</strong> modo que hemos ampliado elmodificador <strong>de</strong> acceso <strong>de</strong>s<strong>de</strong> acceso paquete hasta public. Lo que no está permitidoes que cuando se sobrescribe un método <strong>de</strong> la clase padre, el hijo restrinjael modificador <strong>de</strong> acceso. Siguiendo <strong>con</strong> el ejemplo, intentar sobrescribir el métodoincrementaDistancia como private void incrementaDistancia(doubleincremento), daría un error.Gracias al uso <strong>de</strong> la anotación @Overri<strong>de</strong> 2 obtenemos un error que nosinforma que el nuevo método no está sobrescribiendo a ningún método en suclase padre. Si eliminamos la anotación no obtenemos ningún error y lo queestaremos haciendo es <strong>de</strong>finiendo un nuevo método que toma como argumentouna variable <strong>de</strong> tipo double.2 Las anotaciones fueron introducidas en la versión 5 <strong>de</strong> <strong>Java</strong>


58CAPÍTULO 3. HERENCIA E INTERFACESBuenas prácticasPara asegurar que los métodos en las clases hijas sobrescriben métodos en laclase padre utilícese la anotación @Overri<strong>de</strong> en el método sobrescrito.Ya sabemos que a una referencia a una clase padre le po<strong>de</strong>mos asignar unareferencia a cualquier clase hija, <strong>de</strong> acuerdo, modifiquemos nuestro código <strong>de</strong>prueba <strong>de</strong>l siguiente modo:1 D i s t a n c i a d i s t a n c i a = new D i s t a n c i a D o b l e P r e c i s i o n ( 1 0 0 ) ;2 d i s t a n c i a . i n c r e m e n t a D i s t a n c i a ( 1 0 0 ) ;3 System . out . p r i n t l n ( " El v a l o r <strong>de</strong> d i s t a n c i a es : " + d i s t a n c i a . d i s t a n c i a ) ;¿Qué es lo que obtenemos? Sorpresa <strong>de</strong> nuevo:El valor <strong>de</strong> distancia es: 0.0¿Cómo pue<strong>de</strong> ser que obtengamos 0.0 si estamos creando un objeto <strong>de</strong> tipoDistanciaDoblePrecision <strong>con</strong> un valor inicial <strong>de</strong> 100 y <strong>de</strong>spués lo estamosincrementando en 100 unida<strong>de</strong>s más?. La respuesta, esta vez está recogida eneste <strong>con</strong>cepto clave:Concepto claveCuando acce<strong>de</strong>mos a los métodos <strong>de</strong> un objeto a través <strong>de</strong> una referencia seselecciona el método a partir <strong>de</strong>l tipo <strong>de</strong>l objeto y no <strong>de</strong> la referencia a través<strong>de</strong> la que se acce<strong>de</strong>.Este <strong>con</strong>cepto es muy importante en POO y a este mecanismo se le llamaVinculación dinámica .¿Qué está ocurriendo entonces? Ocurre que distancia es una referencia<strong>de</strong> tipo Distancia, pero el tipo <strong>de</strong>l objeto al que hace referencia, elque creamos <strong>con</strong> el operador new es DistanciaDoblePrecision. Al usarel método incrementaDistancia(100) la vinculación dinámica ejecuta elcódigo <strong>de</strong> DistanciaDoblePrecision no el <strong>de</strong> Distancia. Mientras quetal y como sabemos <strong>de</strong> la Sección 3.2.1, si utilizamos atributos no sehace uso <strong>de</strong> la vinculación dinámica y se acce<strong>de</strong> al atributo correspondienteal tipo que indica la referencia no el objeto que hay por <strong>de</strong>bajo<strong>de</strong> ella, por lo tanto si escribimos distancia.distancia estamos accediendoal atributo en Distancia pero el atributo que se incrementó <strong>con</strong>distancia.incrementaDistancia(100) fue el que incrementó la vinculacióndinámica, es <strong>de</strong>cir, el <strong>de</strong> DistanciaDoblePrecision.Nótese la diferencia fundamental <strong>con</strong> respecto al acceso a los atributos, don<strong>de</strong>el atributo al que se acce<strong>de</strong> se <strong>de</strong>termina por el tipo <strong>de</strong> la referencia y no <strong>de</strong>lobjeto.También es posible que una clase sobrescriba un método ampliando el tipo<strong>de</strong>l valor <strong>de</strong> retorno, es <strong>de</strong>cir que si en la clase padre Distancia tenemos unmétodo como Distancia metodo() la clase hija pue<strong>de</strong> sobrescribirlo <strong>con</strong> el métodoDistanciaDoblePrecision metodo(), ya que se ha ampliado el tipo <strong>de</strong>l valor <strong>de</strong> retorno


3.2.EXTENSIÓN DE UNA CLASE. 59<strong>de</strong>s<strong>de</strong> el original Distancia a DistanciaDoblePrecision, esta posibilidad fueintroducida en la versión 5 <strong>de</strong> <strong>Java</strong>.Pero cuidado, esto último no funciona <strong>con</strong> los tipos <strong>de</strong> retorno primitivos, sitenemos en la clase padre un método <strong>de</strong>finido como public float unMetodo()que <strong>de</strong>vuelve el tipo primitivo float no lo po<strong>de</strong>mos sobrescribir en una clasehija <strong>con</strong> public double unMetodo() que <strong>de</strong>vuelve el tipo primitivo double.3.2.3. La palabra reservada super.Existen casos en los que, <strong>de</strong>s<strong>de</strong> una clase hija, nos interesa acce<strong>de</strong>r a losmétodos o atributos sobrescritos en la clase padre. Si escribimos en la clase hijasimplemente el nombre <strong>de</strong>l atributo o <strong>de</strong>l método estaremos haciendo uso <strong>de</strong>la <strong>de</strong>finición dada para ellos en la clase hija. ¿Cómo acce<strong>de</strong>mos a los miembrossobrescritos <strong>de</strong>s<strong>de</strong> la clase hija?. La respuesta es haciendo uso <strong>de</strong> la palabrareservada super.DefiniciónLa palabra reservada super es una referencia a la clase padre, <strong>de</strong>l mismo modoque la palabra reservada this es una referencia a la propia clase.3.2.4. El <strong>con</strong>structor por <strong>de</strong>fecto y la clase Object.En el código <strong>de</strong> los ejemplos <strong>de</strong> prueba, nunca hemos utilizado el <strong>con</strong>structorsin parámetros <strong>de</strong> las clases Distancia (véase el listado 3.2) ni <strong>de</strong> la claseDistanciaDoblePrecision (véase el listado 3.3), luego, po<strong>de</strong>mos intentar eliminarestos dos <strong>con</strong>structores e iniciar el atributo distancia <strong>de</strong> ambas clasesen el momento <strong>de</strong> la <strong>de</strong>finición, tal y como se muestra en el siguiente listado:1 //−−−− D e f i n i c i ó n <strong>de</strong> l a c l a s e D i s t a n c i a −−−−//2 public c l a s s D i s t a n c i a {3 f l o a t d i s t a n c i a = 0 ;45 // Eliminado e l c o n s t r u c t o r s i n parámetros67 public D i s t a n c i a ( f l o a t d i s t a n c i a ) {8 t h i s . d i s t a n c i a = d i s t a n c i a ;9 }1011 void i n c r e m e n t a D i s t a n c i a ( f l o a t incremento ) {12 d i s t a n c i a += incremento ;13 }14 // Sigue l a d e f i n i c i ó n <strong>de</strong> e s t a c l a s e .15 }16 //−−−− D e f i n i c i ó n <strong>de</strong> 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 −−−−//17 public c l a s s D i s t a n c i a D o b l e P r e c i s i o n extends D i s t a n c i a {18 double d i s t a n c i a = 0 ;1920 // Eliminado e l c o n s t r u c t o r s i n parámetros2122 public D i s t a n c i a D o b l e P r e c i s i o n ( double d i s t a n c i a ) {23 t h i s . d i s t a n c i a = d i s t a n c i a ;24 }2526 @Overri<strong>de</strong>27 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 incremento ) {28 d i s t a n c i a += incremento ;29 }30 // Sigue l a d e f i n i c i ó n <strong>de</strong> e s t a c l a s e .


60CAPÍTULO 3. HERENCIA E INTERFACESPero inmediatamente obtendremos el siguiente error en el código <strong>de</strong> la claseDistanciaDoblePrecision Implicit super <strong>con</strong>structor Distancia() is un<strong>de</strong>fined.Must explicitly invoke another <strong>con</strong>structor. Este error es <strong>de</strong>bido a que, el <strong>con</strong>structor<strong>de</strong> la clase hija public DistanciaDoblePrecision(double distancia)está intentando invocar implícitamente al <strong>con</strong>structor <strong>de</strong> la clase padre publicDistancia() que no está <strong>de</strong>finido. Este es el mecanismo en la creación <strong>de</strong> objetosen <strong>Java</strong> cuando existe relación <strong>de</strong> herencia entre clases, <strong>de</strong>s<strong>de</strong> los <strong>con</strong>structores<strong>de</strong> las clases hijas, si no se indica lo <strong>con</strong>trario, se intenta invocar al <strong>con</strong>structorsin parámetros <strong>de</strong> la clase padre, que por este motivo es llamado Constructor por<strong>de</strong>fecto . Si no se indica lo <strong>con</strong>trario, lo primero que se hace <strong>de</strong>s<strong>de</strong> el <strong>con</strong>structor<strong>de</strong> una clase hija es llamar al <strong>con</strong>structor por <strong>de</strong>fecto <strong>de</strong> la clase padre.Buenas prácticasPara evitar problemas en la ceración <strong>de</strong> objetos, es <strong>con</strong>veniente <strong>de</strong>finir siempreel <strong>con</strong>structor por <strong>de</strong>fecto en nuestras clases.El error anterior lo po<strong>de</strong>mos corregir <strong>de</strong> dos modos, añadiendo los <strong>con</strong>structorespor <strong>de</strong>fecto a cada una <strong>de</strong> las clases, o bien, llamando <strong>de</strong>s<strong>de</strong> el <strong>con</strong>structor<strong>con</strong> parámetros <strong>de</strong> la clase hija al <strong>con</strong>structor <strong>con</strong> parámetros <strong>de</strong> la clase padre,para que no se llame por <strong>de</strong>fecto el <strong>con</strong>structor sin parámetros, que noestá <strong>de</strong>finido:1 public D i s t a n c i a D o b l e P r e c i s i o n ( f l o a t d i s t a n c i a ) {2 super ( 0 ) ; // Llamamos a l c o n s t r u c t o r <strong>con</strong> parámetros d e l padre3 t h i s . d i s t a n c i a = d i s t a n c i a ;4 }Si optamos por la segunda solución, la llamada al <strong>con</strong>structor <strong>de</strong>l padre eslo primero que <strong>de</strong>bemos hacer en el <strong>con</strong>strutor <strong>de</strong>l hijo; en el ejemplo anterior siintercambiamos las líneas 3 y 4 obtendremos el siguiente error Constructor callmust be the first statement in a <strong>con</strong>structorLa pregunta que nos surge es ¿A qué <strong>con</strong>structor se llama <strong>de</strong>s<strong>de</strong> el <strong>con</strong>structorpor <strong>de</strong>fecto <strong>de</strong> la clase Distancia que no está extendiendo a ninguna otraclase, tal y como se muestra en el Listado 3.2? Para respon<strong>de</strong>r a esta preguntanecesitamos saber que la clase Object es la clase que está en la raíz <strong>de</strong>l árbol<strong>de</strong> jerarquí <strong>de</strong> clases en <strong>Java</strong>, y que si una clase explícitamente no extien<strong>de</strong> aninguna otra, implícitamente está extendiendo a la clase Object.Concepto claveLa clase Object se encuentra en la raíz <strong>de</strong>l árbol <strong>de</strong> jerarquía <strong>de</strong> clases en <strong>Java</strong>.Cualquier otra clase, bien directamente o bien a través <strong>de</strong> herencia, es hija <strong>de</strong>la clase Object.3.2.5. El operador instanceof.Ya sabemos que cuando llamamos a los métodos <strong>de</strong> un objeto a través <strong>de</strong> unareferencia, es el tipo <strong>de</strong>l objeto (la clase a la que pertenece) el que <strong>de</strong>terminaqué método se ha <strong>de</strong> llamar. A este mecanismo lo hemos llamado Vinculación


3.2.EXTENSIÓN DE UNA CLASE. 61dinámica. No importa el tipo <strong>de</strong> la referencia a la que asignemos el objeto siempreque, evi<strong>de</strong>ntemente, el tipo <strong>de</strong> la referencia sea compatible <strong>con</strong> el tipo <strong>de</strong>lobjeto, en tiempo <strong>de</strong> ejecución el mecanismo <strong>de</strong> vinculación dinámica <strong>de</strong>terminarácual es el método que se ha <strong>de</strong> llamar si es que ese método está sobrescrito.La pregunta que ahora nos surge es: Si el único acceso que tenemos es através <strong>de</strong> referencias y el tipo <strong>de</strong> la referencia no tiene por qué coincidir <strong>con</strong>el tipo <strong>de</strong>l objeto que tiene asignado, basta <strong>con</strong> que sean compatibles, ¿Cómopo<strong>de</strong>mos <strong>con</strong>ocer el verda<strong>de</strong>ro tipo <strong>de</strong>l objeto asignado a la referencia?.Para dar <strong>con</strong>testación a esta pregunta <strong>Java</strong> pone a nuestra disposición unoperador binario, el operador instanceof <strong>con</strong> el que po<strong>de</strong>mos preguntar si elobjeto asignado a una referencia es <strong>de</strong> un <strong>de</strong>terminado tipo o no; el valor <strong>de</strong>retorno <strong>de</strong> este operador es un booleano true o false. Estos son algunos casos<strong>de</strong> uso:1 Persona persona = new Persona ( ) ;2 System . out . p r i n t l n ( persona instanceof Persona ) ; // Devolverá \emph{ t r u e }3 System . out . p r i n t l n ( persona instanceof Ciudadano ) ; // Devolverá \emph{f a l s e }4 Ciudadano ciudadano = new Ciudadano ( ) ;5 System . out . p r i n t l n ( ciudadano instanceof Ciudadano ) ; // Devolverá \emph{t r u e }6 System . out . p r i n t l n ( ciudadano instanceof Persona ) ; // Devolverá \emph{t r u e }Aunque el operador instanceof nos pue<strong>de</strong> prestar ayuda en algunos casos,<strong>con</strong>viene seguir la siguiente buena práctica:Buenas prácticasIntenta evitar el uso <strong>de</strong>l operador instanceof en tu código, utiliza polimorfismopara no hacer uso <strong>de</strong> este operator.3.2.6. El modificador final.En el capítulo 2 vimos el uso <strong>de</strong>l modificador final aplicado a los atributos<strong>de</strong> una clase.El operador final también se pue<strong>de</strong> aplicar a los métodos <strong>de</strong> una clase,<strong>de</strong> tal modo que si un método se <strong>de</strong>clara como final estamos indicando queese método no pue<strong>de</strong> ser sobrescrito por ninguna clase hija. Con ello estamosgarantizando que el trabajo que realiza el método es siempre el mismo <strong>con</strong>in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> si el objeto sobre el que se llama es instancia <strong>de</strong> la clase padreo instancia <strong>de</strong> alguna <strong>de</strong> sus clases hijas. En el siguiente listado se muestra cómose pue<strong>de</strong> violar el comportamiento al iniciar una instancia por parte <strong>de</strong> un hijosi el padre no protege el método que inicia los atributos <strong>con</strong> el modificado final:1 // Código <strong>de</strong> l a c l a s e padre2 public c l a s s Persona {3 private S t r i n g nombre ;4 private S t r i n g a p e l l i d o s ;5 private S t r i n g t e l e f o n o ;6 private s t a t i c int n I n s t a n c i a s ;78 public Persona ( ) {


62CAPÍTULO 3. HERENCIA E INTERFACES9 super ( ) ;10 i n i c i a A t r i b u t o s ( ) ;11 }1213 protected void i n i c i a A t r i b u t o s ( ) {14 nombre = " " ;15 a p e l l i d o s = " " ;16 t e l e f o n o = " " ;17 n I n s t a n c i a s = 0 ;18 }19 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e2021 // Código <strong>de</strong> l a c l a s e h i j a22 public Ciudadano ( ) {23 super ( ) ;24 // Aquí cambiamos e l comportamiento <strong>de</strong> i n i c i o <strong>de</strong> l o s a t r i b u t o s25 i n i c i a A t r i b u t o s ( ) ;26 }2728 @Overri<strong>de</strong>29 protected void i n i c i a A t r i b u t o s ( ) {30 setNombre ( " Un n o m b r e " ) ;31 }32 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s eSimplemente añadiendo el modificador final al método iniciaAtributos<strong>de</strong> la clase padre, si intentamos sobrescribir este método en la clase hija obtendremosel siguiente error Cannot overri<strong>de</strong> the final method from Personaadvirtiéndonos que no po<strong>de</strong>mos sobrescribir un método <strong>de</strong>clarado como finalen la clase padre.Buenas prácticasLos métodos a los que se llama <strong>de</strong>s<strong>de</strong> los <strong>con</strong>structores <strong>de</strong> una clase <strong>de</strong>benser modificados como final para prevenir que alguna clase hija modifique elcomportamiento al crear instancias <strong>de</strong> la clase.Es muy recomendable seguir la anterior buena práctica, piensa que ocurriríasi en el <strong>con</strong>structor <strong>de</strong> una clase padre que abre una <strong>con</strong>exión a una base <strong>de</strong>datos, y una clase hija sobrescribiese las tareas <strong>de</strong> inicio, y la <strong>con</strong>exión a la base<strong>de</strong> datos no se estableciese; toda la aplicación <strong>de</strong>jaría <strong>de</strong> funcionar.El modificador final también se pue<strong>de</strong> aplicar sobre una clase <strong>de</strong> este modo:1 public f i n a l c l a s s Persona {2 // La d e f i n i c i ó n <strong>de</strong> l a c l a s eEn este caso lo que estamos indicando es que la clase Persona no se pue<strong>de</strong>exten<strong>de</strong>r porque la hemos <strong>de</strong>clarado como final. Un ejemplo <strong>de</strong> clase finalen <strong>Java</strong> es la clase String que está <strong>de</strong>clarada como final y por lo tanto no sepue<strong>de</strong> exten<strong>de</strong>r, es <strong>de</strong>cir no po<strong>de</strong>mos crear hijas <strong>de</strong> ella.3.2.7. Métodos static.En el Capítulo 2 vimos el significado <strong>de</strong>l modificador static cuando seaplica a métodos. El modificador static indica que el método pertenece a laclase y no a las instancias <strong>de</strong> la clase. Por otro lado hemos visto lo que significala Vinculación dinámica, <strong>de</strong>terminar en tiempo <strong>de</strong> ejecución el método que se<strong>de</strong>be llamar al invocarse <strong>de</strong>s<strong>de</strong> una instancia cuando está sobrescrito. Fíjate


3.3. CLASES ABSTRACTAS. 63que el mecanismo <strong>de</strong> sobrescribir métodos funciona en el ámbito <strong>de</strong> los objetos,mientras que los métodos static pertenecen al ámbito <strong>de</strong> las clases. Es por estoque un método static <strong>de</strong> una clase padre no se pue<strong>de</strong> sobrescribir, los métodosstatic <strong>de</strong> la clase padre son métodos ocultos que no son visibles <strong>de</strong>s<strong>de</strong> las claseshija. Si en una clase hija <strong>de</strong>claramos un método <strong>con</strong> la misma signatura que unmétodo static en la clase padre, lo que estamos haciendo realmente es creandoun nuevo método en la clase hija sin ninguna relación <strong>con</strong> el mismo métodoen la clase padre. Obviamente si intentamos usar la anotación @Overri<strong>de</strong> paraindicar que queremos sobrescribir el método obtendremos un error This instancemethod cannot overri<strong>de</strong> the static method from ...3.3. Clases abstractas.Hasta este momento, siempre que hemos <strong>de</strong>finido los métodos <strong>de</strong> las clases quehemos creado, siempre hemos escrito código en la <strong>de</strong>finición <strong>de</strong> los métodos. Aveces es útil simplemente <strong>de</strong>clarar métodos un una clase padre, sin dar ningunaimplementación para ellos, y <strong>de</strong>legar la implementación a las clases hijas quela extiendan. Esta es una técnica muy potente que utiliza el <strong>con</strong>cepto <strong>de</strong> Polimorfismo<strong>de</strong> la POO. De este modo estamos garantizando que todas las claseshijas <strong>de</strong> la misma clase padre tiene un método <strong>con</strong> la misma signatura, aunque,obviamente, cada una <strong>de</strong> las clase hijas pue<strong>de</strong> tener una implementación distintapara el método polimórfico.Si queremos indicar que no vamos a dar una implementeación para algúnmétodo <strong>de</strong>clarado en la clase, <strong>de</strong>bemos modificarlo <strong>con</strong> la palabra reservadaabstract, <strong>con</strong> la restricción <strong>de</strong> que si una clase tiene algún método abstractella misma también <strong>de</strong>be ser <strong>de</strong>clarada como abstract.También po<strong>de</strong>mos <strong>de</strong>clarar una clase como abstract sin que ninguno <strong>de</strong> susmétodos los sea. Si una clase es <strong>de</strong>clarada como abstract, sobre ella tenemosla restricción recogida en el siguiente <strong>con</strong>cepto clave:Concepto claveNo se pue<strong>de</strong>n crear instancias <strong>de</strong> una clase <strong>de</strong>clarada como abstractDe no existir esta restricción ¿Qué ocurriría si se llamase a un métodoabstract <strong>de</strong> un objeto? ¿Qué código se ejecutaría? Evi<strong>de</strong>ntemente <strong>de</strong> po<strong>de</strong>rser así tendríamos un grave problema, ya que pue<strong>de</strong> que no existiene ningúncódigo para ejecutar.Los métodos abstract <strong>de</strong> la clase padre <strong>de</strong>ben ser <strong>de</strong>finidos en las claseshijas, en cuyo caso los métodos en las clase hijas ya no serán abstract y tampocola propia clase hija. Ahora bien, pue<strong>de</strong> existir algún el caso en que una clase hijatampoco <strong>de</strong>fina algún método abstract <strong>de</strong> la clase padre; en este caso la clasehija también <strong>de</strong>berá ser <strong>de</strong>clarada abstract y no podremos crear instancias <strong>de</strong>ella.Un ejemplo recurrente para mostrar el uso <strong>de</strong> las clases abstract es unaaplicación que dibuje distintas figuras geométricas tales como círculos, triángulosy cuadrados. Podríamos <strong>de</strong>clara el comportamiento común <strong>de</strong> todas estas clases,por ejemplo el método dibujate() en una clase padre llamada Figuras, ycada una <strong>de</strong> las clases hijas tuviese la implementación a<strong>de</strong>cuada para dibujarse


64CAPÍTULO 3. HERENCIA E INTERFACES<strong>de</strong>pendiendo <strong>de</strong> su naturaleza. De modo muy esquemático el código <strong>de</strong> estasclases podría ser algo como lo mostrado en el Listado 3.4:1 public abstract c l a s s Figura {2 public abstract void d i b u j a t e ( ) ;3 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e4 }56 public c l a s s T r i a n g u l o extends Figura {7 public void d i b u j a t e ( ) {8 // Código para d i b u j a r un t r i á n g u l o9 }10 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e11 }1213 public c l a s s Cuadrado extends Figura {14 public void d i b u j a t e ( ) {15 // Código para d i b u j a r un cuadrado16 }17 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e18 }1920 public c l a s s C i r c u l o extends Figura {21 public void d i b u j a t e ( ) {22 // Código para d i b u j a r un c í r c u l o23 }24 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e25 }Listado 3.4: Definición <strong>de</strong> una clase abstract y algunas clases hijas.La potencia <strong>de</strong>l código <strong>de</strong>l Listado anterior se hace evi<strong>de</strong>nte cuando recordamosque po<strong>de</strong>mos asignar cualquier objeto <strong>de</strong> una clase hija (Triangulo,Cuadrado o Circulo) a una referencia <strong>de</strong> la clase padre Figura, <strong>de</strong> modo quepodríamos escribir algo como:1 Figura f i g u r a = new C i r c u l o ( ) ;2 f i g u r a . d i b u j a t e ( ) ; // Dibujará un c í r c u l o3 f i g u r a = new T r i a n g u l o ( ) ;4 f i g u r a . d i b u j a t e ( ) ; // Dibujará un t r i á n g u l o5 f i g u r a = new Cuadrado ( ) ;6 f i g u r a . d i b u j a t e ( ) ; // Dibujará un cuadradoListado 3.5: Uso <strong>de</strong> una referencia a una clase padre abstract para recorrerinstancias <strong>de</strong> las clases hijas.De nuevo la Vinculación dinámica en cada una <strong>de</strong> las llamadas al métododibujate() <strong>de</strong>terminará el método que se <strong>de</strong>be invocar.Buenas prácticasEl diseño <strong>de</strong> tus aplicaciones <strong>de</strong>be estar orientado al interface no a la implementación.¿Qué quiere <strong>de</strong>cir esta buena práctica? La i<strong>de</strong>a es que <strong>de</strong>bes <strong>con</strong>centrarte encrear buenas abstracciones, es <strong>de</strong>cir <strong>de</strong>bes intentar en<strong>con</strong>trar el comportamientocomún (<strong>de</strong>claración <strong>de</strong> los métodos) a tus clases para que las puedas tratar <strong>de</strong>manera homogénea, <strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> cómo se materializa ese comportamientocomún (implementación <strong>de</strong> los métodos en cada clase). En el caso <strong>de</strong>la aplicación para dibujar figuras geométricas, el comportamiento común es el


3.4. INTERFACES. 65hecho <strong>de</strong> que todas las figuras geométricas pue<strong>de</strong> dibujarse. La implementación<strong>con</strong>creta es el modo en que cada una <strong>de</strong> las clases hija se dibuja.El uso <strong>de</strong> esta buena práctica en el Listado 3.5 es que las referencias <strong>de</strong>benser siempre <strong>de</strong>l tipo más general posible (clase abstracta o interface comoveremos en la siguiente sección), y no <strong>de</strong> una clase <strong>con</strong>creta. Rescribamos elcódigo <strong>de</strong>l último listado haciendo caso omiso <strong>de</strong> esta buena práctica:1 C i r c u l o f i g u r a = new C i r c u l o ( ) ;2 f i g u r a . d i b u j a t e ( ) ; // Dibujará un c í r c u l o3 f i g u r a = new T r i a n g u l o ( ) ; // Error n u e s t r a f i g u r a e s un \ t e x t t t { C i r c u l o }no un \ t e x t t t { T r i a n g u l o }4 f i g u r a . d i b u j a t e ( ) ;5 f i g u r a = new Cuadrado ( ) ; // Error n u e s t r a f i g u r a e s un \ t e x t t t { C i r c u l o }no un \ t e x t t t {Cuadrado}6 f i g u r a . d i b u j a t e ( ) ;Como ves, no po<strong>de</strong>mos aprovechar el comportamiento polimórfico <strong>de</strong> nuestrasfiguras ya que las referencias son <strong>de</strong> un tipo <strong>con</strong>creto y no <strong>de</strong> un tipoabstracto, la clase Figura.En una clase hija también po<strong>de</strong>mos <strong>de</strong>clarar como abstract algún método<strong>de</strong>finido en la clase padre y que por lo tanto no es abstract. ¿Cuando nos pue<strong>de</strong>interesar esta estrategia? Pue<strong>de</strong> ser interesante para borrar el comportamientopor <strong>de</strong>fecto que ofrece la clase padre, ya que si la clase hija a su vez es extendidapor otra clases, estas <strong>de</strong>bería <strong>de</strong>finir el método <strong>de</strong>clarado abstract. Obviamente,si una clase hija <strong>de</strong>clara como abstract un método <strong>de</strong> la clase padre, aunque esteno fuese abstract en la clase padre, la tendremos que <strong>de</strong>clarar como abstract.3.4. Interfaces.Los interface son una nueva <strong>con</strong>strucción <strong>de</strong>l lenguaje <strong>Java</strong> que da un pasomás allá en las clases abstract. Pue<strong>de</strong>s pensar que un interface es como unaclase abstract en la que todos sus métodos son abstract.Siguiendo <strong>con</strong> el ejemplo <strong>de</strong> las figuras geométricas <strong>de</strong> la Sección 3.3 po<strong>de</strong>mos<strong>de</strong>finir nuestro primer interface como:1 public interface D i b u j a b l e {2 public void d i b u j a ( ) ;3 }Como ves, estamos usando la palabra reservada interface para indicar queestamos <strong>de</strong>finiendo un interface.Las clases no extien<strong>de</strong>n a los interfaces si no que los implementan y esto seindica <strong>con</strong> el uso <strong>de</strong> la palabra reservada implements <strong>de</strong> este modo:1 public c l a s s T r i a n g u l o implements D i b u j a b l e {2 @Overri<strong>de</strong>3 public void d i b u j a ( ) {4 // Código para d i b u j a r un t r i á n g u l o5 }6 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e7 }89 public c l a s s Cuadrado implements D i b u j a b l e {10 @Overri<strong>de</strong>11 public void d i b u j a ( ) {12 // Código para d i b u j a r un cuadrado


66CAPÍTULO 3. HERENCIA E INTERFACES13 }14 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e15 }1617 public c l a s s C i r c u l o implements D i b u j a b l e {18 @Overri<strong>de</strong>19 public void d i b u j a ( ) {20 // Código para d i b u j a r un c í r c u l o21 }22 // Sigue l a d e f i n i c i ó n <strong>de</strong> l a c l a s e23 }Fíjate que para indicar que las clases están implementando un método <strong>de</strong>claradoen un interface se anota el método <strong>con</strong> @Overri<strong>de</strong>.Y ahora, <strong>de</strong> nuevo, aparece la magia <strong>de</strong>l polimorfismo y la vinculacióndinámica: a una referencia <strong>de</strong> un tipo interface le po<strong>de</strong>mos asignar cualquierobjeto <strong>de</strong> una clase que implemente ese interface, <strong>de</strong> modo que el código <strong>de</strong>lsiguiente listado es perfectamente válido:1 D i b u j a b l e f i g u r a = new C i r c u l o ( ) ;2 f i g u r a . d i b u j a ( ) ; // Dibujará un c í r c u l o3 f i g u r a = new T r i a n g u l o ( ) ;4 f i g u r a . d i b u j a ( ) ; // Dibujará un t r i á n g u l o5 f i g u r a = new Cuadrado ( ) ;6 f i g u r a . d i b u j a ( ) ; // Dibujará un cuadradoFíjate que hemos utilizado una referencia <strong>de</strong> un tipo lo más amplio posibleDibujable y el comportamiento se materializa en la creación <strong>de</strong> las instancias<strong>de</strong> clases <strong>con</strong>cretas: Circulo, Triangulo o Cuadrado.En la <strong>de</strong>finición <strong>de</strong> un interface po<strong>de</strong>mos <strong>de</strong>clarar cualquier número <strong>de</strong>métodos y también cualquier número <strong>de</strong> <strong>con</strong>stantes como en el siguiente ejemplo:1 public c l a s s interface D i b u j a b l e {2 public s t a t i c f i n a l Color BLANCO = new Color ( 2 5 5 , 2 5 5 , 2 5 5 ) ;3 public s t a t i c f i n a l Color NEGRO = new Color ( 0 , 0 , 0 ) ;4 public void d i b u j a ( ) ;5 }El anterior interface tiene <strong>con</strong>tiene la <strong>de</strong>claración <strong>de</strong> dos <strong>con</strong>stantes, una<strong>de</strong>fine el color BLANCO y la otra el color NEGRO.Una ventaja <strong>de</strong>l uso <strong>de</strong> interfaces para mo<strong>de</strong>lar el comportamiento <strong>de</strong> lasclases es que una clase pue<strong>de</strong> implementar cualquier número <strong>de</strong> interfaces.Recuerda que en el caso <strong>de</strong> la extensión <strong>de</strong> una clase <strong>Java</strong> sólo permite herenciasimple. En el siguiente Listado se muestra un ejemplo <strong>de</strong> clase que implementamás <strong>de</strong> un inteface:1 // D e c l a r a c i ó n <strong>de</strong> un nuevo \ t e x t t t { i n t e r f a c e }2 public c l a s s interface Transformable {3 public void e s c a l a ( int sx , int sy ) ;4 public void d e s p l a z a ( int dx , int dy ) ;5 }67 // D e c l a r a c i ó n <strong>de</strong> l a c l a s e8 public c l a s s C i r c u l o implements Dibujable , Transformable {9 @Overri<strong>de</strong>10 public void d i b u j a ( ) {11 // Aquí l a d e f i n i c i ó n d e l método12 }1314 @Overri<strong>de</strong>15 public void e s c a l a ( int sx , int sy ) {


3.4. INTERFACES. 6716 // Aquí l a d e f i n i c i ó n d e l método17 }1819 @Overri<strong>de</strong>20 public void d e s p l a z a ( int dx , int dy ) {21 // Aquí l a d e f i n i c i ó n d e l método22 }23 }Los interface al igual que las clases se pue<strong>de</strong>n exten<strong>de</strong>r, y a<strong>de</strong>más uninterface pue<strong>de</strong> exten<strong>de</strong>r a más <strong>de</strong> un interface, la herencia simple es unarestricción en el ámbito <strong>de</strong> las clases, los interface no poseen esta restricción.En el siguiente listado tienes un ejemplo:1 public interface Figura extends Dibujable , Transformable {2 public void g i r a ( f l o a t angulo ) ;3 }Listado 3.6: Un interface que extien<strong>de</strong> a otros dosCon el uso <strong>de</strong> los interface la Buena práctica <strong>de</strong> programar orientado alinterface toma aún más fuerza. Por cierto no <strong>con</strong>fundas la palabra reservadainterface <strong>con</strong> el <strong>con</strong>cepto <strong>de</strong> interface, este <strong>con</strong>cepto se refiere a los métodosaccesibles <strong>de</strong> una clase que son los que po<strong>de</strong>mos utilizar para trabajar <strong>con</strong> laclase.Hasta aquí hemos visto el grueso <strong>de</strong>l trabajo <strong>con</strong> herencia, hemos visto cuales su potencia y como trabajar <strong>con</strong> ella en <strong>Java</strong>. Y todo ello para llevarnos elsiguiente jarro <strong>de</strong> agua fría:Buenas prácticasEn tus diseños software, favorece la composición frente a la herencia.¿Qué significa esta buena práctica? ¿No <strong>de</strong>bemos utilizar nunca la herencia?No, todo lo <strong>con</strong>trario, la herencia es una técnica <strong>de</strong> POO a objetos muy potentesiempre que se la utilice bien. Un mal uso <strong>de</strong> la herencia es utilizarla parareaprovechar código. La herencia significa que entre los <strong>con</strong>ceptos que queremosabstraer existe una clara relación padre-hijo. No <strong>de</strong>bemos utilizar la herenciapara reaprovechar código entre clases que no están relacionadas lógicamente atraves <strong>de</strong> la herencia.Por ejemplo, tiene poco sentido que la clase Persona sea clase padre <strong>de</strong> laclase Ciudad simplemente porque una ciudad tenga un nombre. Este no es unbuen uso <strong>de</strong> la herencia. La relación entre esos entes en el mundo real no existey no <strong>de</strong>bemos trasladarla <strong>de</strong> un modo artificial a nuestro código.La composición, significa que una clase <strong>con</strong>tiene como atributos instancias<strong>de</strong> otras clases. Este mecanismo <strong>de</strong> relación entre clases es más flexible que laherencia y por lo tanto menos sensible a los cambios que tengamos que haceren nuestro código, más aún si como referencias a las clases utilizamos interfaceso clases abstractas.En <strong>de</strong>finitiva, favorecer la composición frente a la herencia significa usar laherencia sólo cuando esté justificado en nuestro diseño y no sólo por comodidad.


68CAPÍTULO 3. HERENCIA E INTERFACES3.5. Enumeraciones.Las enumeraciones son una <strong>con</strong>strucción <strong>de</strong>l lenguaje introducida en la versión5 <strong>de</strong> <strong>Java</strong>. Las enumeraciones nos sirven para <strong>de</strong>finir listas enumeradas <strong>de</strong>elementos y algo más, ya que en cierto modo son como clases, pue<strong>de</strong>n tener<strong>con</strong>structores, métodos y atributos. El primer elemento <strong>de</strong> la enumeración tienela posición 0.La única restricción que tiene las enumeraciones sobre las clases es que lasenumeraciones no se pue<strong>de</strong>n exten<strong>de</strong>r ya que implícitamente toda enumeraciónestá extendiendo a la clase java.lang.Enum. A<strong>de</strong>más, el ámbito <strong>de</strong> los <strong>con</strong>structores<strong>de</strong>be ser private o <strong>de</strong> paquete.La clase Enum nos proporciona algunos métodos útiles que po<strong>de</strong>mos utilizaren nuestra propias enumeraciones, como veremos a <strong>con</strong>tinuación. Po<strong>de</strong>mos<strong>de</strong>finir una enumeración <strong>de</strong> una manera tan sencilla como la mostrada en elsiguiente Listado:1 public enum Semana {2 LUNES( " P r i m e r día <strong>de</strong> la s e m a n a . " ) ,3 MARTES( " Ni te c a s e s ni te e m b a r q u e s . " ) ,4 MIERCOLES( " Sin c o m e n t a r i o s . " ) ,5 JUEVES( " S i e m p r e en m e d i o . " ) ,6 VIERNES( " Ú l t i m o día <strong>de</strong> t r a b a j o . " ) ,7 SABADO( " E m p i e z a el fin <strong>de</strong> s e m a n a . " ) ,8 DOMINGO( " M a ~n a n a <strong>de</strong> n u e v o a t r a b a j a r . " ) ;910 private S t r i n g comentario ;1112 // C o n s t r u c t o r a c c e s o <strong>de</strong> paquete o p r i v a t e .13 Semana ( S t r i n g comentario ) {14 t h i s . comentario = comentario ;15 }1617 public S t r i n g getComentario ( ) {18 return comentario ;19 }20 }Listado 3.7: Definición <strong>de</strong> una enumeración para los días <strong>de</strong> la semanaLa clase Enum nos proporciona el método values() que <strong>de</strong>vuelve un array<strong>de</strong> String <strong>con</strong> todos los nombre <strong>de</strong> los elementos <strong>de</strong> la enumeración. Cadauno <strong>de</strong> los elementos <strong>de</strong> la enumeración posee dos métodos heredados <strong>de</strong> laclase Enum uno para <strong>con</strong>ocer el nombre <strong>de</strong>l elemento name(), y otro para <strong>con</strong>ocerel ordinal <strong>de</strong>l elemento <strong>de</strong>ntro <strong>de</strong> la enumeración ordinal(), a<strong>de</strong>más,evi<strong>de</strong>ntemente, <strong>de</strong> los métodos que nosotros mismo hayamos <strong>de</strong>finido (el métodogetComentario(), en el ejemplo <strong>de</strong>l Listado 3.8. El siguiente Listado muestraun ejemplo <strong>de</strong> uso <strong>de</strong> la anterior enumeración:1 for ( Semana d i a : Semana . v a l u e s ( ) ) {2 System . out . p r i n t l n ( d i a . name ( ) ) ;3 System . out . p r i n t l n ( d i a . o r d i n a l ( ) ) ;4 System . out . p r i n t l n ( d i a . getComentario ( ) ) ;5 }Listado 3.8: Uso <strong>de</strong> una enumeración


3.6. PAQUETES EN JAVA. 693.6. Paquetes en <strong>Java</strong>.Los paquetes son una <strong>con</strong>strucción <strong>de</strong>l lenguaje <strong>Java</strong> que nos permite agruparclases que están lógicamente relacionadas en el mismo grupo o paquete. Para<strong>de</strong>notar que una clase pertenece a un <strong>de</strong>terminado paquete, se indica en la<strong>de</strong>finición <strong>de</strong> la clase <strong>con</strong> la palabra reservada package seguida <strong>de</strong>l nombre <strong>de</strong>lpaquete como en el siguiente ejemplo:1 package agenda ; // Esta c l a s e e s t á <strong>de</strong>ntro d e l paquete agenda23 c l a s s Persona { // V i s i b i l i d a d <strong>de</strong>ntro d e l paquete4 // D e f i n i c i ó n <strong>de</strong> l a c l a s e5 }Regla <strong>de</strong> <strong>con</strong>venciónLos nombres <strong>de</strong> paquetes se escriben en minúsculas. Si el nombre <strong>de</strong> un paqueteestá compuesto por más <strong>de</strong> una palabra, se separan mediante puntos.Un nombre válido <strong>de</strong> paquete <strong>con</strong> más <strong>de</strong> una palabra es: agenda.datos oagenda.io.teclado.El ámbito o visibilidad por <strong>de</strong>fecto <strong>de</strong> una clase es el paquete, por ello, para<strong>de</strong>notar que la visibilidad <strong>de</strong> una clase está restringida al paquete en la queestá <strong>de</strong>finida no se utiliza ningún modificador <strong>de</strong> acceso.Como ya sabemos, la visibilidad también se <strong>de</strong>fine sobre los miembros <strong>de</strong>una clase <strong>de</strong> tal manera que un miembro pue<strong>de</strong> se público, privado, protegidoo <strong>de</strong> paquete. Veamos <strong>con</strong> mas <strong>de</strong>talle como se restringe la visibilidad <strong>con</strong> cadauno <strong>de</strong> estos modificadores <strong>de</strong> acceso.El modificador <strong>de</strong> acceso private es el más restrictivo <strong>de</strong> los cuatro, unmiembro privado no es accesible por ninguna otra clase. Po<strong>de</strong>mos utilizar estemodificador <strong>de</strong> acceso para ocultar completamente miembros <strong>de</strong> una clase. Unaclase nunca se pue<strong>de</strong> <strong>de</strong>finir como private.El modificador <strong>de</strong> acceso por <strong>de</strong>fecto, o <strong>de</strong> paquete, hace visibles los miembros<strong>de</strong> una clase al resto <strong>de</strong> clases <strong>de</strong>ntro <strong>de</strong>l mismo paquete. Una clase pue<strong>de</strong><strong>de</strong>finirse como <strong>de</strong> paquete.El modificador <strong>de</strong> acceso protected, se comporta exactamente igual queel modificador por <strong>de</strong>fecto pero a<strong>de</strong>más permite que las clases hijas <strong>de</strong> la claseprotected puedan usar sus miembros a través <strong>de</strong> la herencia aunque estas claseshijas pertenezcan a paquetes distintos.El modificador <strong>de</strong> acceso public asigna la mayor visibilidad a los miembros<strong>de</strong> una clase. Un miembro público es accesible <strong>de</strong>s<strong>de</strong> cualquier otra clase sinimportar el paquete en el que esté <strong>de</strong>finido, o si existe una relación padre-hijaentre ellas.La Tabla 3.1 muestra todas las posibilida<strong>de</strong>s <strong>de</strong> acceso entre miembros <strong>de</strong>clases.En la Figura 3.1 su muestra gráficamente las posibilida<strong>de</strong>s según el modificador<strong>de</strong> acceso empleado.A partir <strong>de</strong> la versión 5 <strong>de</strong> <strong>Java</strong> se introdujo el <strong>con</strong>cepto <strong>de</strong> import static<strong>con</strong> la intención <strong>de</strong> facilitar la escritura <strong>de</strong> código. La i<strong>de</strong>a <strong>de</strong> los import static


70CAPÍTULO 3. HERENCIA E INTERFACES¿Es accesible? private paquete protected publicMisma clase SI SI SI SIClase/subclase <strong>de</strong>l paquete NO SI SI SISubclase otro paquete NO NO SI 3 SIClase otro paquete NO NO NO SITabla 3.1: Modificadores <strong>de</strong> acceso y su visibilidadFigura 3.1: Visibilidad <strong>de</strong> los miembros según el modificador <strong>de</strong> acceso utilizado.


3.7. CLASES E INTERFACE ANIDADOS 71es incluir una clase o un paquete <strong>de</strong> clases y po<strong>de</strong>r llamar a los miembros estáticos<strong>de</strong> las clases importadas sin necesidad <strong>de</strong> escribir el nombre <strong>de</strong> la clase, taly como se muestra en el siguiente Listado:1 // D e f i n i c i ó n <strong>de</strong> una c l a s e2 package paquete . subpaquete ;34 public c l a s s Clase {5 public s t a t i c void metodo ( ) {6 // D e f i n i c i ó n d e l método7 }8 }910 // D e f i n i c i ó n <strong>de</strong> o t r a c l a s e11 import s t a t i c paquete . subpaquete . Clase ;1213 public c l a s s ClaseQueUsaImports {14 public void otroMetodo ( ) {15 metodo ( ) ;Los import static son un arma <strong>de</strong> doble filo, por un lado facilitan la codificación,pero por otro se pier<strong>de</strong> la perspectiva <strong>de</strong> la pertenencia <strong>de</strong> los miembrosstatic a sus clases <strong>con</strong>cretas. Hay que utilizarlos <strong>con</strong> precaución. Un caso <strong>de</strong>uso comunmente extendido es en las pruebas unitarias, para incluir los miembrosestáticos <strong>de</strong> los frameworks como JUnit .3.7. Clases e interface anidadosHasta este momento hemos <strong>de</strong>finido cada una <strong>de</strong> nuestras clases en un ficheroin<strong>de</strong>pendiente <strong>con</strong> extensión .java. No obstante, en un mismo fichero po<strong>de</strong>mos<strong>de</strong>finir más <strong>de</strong> una clase siempre que sólo una <strong>de</strong> ellas sea public y su nombrecoincida <strong>con</strong> el <strong>de</strong>l fichero. El ámbito o visibilidad <strong>de</strong>l resto <strong>de</strong> clases <strong>de</strong>be ser elpaquete (recuerda, visibilidad por <strong>de</strong>fecto). Aunque lo a<strong>con</strong>sejable es que, <strong>con</strong>in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong>l ámbito, cada clase esté <strong>de</strong>finida en un fichero distinto, ya queesto favorece el mantenimiento <strong>de</strong>l código.Si embargo, <strong>de</strong>ntro <strong>de</strong> la <strong>de</strong>finición <strong>de</strong> una clase po<strong>de</strong>mos <strong>de</strong>finir nuevasclases e interface como se muestra en el siguiente Listado:1 public c l a s s Persona {2 private S t r i n g nombre ;3 private S t r i n g a p e l l i d o s ;4 private D i r e c c i o n d i r e c c i o n ;5 private c l a s s D i r e c c i o n {6 private S t r i n g c a l l e ;7 private int numero ;8 private S t r i n g puerta ;9 private S t r i n g p o b l a c i o n ;10 private S t r i n g p r o v i n c i a ;11 }1213 public interface LeerDatos {14 public S t r i n g getNombre ( ) ;15 }Listado 3.9: Uso <strong>de</strong> una enumeraciónA la clase Direccion así <strong>de</strong>finida se le llama clase interna y, a efectos <strong>de</strong>programación es una nueva clase como cualquier otra. De igual modo interfaceLeerDatos es un interface como otro cualquiera.


72CAPÍTULO 3. HERENCIA E INTERFACESHay un caso particular <strong>de</strong> creación <strong>de</strong> clases internas en el que la nueva claseno recibe ningún nombre como se muestra en el siguiente Listado, <strong>con</strong>tinuación<strong>de</strong>l anterior:17 public LeerDatos l e c t o r = new LeerDatos ( ) {18 private Persona persona ;1920 @Overri<strong>de</strong>21 public S t r i n g getNombre ( ) {22 return nombre ;23 }Listado 3.10: Uso <strong>de</strong> una enumeraciónFíjate que en la línea 17 parece que se está intentando instanciar uninterface, cosa que como sabes no está permitida. Lo que está ocurriendoes que se está creando e instanciando una nueva clase sin nombre, y por lo tantoanónima, que está implementando el interface LeerDatos. Se está creandoe instanciando la clase interna anónima al mismo tiempo, este es el único momentoen el que se pue<strong>de</strong> instanciar una clase interna anónima, ya que por seranónima no tienen nombre y por lo tanto no po<strong>de</strong>mos <strong>de</strong>finir sus <strong>con</strong>structores.Las clases internas anónimas son una <strong>con</strong>strucción muy potente <strong>de</strong>l lenguaje.Veremos toda su potencia en el Capítulo 11 <strong>de</strong>dicado a la creación <strong>de</strong> interfacesgráficos <strong>de</strong> usuario, y en el Capítulo 14 <strong>de</strong>dicado a la programación <strong>con</strong>currente<strong>con</strong> hilos.Cuestiones.1. ¿Tiene sentido <strong>de</strong>clarar los <strong>con</strong>structores <strong>de</strong> una clase como private? ¿Sepodrán crear instancias <strong>de</strong> una clase en la que todos sus <strong>con</strong>structores sonprivate? ¿Se podrá exten<strong>de</strong>r una clase en la que todos sus <strong>con</strong>structoresson private?.Ejercicios.1. Modifica tu implementación <strong>de</strong> la clase Agenda que escribiste en el ejercicio3 <strong>de</strong>l Capítulo 2, para que pueda trabajar, <strong>de</strong> modo transparente, tanto<strong>con</strong> instancias <strong>de</strong> tipo Persona como <strong>con</strong> instancias <strong>de</strong> tipo Empresa.2. Amplia la clase Persona para que <strong>con</strong>tenga información sobre la dirección<strong>de</strong> resi<strong>de</strong>ncia <strong>de</strong> la persona.3. Amplia la clase Empresa para que <strong>con</strong>tenga información sobre la dirección<strong>de</strong> la se<strong>de</strong> <strong>de</strong> la empresa.4. Modifica tu agenda para que sea capaz <strong>de</strong> trabajar <strong>con</strong> los nuevos tipos<strong>de</strong> datos <strong>de</strong>finidos.


3.7. CLASES E INTERFACE ANIDADOS 73Lecturas recomendadas.El capítulo 7 <strong>de</strong>l libro <strong>de</strong> Sierra y Bates [3] expone gráficamente todos los<strong>con</strong>ceptos relacionados <strong>con</strong> la herencia en <strong>Java</strong>.Para una exposición más <strong>de</strong>tallada sobre clases e interface anidados unaexcelente referecia es el capítulo 5 <strong>de</strong>l libro <strong>de</strong> James Gosling [2].Para un fundamentado razonamiento <strong>de</strong> porqué favorecer la composiciónsobre la herencia véase el item 14 <strong>de</strong> la referencia [4].


74CAPÍTULO 3. HERENCIA E INTERFACES


Capítulo 4Control <strong>de</strong> versiones <strong>con</strong>SubversionContenidos4.1. ¿Qué es un sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones? . . . 764.2. Principales características <strong>de</strong> Subversion . . . . . 764.3. Creación <strong>de</strong> un repositorio . . . . . . . . . . . . . . 774.4. Trabajo <strong>con</strong> repositorios . . . . . . . . . . . . . . . 784.4.1. Obteniendo información <strong>de</strong>l repositorio . . . . . . . 824.5. Integración <strong>con</strong> Eclipse . . . . . . . . . . . . . . . . 84IntroducciónEste es el primer capítulo <strong>de</strong>dicado a una herramienta utilizada en el <strong>de</strong>sarrollo<strong>de</strong> <strong>proyectos</strong> informáticos y no directamente al lenguaje <strong>de</strong> programación <strong>Java</strong>.Como ya se comentó en la introducción, el objetivo <strong>de</strong> este libro es mostrarcómo <strong>de</strong>sarrollar <strong>proyectos</strong> informáticos <strong>con</strong> tecnología <strong>Java</strong> en un <strong>con</strong>texto<strong>de</strong> <strong>de</strong>sarrollo lo más cercano posible al que el programador va a en<strong>con</strong>trar encualquier organización <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> software. Lo que hace realmente valiosoa un programador no es sólo que <strong>con</strong>ozca lenguajes <strong>de</strong> programación, si no que<strong>con</strong>ozca las herramientas <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> software más utilizadas y que, <strong>de</strong>s<strong>de</strong> unprincipio, se integre suavemente en un equipo <strong>de</strong> <strong>de</strong>sarrollo. Y esta integracióntiene mucho que ver <strong>con</strong> toda la experiencia que posea <strong>con</strong> el trabajo en grupoy las herramientas que lo favorecen.En este capítulo se presenta la herramienta <strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones Subversión,que, incluso trabajando individualmente, se hace imprescindible en todoproyecto para la gestión <strong>de</strong> las diferentes versiones <strong>de</strong>l proyecto a medida queeste evoluciona.75


76CAPÍTULO 4. SUBVERSION4.1. ¿Qué es un sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones?El escenario más usual en el <strong>de</strong>sarrollo <strong>de</strong> software es un equipo formado porvarios programadores, probablemente coordinado por un jefe <strong>de</strong> proyecto. Seacual sea la metodología que se utilice en el <strong>de</strong>sarrollo <strong>de</strong>l proyecto, el código vaa estar sujeto a <strong>con</strong>tinuas modificaciones por parte <strong>de</strong> todos los programadores<strong>de</strong>l equipo. En este escenario, no es raro en<strong>con</strong>trar que dos programadores hanmodificado el mismo fragmento <strong>de</strong> código <strong>de</strong> modo que se llegue a <strong>con</strong>flictoscuando se quiere unificar el código <strong>de</strong>l proyecto. Otra necesidad <strong>de</strong>l equipo esgarantizar que todos los programadores pue<strong>de</strong>n disponer <strong>de</strong> la última versión<strong>de</strong>l código <strong>de</strong>l proyecto.Un Sistema <strong>de</strong> Control <strong>de</strong> versiones es una herramienta software que, <strong>de</strong>manera automática, se encarga <strong>de</strong> facilitar la gestión <strong>de</strong> las versiones <strong>de</strong>l código<strong>de</strong> un proyecto <strong>de</strong> manera centralizada.4.2. Principales características <strong>de</strong> SubversionSubversion es una herramienta centralizada <strong>de</strong> ayuda al <strong>con</strong>trol <strong>de</strong> versiones.Su uso no es exclusivo en el <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> informáticos, si no quepue<strong>de</strong> utilizarse en cualquier proyecto que requiera <strong>de</strong> un sistema automático<strong>de</strong> <strong>con</strong>trol <strong>de</strong> versiones.El <strong>con</strong>cepto central en Subversion es el Repsositorio . Por repositorio seentien<strong>de</strong> la última versión <strong>de</strong>l proyecto que existe en el sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong>versiones.El paradigma que Suversion utiliza es Copia-Modificación-Fusión (Copy-Modify-Merge en inglés). En este paradigma, cada uno <strong>de</strong> los miembros <strong>de</strong>lequipo, cuando empieza a trabajar en el proyecto, hace una copia local <strong>de</strong>l<strong>con</strong>tenido <strong>de</strong>l repositorio; modifica su copia local y finalmente fusiona sus modificacioneslocales <strong>con</strong> el código <strong>de</strong>l repositorio, resolviendo los posibles <strong>con</strong>flictoque hayan aparecido. Al finalizar esta fase, se dice que se ha creado una nuevaversión <strong>de</strong>l proyecto en el repositorio.Una <strong>de</strong> las característica principales <strong>de</strong> Subversion es que las actualizacionesen el repositorio son incrementales, sólo se actualizan los ficheros que se hanmodificado <strong>con</strong> respecto a la versión anterior. Otra característica es relativa a lanumeración <strong>de</strong> la versión <strong>de</strong>l repositorio, cada vez que se realiza una modificaciónen el repositorio, se actualiza la versión <strong>de</strong> todos los ficheros existentes en elrepositorio y no únicamente <strong>de</strong> los ficheros que se han modificado.Por otro lado, se pue<strong>de</strong> trabajar <strong>con</strong> Subversion <strong>de</strong> manera local sobre elpropio sistema <strong>de</strong> ficheros don<strong>de</strong> se realiza el <strong>de</strong>sarrollo, o sobre un servidor enred. Y en este último caso, el servidor utilizado pue<strong>de</strong> ser el propio servidor adhocque viene incluido <strong>con</strong> la distribución <strong>de</strong> Subversion (svnserve), o como unmódulo <strong>de</strong> Apache. La elección <strong>de</strong>l modo <strong>de</strong> trabajo <strong>con</strong> Subversion se verá reflejadaen la URL que utilizaremos para acce<strong>de</strong>r al repositorio. Dependiendo <strong>de</strong>lprotocolo utilizado, las opciones son las que aparecen en la Tabla 4.1.En la primera <strong>de</strong> las opciones <strong>de</strong> la Tabla 4.1 se acce<strong>de</strong> directamente alrepositorio en el sistema <strong>de</strong> ficheros. En la segunda <strong>de</strong> las opciones se acce<strong>de</strong>utilizando el servidor ad-hoc que viene incluido en la propia distribución <strong>de</strong>Subversion. En la tercera opción se utiliza Subversion a través <strong>de</strong> un túnel ssh.La cuarta opción permite el acceso a un repositorio a través <strong>de</strong> Apache y el


4.3.CREACIÓN DE UN REPOSITORIO 77file:///svn://svn+ssh://http://https://El repositorio se encuentra en el disco local.El acceso al repositorio se realiza a través <strong>de</strong>l servidorsvnserve.El acceso al repositorio se realiza a través <strong>de</strong>l servidorsvnserve utilizando un túnel SSHEl acceso al repositorio se realiza a través <strong>de</strong> Apache <strong>con</strong> elmódulo WebDAV.El acceso al repositorio ser realiza <strong>con</strong> encriptación SSL através <strong>de</strong> Apache <strong>con</strong> el módulo WebDAV.Tabla 4.1: Tipos <strong>de</strong> acceso a los repositorios Subversion.módulo WebDAV (<strong>de</strong>l inglés Web-based Distributed Authoring and Versioning).Finalmente, en la última opción se acce<strong>de</strong> al respositorio a través <strong>de</strong> un servidorApache <strong>con</strong> encriptación ssl (<strong>de</strong>l inglés Secure Socket Layer).Cada una <strong>de</strong> estas opciones tiene sus ventajas y <strong>de</strong>sventajas. En las próximassecciones utilizaremos el protocolo svn:// para acce<strong>de</strong>r a un repositorio a través<strong>de</strong>l servidor svnserve. El trabajo <strong>con</strong> Subversion es in<strong>de</strong>pendiente <strong>de</strong>l protocoloutilizado.4.3. Creación <strong>de</strong> un repositorioLa creación <strong>de</strong> un nuevo repositorio se hace utilizando la herramienta svnadminincluida en la distribución <strong>de</strong> Subversion. Supongamos que hemos creado el directorio./Repositorio (directorio Repositorio en la raíz <strong>de</strong> nuestro directorio<strong>de</strong> usuario), en nuestro disco duro local. Para crear un repositorio Subversionen este directorio, en una <strong>con</strong>sola escribiríamos:~$ svnadmin create ~./RepositorioSi examinamos el <strong>con</strong>tenido <strong>de</strong>l directorio veremos que se han creado lossiguiente subdirectorios y ficheros <strong>de</strong>ntro <strong>de</strong>l directorio ./Repositorio:drwxr-xr-x 8 oscar staff 272 23 may 18:48 .drwxr-xr-x 32 oscar staff 1088 23 may 18:48 ..-rw-r--r-- 1 oscar staff 229 23 may 18:48 README.txtdrwxr-xr-x 5 oscar staff 170 23 may 18:48 <strong>con</strong>fdrwxr-sr-x 16 oscar staff 544 23 may 18:48 db-r--r--r-- 1 oscar staff 2 23 may 18:48 formatdrwxr-xr-x 11 oscar staff 374 23 may 18:48 hooksdrwxr-xr-x 4 oscar staff 136 23 may 18:48 locksEl fichero README.txt <strong>con</strong>tiene un aviso sobre cómo <strong>de</strong>be ser usado estedirectorio, sólo a través <strong>de</strong> las herramientas que proporciona Subversion. Eldirectorio hooks <strong>con</strong>tiene scripts básicos para el trabajo <strong>con</strong> Subversion. El directoriolocks es utilizado por Subversion para los bloqueos <strong>de</strong>l repositorio. Eldirectorio db es el que emplea Subversion para registrar todos los cambios realizadosen el <strong>con</strong>tenido <strong>de</strong>l repositorio, es el corazón <strong>de</strong>l repositorio. Finalmente,el directorio <strong>con</strong>f es don<strong>de</strong> se encuentran los ficheros <strong>de</strong> <strong>con</strong>figuración para elacceso al servidor <strong>de</strong> Subversion.


78CAPÍTULO 4. SUBVERSIONA efectos prácticos, el directorio don<strong>de</strong> vamos a realizar tareas <strong>de</strong> <strong>con</strong>figuraciónes <strong>con</strong>f. Su <strong>con</strong>tenido es el siguiente:drwxr-xr-x 5 oscar staff 170 23 may 18:48 .drwxr-xr-x 8 oscar staff 272 23 may 18:48 ..-rw-r--r-- 1 oscar staff 1080 23 may 18:48 authz-rw-r--r-- 1 oscar staff 309 23 may 18:48 passwd-rw-r--r-- 1 oscar staff 2279 23 may 18:48 svnserve.<strong>con</strong>fEn el fichero svnserve.<strong>con</strong>f indicamos las opciones <strong>de</strong> acceso al repositorio,en particular po<strong>de</strong>mos restringir los permisos <strong>de</strong> lectura y escritura para cadauno <strong>de</strong> los usuarios a cada uno <strong>de</strong> los directorios que <strong>con</strong>tiene nuestro repositorioa través <strong>de</strong> lo especificado en el fichero authz, y los usuarios y claves <strong>de</strong> accesoal repositorio en el fichero passwd.Como ejemplo únicamente vamos a especificar los usuarios y sus claves enel fichero passwd sin modificar el fichero authz, lo que implica que todos losusuarios dados <strong>de</strong> alta en el fichero passwd tendrán acceso total a todos losdirectorios y ficheros <strong>de</strong>l repositorio.Para activar la opción <strong>de</strong> acceso a través <strong>de</strong> usuario autorizado hay que<strong>de</strong>scomentar la línea:password-db = passw<strong>de</strong>sta línea indica el nombre <strong>de</strong>l fichero <strong>de</strong> pares usuario/clave para nuestro repositorio.Después <strong>de</strong> <strong>de</strong>scomentar esta línea <strong>de</strong>bemos editar el fichero passwdy añadir los usuario y sus claves siguiendo el ejemplo que en<strong>con</strong>traremos en él:# harry = harryssecret# sally = sallyssecretoscar = clave_secretaCon esto ya tenemos activa la <strong>con</strong>figuración más básica para nuestro repositorio,al que sólo pue<strong>de</strong> acce<strong>de</strong>r el usuario oscar <strong>con</strong> permisos <strong>de</strong> lectura yescritura para todos los directorios y ficheros <strong>de</strong>l repositorio.El siguiente paso antes <strong>de</strong> empezar a trabajar <strong>con</strong> nuestro repositorio esiniciar el servidor <strong>de</strong> Subversion <strong>de</strong>l siguiente modo:~$ sudo svnserve --daemonEs necesario tener permisos <strong>de</strong> administrador para iniciar el servidor <strong>de</strong>Subversion como un proceso daemon.A partir <strong>de</strong> ahora ya po<strong>de</strong>mos empezar a trabajar <strong>con</strong> nuestro repositorio.4.4. Trabajo <strong>con</strong> repositoriosSupongamos que, por claridad, elegimos nombrar al directorio que va a mantenernuestra copia <strong>de</strong> trabajo como . /CopiaTrabajo (en Eclipse nuestra copia<strong>de</strong> trabajo será algo como . /workspace/NombreProyecto, no es necesario quelos nombre <strong>de</strong>l proyecto y <strong>de</strong>l repositorio coincidan). El primer paso que <strong>de</strong>bemosdar es importar el <strong>con</strong>tenido <strong>de</strong>l directorio CopiaTrabajo al repositorioSubversion, suponiendo que nos en<strong>con</strong>tramos en el directorio CopiaTrabajo, <strong>de</strong>este modo:


4.4. TRABAJO CON REPOSITORIOS 79~/CopiaTrabajo$ svn import . svn://localhost/home/oscar/Repositorio/trunk -m "Import inicial <strong>de</strong>l proyecto"El . correspon<strong>de</strong> al directorio actual, y la dirección que aparece a <strong>con</strong>tinuaciónsvn://localhost/home/oscar/Repositorio/trunk correspon<strong>de</strong> a la direccióndon<strong>de</strong> se encuentra el repositorio. Finalmente -m Ïmport inicial <strong>de</strong>lproyecto" es un mensaje <strong>de</strong>scriptivo <strong>de</strong> lo que estamos haciendo para que, aposteriori, resulte cómodo en<strong>con</strong>trar una <strong>de</strong>terminada versión <strong>de</strong>l proyecto. Eneste momento se solicitará la clave <strong>de</strong>l usuario que hemos activado en el fichero<strong>de</strong> <strong>con</strong>figuración passwd.Te habrás dado cuenta <strong>de</strong> que estamos añadiendo el subdirectorio trunk alrepositorio, esto forma parte <strong>de</strong> las buenas prácticas <strong>de</strong> trabajo <strong>con</strong> Subversion.Buenas prácticasEn nuestro directorios Subversion es recomendable crear los siguiente subdirectorios:trunk: Directorio don<strong>de</strong> se encuentra la versión en <strong>de</strong>sarrollo <strong>de</strong>l proyecto.branches: Directorio don<strong>de</strong> se encuentran las posibles ramificaciones <strong>de</strong>l proyecto.tags: Directorio don<strong>de</strong> se encuentran las versiones finales (releases).Para que los ficheros y directorios en CopiaTrabajo se <strong>con</strong>viertan en unacopia <strong>de</strong> trabajo real, necesitamos hacer el checkout <strong>de</strong>l repositorio hacia nuestracopia <strong>de</strong> trabajo <strong>de</strong> este modo (fíjate en el punto final ✭✭.✮✮ para indicar queel checkout lo queremos hacer sobre el directorio actual):~/CopiaTrabajo$ svn checkout svn://localhost/home/oscar/Repositorio/trunk .En este momento tenemos sincronizada la copia en el repositorio <strong>con</strong> nuestracopia <strong>de</strong> trabajo. Para seguir la estructura típica <strong>de</strong> un proyecto Eclipse creemosel directorio src para <strong>con</strong>tener los fuentes <strong>de</strong>l proyecto. Cualquier modificaciónen el directorio <strong>de</strong> trabajo la tenemos que hacer utilizando Subversion, luego, enla <strong>con</strong>sola <strong>de</strong>bemos escribir lo siguiente para crear un directorio que Subversionpueda sincronizar <strong>con</strong> el repositorio:~/CopiaTrabajo$ svn mkdir srcA srcSubversion nos indicará que se ha añadido (A) el directorio src a la copialocal. Ahora, <strong>de</strong>ntro <strong>de</strong>l directorio creado en el paso anterior creamos un fichero<strong>de</strong> <strong>de</strong>finición <strong>de</strong> clase llamado Saludo.java, y le indicamos a Subverion que loañadimos a nuestra copia local:~/CopiaTrabajo$ touch src/Saludo.java~/CopiaTrabajo$ svn add Saludo.javaA src/Saludo.java


80CAPÍTULO 4. SUBVERSIONEn este momento el fichero Saludo.java no se ha enviado al repositorio,sólo se ha marcado para que la próxima vez que se haga un commit este ficherose añada efectivamente al Repositorio.Lo siguiente que <strong>de</strong>bemos hacer es subir al repositorio todos los cambios quehemos hecho en nuestra copia local, <strong>de</strong> este modo:~/CopiaTrabajo$ svn commit -m "A~nadiendo la clase Saludo alrepositorio"Observa que es necesario añadir un mensaje <strong>de</strong>scriptivo <strong>de</strong> lo que estamoshaciendo <strong>con</strong> la opción -m ”Texto <strong>de</strong>l mensaje”, si lo olvidas, Subversion te lopedirá. La respuesta que verás en <strong>con</strong>sola por parte <strong>de</strong> Subversion será parecidaa esta:A~nadiendo srcA~nadiendo src/Saludo.javaTransmitiendo <strong>con</strong>tenido <strong>de</strong> archivos .Commit <strong>de</strong> la revisión 2.Don<strong>de</strong> se nos informa que la última versión disponible en el repositorio es la 2.A la última versión se le llama HEAD. Ahora realiza algún pequeño cambio en elfichero Saludo.java, como añadir un simple comentario y graba tu fichero. Para<strong>con</strong>ocer el estado <strong>de</strong> tu copia local <strong>con</strong> respecto a la última versión existente enel Repositorio pue<strong>de</strong>s utilizar la instrucción status <strong>con</strong> la opción -v tecleandoen <strong>con</strong>sola:~/CopiaTrabajo$ svn status -v1 1 oscar .2 2 oscar srcM 2 2 oscar src/Saludo.javaEste texto nos informa que el fichero Saludo.java se ha modificado (letrainicial M), y en el próximo commit se subirá la versión local al repositorio, laopción -v significa verbose, es <strong>de</strong>cir queremos una <strong>de</strong>scripción <strong>de</strong>tallada <strong>de</strong>lestado <strong>de</strong> la copia local. Veamos el resultado <strong>de</strong> un commit escribiendo:~/CopiaTrabajo$ svn commit -m "Una modificación en el ficheroSaludo.java"Enviando src/Saludo.javaTransmitiendo <strong>con</strong>tenido <strong>de</strong> archivos .Commit <strong>de</strong> la revisión 3.Este texto nos informa, <strong>de</strong> nuevo, que no ha habido ningún problema al subirla nueva copia local al Repsositorio.En un equipo <strong>de</strong> <strong>de</strong>sarrollo cualquier otro programador pue<strong>de</strong> realizar cambiossobre algún fichero en el Repositorio, o pue<strong>de</strong> añadir nuevos ficheros ydirectorios al repositorio, para <strong>con</strong>ocer en todo momento cual es el estado <strong>de</strong>nuestra copia local <strong>con</strong> respecto a la última versión existente en el Repositoriopo<strong>de</strong>mos escribir:~/CopiaTrabajo$ svn status -u* 3 src/Saludo.javaEstado respecto a la revisión: 5


4.4. TRABAJO CON REPOSITORIOS 81La opción -u indica que se compare nuestra versión local <strong>con</strong> respecto ala última versión en el Repositorio, si no la incluimos la comparación se realizaráentre la copia local y la última versión que nos <strong>de</strong>scargamos <strong>de</strong>s<strong>de</strong> elRepositorio que pue<strong>de</strong> no ser la versión HEAD <strong>de</strong>l Repositorio. El * indica que elfichero Saludo.java en mi copia <strong>de</strong> trabajo está en la versión 3 mientras queen el repositorio la última versión es la 5. Si queremos <strong>con</strong>ocer cual es el estadolocal <strong>de</strong> nuestros fichero <strong>con</strong> respecto <strong>de</strong> la última actualización escribimos:~/CopiaTrabajo$ svn diffIn<strong>de</strong>x: src/Saludo.java==============================================--- src/Saludo.java (revisión: 3)+++ src/Saludo.java (copia <strong>de</strong> trabajo)@@ -1,2 +1,3 @@public class Saludo {+ // Un comentario}\ No newline at end of fileSi vemos un signo ✭✭+✮✮ al inicio <strong>de</strong> la línea significa que esa línea se ha añadido<strong>con</strong> respecto <strong>de</strong> la última actualización que hicimos (que no tiene porqué coincidir<strong>con</strong> la última versión que existe en el repositorio). Si la línea empieza <strong>con</strong>un signo ✭✭-✮✮ indica que esa línea sea ha eliminado. Si ahora intentamos hacerun commit obtendremos el siguiente error:~/CopiaTrabajo$ svn commit -m "Intentando un commit que fallará"Enviando src/Saludo.javaTransmitiendo <strong>con</strong>tenido <strong>de</strong> archivos .svn: Falló el commit(<strong>de</strong>talles a <strong>con</strong>tinuación):svn: El archivo ’/trunk/src/Saludo.java’ está <strong>de</strong>sactualizadoEste error se <strong>de</strong>be a que nuestra copia local se encuentra en la versión 3 yla última versión en el repositorio es la 5, luego es necesario que primero actualicemosnuestra copia local a la última versión en el repositorio, y en segundolugar que enviemos los cambios. Para actualizar nuestra copia local a la últimaversión <strong>de</strong>l Repositorio (HEAD) escribimos:~/CopiaTrabajo$ svn updateG svn/Saludo.javaActualizado a la revisión 5.Esta vez, la letra G al inicio <strong>de</strong> la línea indica que Subversion ha sido capaz<strong>de</strong> mezclar (merge en ingles) la última revisión existente en el Repositorio <strong>con</strong>los cambios locales en nuestro fichero y no ha en<strong>con</strong>trado <strong>con</strong>flictos. Si dos programadoresno modifican las mismas líneas <strong>de</strong> código, si no que las discrepanciasaparecen el líneas <strong>de</strong> código distintas, no aparecerá ningún <strong>con</strong>flicto cuando Subversionintente mezclar el código <strong>de</strong> nuestra copia local <strong>con</strong> el <strong>de</strong> la copia en elRepositorio. En este caso tenemos, en nuestra copia local, la última versión enel Repositorio más nuestros cambios que se han añadido sin <strong>con</strong>flicto, ahora yapo<strong>de</strong>mos hacer <strong>de</strong> nuevo un commit:


82CAPÍTULO 4. SUBVERSION~/CopiaTrabajo$ svn commit -m "Ya actualizado subo mis cambios"Enviando src/Saludo.javaTransmitiendo <strong>con</strong>tenido <strong>de</strong> archivos .Commit <strong>de</strong> la revisión 6.Ahora sí que ha tenido éxito el commit puesto que la versión <strong>de</strong> nuestra copialocal coinci<strong>de</strong> <strong>con</strong> la última versión en el repositorio.En otras ocasiones, cuando una línea ha sido modificada por dos o másprogramadores, Subversion no sabrá cómo resolver el <strong>con</strong>flicto por sí solo, y enel momento <strong>de</strong> intentar hacer un update seremos informados <strong>de</strong> que existe un<strong>con</strong>flicto, como en el caso siguiente:~/CopiaTrabajo$ svn diffIn<strong>de</strong>x: src/Saludo.java================================================--- src/Saludo.java (revisión: 7)+++ src/Saludo.java (copia <strong>de</strong> trabajo)@@ -1,4 +1,7 @@@author Oscarpublic class Saludo {+> .r7}\ No newline at end of fileEn este mensaje se nos informa que hay un <strong>con</strong>flicto ya que nuestra copialocal <strong>con</strong>tiene la línea <strong>de</strong>l comentario que ha sido eliminada en el repositorio, <strong>de</strong>hecho, las líneas extra que aparecen en el código se han añadido realmente alfichero Saludo.java. En este caso <strong>de</strong>bemos resolver el <strong>con</strong>flicto a mano, y unavez resuelto (por ejemplo, eliminando todas las líneas insertadas y manteniendoel comentario) se lo indicamos a Subversion <strong>de</strong>l siguiente modo:~/CopiaTrabajo$ svn resolved Saludo.javaSe resolvió el <strong>con</strong>flicto <strong>de</strong> ’src/Saludo.java’De nuevo, po<strong>de</strong>mos seguir trabajando <strong>con</strong> nuestro repositorio como hasta elmomento o hasta que aparezca un nuevo <strong>con</strong>flicto.4.4.1. Obteniendo información <strong>de</strong>l repositorioSin ánimo <strong>de</strong> ser exhaustivos <strong>con</strong> respecto a las posibilidad para obtener informaciónsobre un repositorio Subversión, aquí mostramos algunas <strong>de</strong> las opcionespara <strong>con</strong>ocer el estado <strong>de</strong>l repositorio y <strong>de</strong> nuestra copia local. Subversion nosproporciona instrucciones para <strong>con</strong>ocer en cualquier momento información sobreel repositorio.Con svn log obtenemos información sobre los mensajes que fueron adjuntados<strong>con</strong> cada nuevo commit, tal y como se muestra a <strong>con</strong>tinuación:


4.4. TRABAJO CON REPOSITORIOS 83--------------------------------------------------------------r2 | oscar | 2010-05-17 09:44:03 +0200 (lun, 17 may 2010) | 1 lineCódigo <strong>de</strong>l capítulo Clases.--------------------------------------------------------------r1 | oscar | 2010-05-17 09:43:33 +0200 (lun, 17 may 2010) | 1 lineInitial import.--------------------------------------------------------------Si estamos interesados en alguna revisión en particular, po<strong>de</strong>mos indicarlo<strong>con</strong> la opción -r como en el siguiente ejemplo:caterva:Libro<strong>Java</strong> oscar$ svn log -r 10--------------------------------------------------------------r10 | oscar | 2010-06-25 10:31:51 +0200 (vie, 25 jun 2010) | 1 linePara el Capítulo <strong>de</strong> Entrada/Salida.--------------------------------------------------------------Para <strong>con</strong>ocer el estado <strong>de</strong>l repositorio po<strong>de</strong>mos usar svn list. De nuevo, siestamos interesados en el estado <strong>de</strong>l repositorio para una <strong>de</strong>terminada revisiónpo<strong>de</strong>mos utilizar la opción -r tal y como se muestra en el siguiente ejemplo:caterva:Libro<strong>Java</strong> oscar$ svn list -r 3clases/herencia/Si lo que nos interesa es <strong>con</strong>ocer el estado <strong>de</strong> las últimas modificaciones <strong>de</strong>nuestra copia local <strong>con</strong> respecto al repositorio, po<strong>de</strong>mos utilizar la instrucciónsvn status, <strong>con</strong> lo que obtendremos información <strong>de</strong>l modo siguiente:? Punto.java! Nuevo.javaA Punto3D.javaC Principal.javaD PruebaPunto.javaM Ejemplo.javaL Hola.javaLa letra mayúscula <strong>de</strong> la primera columna, antes <strong>de</strong>l nombre <strong>de</strong> cada fichero,es un código que nos indica el estado <strong>de</strong>l fichero o directorio <strong>con</strong> el siguientesignificado:En la tabla 4.2, el último código indica que el fichero ha quedado bloqueado.Esta situación pue<strong>de</strong> ocurrir cuando, por ejemplo, al intentar hacer un commitla <strong>con</strong>exión <strong>con</strong> el repositorio remoto se pier<strong>de</strong> y no se pue<strong>de</strong> acabar el envío. Alrealizar cambios en la copia local, Subversion va acumulando en una lista todaslas tareas pendientes. En el momento en que se <strong>de</strong>sea sincronizar la copia local<strong>con</strong> la remota, se bloquean los ficheros que se van a sincronizar. Si por algunarazón alguna <strong>de</strong> las tareas pendientes no se pue<strong>de</strong> llevar a cabo, el resultadoserá que algún fichero pue<strong>de</strong> quedar bloqueado.


84CAPÍTULO 4. SUBVERSION? El fichero no se está añadido al repositorio! No existe una copia local <strong>de</strong> este ficheroA El fichero se ha marcado para añadir al repositorioC Existen <strong>con</strong>flictos entre la copia local y el repositorioD El fichero se ha marcado para ser borrado <strong>de</strong>l repositorioM Hay modificaciones en la copia local <strong>de</strong>l ficheroLEl fichero está bloqueadoTabla 4.2: Códigos <strong>con</strong> información sobre el estado <strong>de</strong> la copia local <strong>de</strong>l ficherosobre la copia remota.Para eliminar los bloqueos po<strong>de</strong>mos utilizar la instrucción svn cleanup,esta instrucción comprueba el listado <strong>de</strong> tareas a realizar, y si queda algunapendiente, intenta realizarla, al final <strong>de</strong> lo cual, se eliminará el bloqueo sobrelos ficheros.Finalmente, si lo que nos interesas <strong>con</strong>ocer <strong>con</strong> <strong>de</strong>talle son los cambios producidosen los ficheros entre la copia local y la existente en el repositorio po<strong>de</strong>mosutilizar svn diff.4.5. Integración <strong>con</strong> EclipseAunque el trabajo <strong>con</strong> Subversion <strong>de</strong>s<strong>de</strong> línea <strong>de</strong> instrucciones es bastante sencillo,resulta interesante no tener que abandonar Eclipse para trabajar <strong>con</strong> elRepositorio <strong>de</strong> nuestro proyecto. Como se comentó en la Sección 1.4.1, se pue<strong>de</strong>añadir nueva funcionalidad a Eclipse instalando nuevos plug-ins, y precisamenteexisten excelentes plug-ins para trabajar <strong>con</strong> Subversion <strong>de</strong>s<strong>de</strong> Eclipse. Uno <strong>de</strong>ellos es Subclipse, (http://subclipse.tigris.org/), aunque existe otros excelentesplug-ins como Subversive (http://www.eclipse.org/subversive/).Elegir entre uno u otro acaba siendo cuestión <strong>de</strong> gustos ya que todos ellos sonexcelentes, la mejor i<strong>de</strong>a es probar algunos <strong>de</strong> ellos y quedarnos <strong>con</strong> el que másse adapte a nuestra forma <strong>de</strong> trabajo.La última versión <strong>de</strong>l plug-in Subclipse pue<strong>de</strong>s en<strong>con</strong>trarla en la direcciónhttp://subclipse.tigris.org. En la sección Dowload and Install en<strong>con</strong>trarásla última release y la URL <strong>de</strong>s<strong>de</strong> don<strong>de</strong> instalar el plug-in.Para instalar un plug-in 1 selecciona la opción Help → Install new Softwarese abrirá una ventana <strong>con</strong> una sección Work with don<strong>de</strong> <strong>de</strong>bes introducir laURL <strong>de</strong>l plug-in Subclipse 2 . Al pulsar Enter te aparecerán todos los plug-insdisponibles en esa dirección, aunque algunos <strong>de</strong> ellos son opcionales, <strong>con</strong>vieneinstalarlos todos, así que marca todos ellos y pulsa el botón Next. En la siguienteventana se te mostrarán a modo <strong>de</strong> información todos los paquetes que seinstalarán. Pulsa <strong>de</strong> nuevo Next. En la siguiente ventana se te pi<strong>de</strong> que acepteslos términos <strong>de</strong> la licencia, para ello selecciona la opción I accept the terms andof the license agreements y pulsa el botón Finish, verás como todos los paquetes<strong>de</strong> clases necesarios para el nuevo plug-in se <strong>de</strong>scargan hacia tu máquina. Haciael final <strong>de</strong> la instalación se abrirá una ventana <strong>de</strong> advertencia indicándote que1 Estas instrucciones son válidas para la versión 3.5 (Galileo) y la 3.6 (Helios) <strong>de</strong> Eclipse2 En el momento <strong>de</strong> escribir este libro la última versión <strong>de</strong> este plug-in es la 1.6 y su URLes http://subclipse.tigris.org/update_1.6.x


4.5.INTEGRACIÓN CON ECLIPSE 85parte <strong>de</strong>l software que estás instalando no está firmado, por esta vez pulsa latecla OK para seguir <strong>con</strong> la instalación. Finalmente se te pedirá que para acabar<strong>con</strong> la instalación está recomendado reiniciar Eclipse, pulsa el botón Yes yEclipse se reiniciará y ya podrás usar el plug-in recién instalado.Ahora, dispondrás <strong>de</strong> una nueva Perspectiva que te permitirá visualizar tusrepositorios Subversion. Para abrir esta nueva Perspectiva, selecciona la opción<strong>de</strong> menú <strong>de</strong> Eclipse Window → Show perspective → Other.... Al hacerlo seabrirá una ventana, en ellaselecciona SVN Repository Exploring. Dentro <strong>de</strong> esanueva Perspectiva pue<strong>de</strong>s abrir una nueva vista <strong>con</strong> la opción <strong>de</strong> menú Window→ Show View → SVN Repositories. Finalmente, se abrirá una Vista <strong>con</strong> eltítulo SVN Repositories que inicialmente estará vacía.En esta nueva vista es don<strong>de</strong> pue<strong>de</strong>s añadir <strong>con</strong>exiones a los repositoriosSubversion que quieras. Como ejemplo, vamos a crear una <strong>con</strong>exión al repositoriolocal que tenemos en svn://localhost/home/oscar/Repositorio/trunk(esta dirección no tiene por qué coincidir <strong>con</strong> la que hayas elegido tú), paraello, sobre la vista SVN Repositories pulsa el botón <strong>de</strong>recho <strong>de</strong> tu ratón y enel menú emergente que aparecerá selecciona New → Repository Location, seabrirá una ventana solicitándote la URL <strong>de</strong>l repositorio al que te quieres <strong>con</strong>ectar,introduce svn://localhost/home/oscar/Repositorio, fíjate que nohemos introducido trunk al final <strong>de</strong> la URL, pulsa Finish, verás que la nuevaURL te aparece en la vista SVN Repositories, <strong>de</strong>spliega la URL y verás bajo ellael directorio trunk y bajo él, el fichero Saludo.java. Para hacer el chekout <strong>de</strong>lrepositorio selecciona trunk <strong>con</strong> el botón <strong>de</strong>recho y en el menú <strong>con</strong>textual quete aparecerá selecciona Checkout... se abrirá una nueva ventana <strong>con</strong> la opciónseleccionada por <strong>de</strong>fecto Check out as a project <strong>con</strong>figured using New ProjectWizard, pulsa Finish, y en la nueva ventana <strong>de</strong>spliega la opción <strong>Java</strong> y seleccionala opción <strong>Java</strong> Project, a partir <strong>de</strong> aquí, el Wizard es el que ya <strong>con</strong>ocesy utilizas cada vez que quieres crear un nuevo proyecto, así que simplementeintroduce un nombre para el nuevo proyecto y pulsa Finish.Al final <strong>de</strong> este proceso tendrás una copia local <strong>de</strong>l proyecto en Eclipse sobrela que pue<strong>de</strong>s trabajar tal y como trabajas sobre cualquier otro proyectoEclipse, <strong>con</strong> la enorme ventaja <strong>de</strong> que todo el trabajo <strong>con</strong> el repositorio pue<strong>de</strong>shacerlo <strong>de</strong>s<strong>de</strong> el propio Eclipse. Cada vez que queramos ver las discrepanciasentre nuestra copia local y la existente en el repositorio, es <strong>de</strong>cir la informaciónque en <strong>con</strong>sola obteníamos <strong>con</strong> svn diff, ahora la po<strong>de</strong>mos obtener, <strong>de</strong> maneragráfica, pulsado el botón <strong>de</strong>recho <strong>de</strong>l ratón sobre el proyecto en Eclipse yeligiendo <strong>de</strong>l menú <strong>con</strong>textual Team → Synchronize with repository, pasaremosa la perspectiva Team Synchronization don<strong>de</strong> nos aparecerá toda la información<strong>de</strong> sincronización <strong>con</strong> el repositorio. La interpretación <strong>de</strong> los i<strong>con</strong>os es la que semuestra en la Tabla 4.3.Si existe <strong>con</strong>flicto en alguno <strong>de</strong> nuestros ficheros <strong>de</strong> código po<strong>de</strong>mos abrir unaVista don<strong>de</strong> se nos muestra, al mismo tiempo, el estado <strong>de</strong> nuestra copia localy el estado <strong>de</strong>l fichero en el repositorio, para que podamos resolver los <strong>con</strong>flictoscómodamente. Una vez resueltos los <strong>con</strong>flictos pulsamos el botón <strong>de</strong>recho <strong>de</strong>lratón y elegimos Mark as Merged para indicar que hemos resuelto los <strong>con</strong>flictosy que nuestra copia local está lista para ser subida al repositorio (commit),solo nos restará hacer botón <strong>de</strong>recho sobre el nombre <strong>de</strong>l fichero y seleccionarCommit..., se abrirá una ventana para introducir un comentario para el commity pulsamos OK.


86Flecha azul hacia la izquierdaFlecha gris hacia la <strong>de</strong>rechaDoble flecha rojaCAPÍTULO 4. SUBVERSIONLa versión <strong>de</strong>l repositorio es más actualque la copia local.La versión local es más actual que laexistente en el repositorio.Existe un <strong>con</strong>flicto que Subversion nosabe resolver entre la copia local y laexistente en el repositorio.Tabla 4.3: Significado <strong>de</strong> los i<strong>con</strong>os en la perspectiva Team SynchronizationLecturas recomendadas.La referencia obligada para <strong>con</strong>ocer cualquier aspecto <strong>de</strong> Subversion es[7]. Existe una versión gratuita que se pue<strong>de</strong> <strong>de</strong>scargar <strong>de</strong>s<strong>de</strong> http://subversion.apache.org/.Otra referencia más compacta es el capítulo 4 <strong>de</strong>l la referencia [13], don<strong>de</strong>se <strong>de</strong>tallan los puntos principales para empezar a trabajar <strong>con</strong> Subversion.


Capítulo 5ExcepcionesContenidos5.1. ¿Qué es una excepción? . . . . . . . . . . . . . . . . 875.1.1. Tipos <strong>de</strong> excepciones . . . . . . . . . . . . . . . . . . 885.2. Cómo se gestiona una excepción . . . . . . . . . . 885.3. Creación <strong>de</strong> excepciones propias . . . . . . . . . . 91IntroducciónEs evi<strong>de</strong>nte que lo <strong>de</strong>seable es que no se produzcan errores durante la ejecución<strong>de</strong> un programa. A todos nos provoca rechazo utilizar herramientas quefallan, ya sean herramientas software o cualquier otro tipo <strong>de</strong> herramientas.En el capítulo 6 veremos la técnica <strong>de</strong> Test unitarios para comprobar nuestrosoftware <strong>con</strong> objeto <strong>de</strong> eliminar el mayor número posible <strong>de</strong> errores durante lafase <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> nuestras aplicaciones. No obstante, durante la ejecución <strong>de</strong>nuestras aplicaciones existirán situaciones anómalas susceptibles <strong>de</strong> provocar unmal funcionamiento <strong>de</strong> nuestro software. Piensa por ejemplo en el caso en que unusuario intenta guardar un fichero en un directorio protegido <strong>con</strong>tra escritura.Existen lenguajes <strong>de</strong> programación, como C++, que proporcionan un mecanismoopcional <strong>de</strong> reacción frente a estas situaciones anómalas, pero el programadorno está obligado a utilizarlo, es como se ha dicho, una opción.En <strong>Java</strong> existe un mecanismo <strong>de</strong> reacción ante situaciones anómalas muy parecidoal <strong>de</strong> C++ <strong>con</strong> la gran diferencia <strong>de</strong> que el programador sí que está obligadoa usarlo en aquellas situaciones susceptibles <strong>de</strong> provocar un error, lo que<strong>con</strong>duce a la producción <strong>de</strong> código más robusto frente a este tipo <strong>de</strong> fallos entiempo <strong>de</strong> ejecución.5.1. ¿Qué es una excepción?En <strong>Java</strong>, una excepción es una situación anómala en tiempo <strong>de</strong> ejecución. Piensaen el ejemplo <strong>de</strong> la introducción en el que un usuario intenta guarda un ficheroen un directorio protegido <strong>con</strong>tra escritura. Piensa también en el acceso a una87


88CAPÍTULO 5. EXCEPCIONESposición fuera <strong>de</strong> un array estático. Todo este tipo <strong>de</strong> situaciones se producenen tiempo <strong>de</strong> ejecución, y aunque po<strong>de</strong>mos estar prevenidos <strong>con</strong>tra ellas, nopo<strong>de</strong>mos evitar completamente que vayan a ocurrir.5.1.1. Tipos <strong>de</strong> excepcionesEn <strong>Java</strong> existen tres gran<strong>de</strong>s grupos <strong>de</strong> excepciones <strong>de</strong>pendiendo <strong>de</strong> su naturaleza:1. Excepciones <strong>de</strong> la propia máquina virtual. Estas excepciones causadaspor un mal funcionamiento <strong>de</strong> la propia máquina virtual (Sí, la máquinavirtual <strong>Java</strong> también es una pieza <strong>de</strong> software y como tal está sujeta afallos). Este tipo <strong>de</strong> errores, por su naturaleza, son ajenos al programadory por lo tanto no estamos obligados a gestionarlos. Si este tipo <strong>de</strong> erroresse produce durante la ejecución <strong>de</strong> nuestras aplicaciones pue<strong>de</strong> ocurrirque nuestra aplicación se cierre y veamos un mensaje <strong>de</strong> error <strong>de</strong> la propiamáquina virtual. Pero que<strong>de</strong> el lector tranquilo, es extremadamente difícilen<strong>con</strong>trarse <strong>con</strong> un error <strong>de</strong> esta naturaleza. En la Figura 5.1 la clase Errores la clase padre <strong>de</strong> todo este grupo <strong>de</strong> excepciones.2. El siguiente grupo <strong>de</strong> situaciones excepcionales son aquellas tan comunescómo intentar acce<strong>de</strong>r a una posición inexistente <strong>de</strong> un array estático; ointentar hacer un casting incompatible sobre una variable <strong>de</strong> tipo referencia.El código don<strong>de</strong> se pue<strong>de</strong> dar este tipo <strong>de</strong> situaciones es tan comúnque añadir más código para gestionarlas sobrecargaría terriblemente la escritura<strong>de</strong> nuestros programas, por lo que no es necesario gestionar estetipo <strong>de</strong> excepciones, aunque si queremos siempre lo po<strong>de</strong>mos hacer. En laFigura 5.1 la clase RunTimeException es la clase padre <strong>de</strong> este grupo <strong>de</strong>excepciones.3. El tercer y último tipo <strong>de</strong> excepciones está formado por el resto <strong>de</strong> situacionesque no son las anteriores, como por ejemplo, <strong>de</strong> nuevo, intentar escribiren un directorio protegido <strong>con</strong>tra escritura. Este es el tipo <strong>de</strong> excepcionesque sí estamos obligados a gestionar y para los que <strong>Java</strong> proporciona unpotente mecanismo <strong>de</strong> gestión. En la Figura 5.1 la clase Exception es laclase padre <strong>de</strong> este grupo <strong>de</strong> excepciones.5.2. Cómo se gestiona una excepción<strong>Java</strong> proporciona un mecanismo <strong>de</strong> gestión <strong>de</strong> errores a través <strong>de</strong> los bloquestry...catch...finally, tal y como se muestra en el siguiente Listado:1 t r y {2 F i c h e r o f = a b r e F i c h e r o ( nombre ) ; // Quizás e l f i c h e r o no e x i s t a3 S t r i n g l i n e a = f . l e e L i n e a ( ) ; // Quizás s e produzca un e r r o r durante l al e c t u r a4 } c atch ( FileNotFoundException e ) {5 // Código <strong>de</strong> r e c u p e r a c i ó n d e l e r r o r6 System . out . p r i n t l n ( e . getMessage ( ) ) ; // Muestra una d e s c r i p c i ó n d e le r r o r7 } c atch ( IOException e ) {8 // Código <strong>de</strong> r e c u p e r a c i ó n d e l e r r o r9 } f i n a l l y {


5.2.CÓMO SE GESTIONA UNA EXCEPCIÓN 89Figura 5.1: Parte <strong>de</strong>l árbol <strong>de</strong> jerarquía <strong>de</strong> las excepciones en <strong>Java</strong>.10 // Código común11 }12 // Otras l í n e a s <strong>de</strong> c ó d i g o13 System . out . p r i n t l n (” Aquí s i g u e l a e j e c u c i ó n ”) ;Listado 5.1: Ejemplo bloque try{...} catch{...} finally{...}En el ejemplo anterior, el método <strong>de</strong> la línea 2 está intentando abrirun fichero, y una posible situación anómala es que el fichero no exista(FileNotFoundException) lo que pue<strong>de</strong> provocar un error. En la línea 3 seestá intentando leer una línea <strong>de</strong>s<strong>de</strong> el fichero que sí está abierto, lo que tambiénpue<strong>de</strong> provocar algún tipo <strong>de</strong> error <strong>de</strong> entrada/salida.Antes <strong>de</strong> pasar a ver la técnica para gestionar estas situaciones anómalas,fíjate que cuando se produce un error, lo que recibe el bloque catch{...} es unareferencia <strong>de</strong>l tipo <strong>de</strong>l error correspondiente, que, entre otras cosas, lleva una<strong>de</strong>scripción sobre el error que se produjo. Recuerda, todo en <strong>Java</strong> es un objetoy en particular los errores en tiempo <strong>de</strong> ejecución son instancias <strong>de</strong> clases querepresentan errores.La técnica para gestionar esta situación es:1. Encerrar en un bloque try{...} el o los métodos susceptibles <strong>de</strong> provocarun error.2. Atrapar en bloques catch{...} separados, cada uno <strong>de</strong> los posibles erroresque se pue<strong>de</strong>n producir en el bloque try{...}.3. Opcionalmente po<strong>de</strong>mos <strong>de</strong>finir un bloque finally{...} que se ejecutará<strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> que se genere alguna <strong>de</strong> las excepciones gestionadaso no. Es <strong>de</strong>cir si existe el bloque finally{...} su código siemprese ejecutara se produzca o no una excepción.En el listado 5.1, si en la línea 2 se produce un error porque no se en<strong>con</strong>traseel fichero que se <strong>de</strong>sea abrir, la siguiente línea <strong>de</strong> código 3 no se ejecutaría, la


90CAPÍTULO 5. EXCEPCIONESejecución pasaría directamente al bloque catch(FileNotFoundException e),y tras su ejecución al bloque finally <strong>de</strong> la línea 10, ya que tenemos <strong>de</strong>finidouno. Después <strong>de</strong> la gestión <strong>de</strong>l error, la ejecución seguirá en la línea 13. Sitanto el código en el bloque finally{...} como el código posterior a la gestión<strong>de</strong> la excepción se ejecuta (código en la línea 13), ¿Qué sentido tiene incluirel bloque finally{...}?. Antes <strong>de</strong> <strong>con</strong>testar a esta pregunta veamos cómopo<strong>de</strong>mos obviar la gestión <strong>de</strong> una excepción en el momento en que esta seproduce y <strong>de</strong>legar su gestión en el método que invocó al actual en la pila <strong>de</strong>llamadas.Existen casos es los que nos interesa <strong>de</strong>legar la gestión <strong>de</strong> la excepción, laexcepción ocurre en la <strong>de</strong>finición <strong>de</strong> un <strong>de</strong>terminado método pero no queremosañadir el código <strong>de</strong> gestión <strong>de</strong> la excepción en la <strong>de</strong>finición <strong>de</strong> ese método, ¿Cómoindicamos que un método no va a gestionar una <strong>de</strong>terminada excepción?, <strong>con</strong>el uso <strong>de</strong> la palabra reservada throws como en el siguiente listado:1 public void metodo ( ) throws FileNotFoundException {2 // Aquí l a d e f i n i c i ó n d e l método3 }En este caso estamos <strong>de</strong>legando la gestión <strong>de</strong> la excepción al método quellamó a este otro, que es quien <strong>de</strong>lega la gestión <strong>de</strong> la excepción.La palabra reservada throws también se utiliza para lanzar excepciones propiastal y como vamos a ver en la Sección 5.3.Ahora ya po<strong>de</strong>mos <strong>con</strong>testar a la pregunta sobre la utilidad <strong>de</strong>l bloquefinally{...} observando el ejemplo <strong>de</strong>l Listado 5.2. Si se produjese la excepciónIOException en la línea 4 durante el proceso <strong>de</strong> lectura, se abandonaríala ejecución <strong>de</strong>l método <strong>de</strong>legando la gestión <strong>de</strong> estas excepciones al métodoque invocó a este (ejecuta()). La línea 6 nunca se ejecutaría y el fichero quedaríaabierto, su referencia perdida al salir <strong>de</strong>l método y no podríamos cerrar elfichero.1 // Este método d e l e g a l a g e s t i ó n <strong>de</strong> l a s e x c e p c i ó n FileNotFoundExceptiony IOException2 private void e j e c u t a ( ) throws FileNotFoundException , IOException {3 F i l e R e a d e r f r = new F i l e R e a d e r ( " f i c h e r o . txt " ) ;4 int c a r a c t e r = f r . read ( ) ;5 System . out . p r i n t l n ( " c a r a c t e r : " + c a r a c t e r ) ;6 f r . c l o s e ( ) ;7 }Listado 5.2: Tanto la excepción FileNotFoundException como IOException se<strong>de</strong>leganEn el ejemplo <strong>de</strong>l Listado 5.3, tanto si se produce una excepción, como sino se produce, el bloque finally{...} siempre se ejecutará y el fichero secerrará en cualquier caso, se produzca o no una excepción.1 // Este método d e l e g a l a g e s t i ó n <strong>de</strong> l a s e x c e p c i ó n FileNotFoundExceptiony IOException2 private void e j e c u t a ( ) throws FileNotFoundException , IOException {3 F i l e R e a d e r f r = null ;4 int c a r a c t e r = 0 ;5 try {6 f r = new F i l e R e a d e r ( " f i c h e r o . txt " ) ;7 c a r a c t e r = f r . read ( ) ;8 } f i n a l l y {


5.3.CREACIÓN DE EXCEPCIONES PROPIAS 919 System . out . p r i n t l n ( " c a r a c t e r : " + c a r a c t e r ) ;10 i f ( f r != null ) f r . c l o s e ( ) ;11 }12 }Listado 5.3: En este caso el fichero siempre se gracias al uso <strong>de</strong>l bloquefinally{...}Otro <strong>de</strong>talle importante es el or<strong>de</strong>n <strong>de</strong> los errores que se atrapan en losbloques catch{...}. Compara el or<strong>de</strong>n <strong>de</strong>l ejemplo <strong>de</strong>l Listado 5.1. Fíjateque el or<strong>de</strong>n en los bloques try{...} catch{...} finally{...} va <strong>de</strong>s<strong>de</strong> elmás específico (FileNotFoundException) al más general (IOException). Piensaqué ocurriría si se intercambiase el or<strong>de</strong>n, la clase padre aparecería en elprimer bloque catch{...} <strong>de</strong> modo que tanto si se produjera una excepción <strong>de</strong>tipo IOException como si fuera <strong>de</strong> tipo FileNotFoundException ambas provocaríala ejecución <strong>de</strong>l bloque catch{IOException} ya que este bloque atrapareferencias <strong>de</strong> la clase padre y cualquier clase hija. Recuerda, las excepcionestambién son instancias <strong>de</strong> objetos, y por lo tanto sobre ellas es válido todo loque aprendimos sobre herencia en el capítulo 3.5.3. Creación <strong>de</strong> excepciones propiasEn el <strong>de</strong>sarrollo <strong>de</strong> nuestras propias aplicaciones resulta interesante po<strong>de</strong>r lanzarexcepciones propias ante situaciones inesperadas. <strong>Java</strong> proporciona un mecanismopara <strong>de</strong>finir nuevas excepciones y lanzarlas en los casos en los que seproduzcan situaciones anómalas.El mecanismo para <strong>de</strong>finir y lazar excepciones propias es el siguiente:1. Definir la nueva clase que representa a nuestra excepción.2. Lanzar la excepción en las situaciones anómalas.3. Gestionar la excepción como cualquier otra.Al utilizar este mecanismo estamos creando excepciones que son tratadas<strong>de</strong>l mismo modo que las excepciones pre<strong>de</strong>finidas en <strong>Java</strong>. Veamos cada uno <strong>de</strong>estos pasos <strong>con</strong> un ejemplo. Supongamos que queremos generar una excepciónsi se solicita una posición no válida <strong>de</strong>ntro <strong>de</strong> nuestra aplicación <strong>de</strong> la Agenda.Lo primero que <strong>de</strong>bemos hacer es <strong>de</strong>finir la clase que representa a nuestra nuevaexcepción. El <strong>de</strong>talle que <strong>de</strong>bemos tener en cuenta es que nuestra excepción<strong>de</strong>be ser hija <strong>de</strong> la clase Exception, tal y como se muestra en el Listado 5.4:1 public c l a s s TemperaturaNoValidaException extends Exception {2 public TemperaturaNoValidaException ( ) {3 super ( " La t e m p e r a t u r a no p u e d e se m e n o r que -273 o C " ) ;4 }5 }Listado 5.4: Definición <strong>de</strong> una excepción propiaFíjate que el <strong>con</strong>structor por <strong>de</strong>fecto llama a super <strong>de</strong>l padre <strong>con</strong> un Stringque es una <strong>de</strong>scripción <strong>de</strong>l error que ha ocurrido. Este String se podrá recuperaren el bloque catch{...} correspondiente como veremos más a<strong>de</strong>lante.El siguiente paso es lanzar la excepción en caso <strong>de</strong> producirse una situaciónanómala, como en el Listado 5.5 :


92CAPÍTULO 5. EXCEPCIONES1 public c l a s s ConversorTemperaturas {2 private f i n a l double CERO ABSOLUTO = −273.15;34 public ConversorTemperaturas ( ) {5 super ( ) ;6 }78 public double c e l s i u s A F h a r e n h e i t ( double c e l s i u s ) throwsTemperaturaNoValidaException {9 i f ( c e l s i u s < CERO ABSOLUTO) throw new TemperaturaNoValidaException ( ) ;10 return 9 . 0 / 5 . 0 ∗ c e l s i u s + 3 2 . 0 ;11 }1213 public double celsiusAReamur ( double c e l s i u s ) throwsTemperaturaNoValidaException {14 i f ( c e l s i u s < CERO ABSOLUTO) throw new TemperaturaNoValidaException ( ) ;15 return 4 . 0 / 5 . 0 ∗ c e l s i u s ;16 }17 }Listado 5.5: Definición <strong>de</strong> una excepción propiaCabe resaltar un par <strong>de</strong> cosas <strong>de</strong>l Listado 5.5, la primera es que el método<strong>de</strong>s<strong>de</strong> el que se lanza la excepción indica que va a hacerlo <strong>con</strong> el uso <strong>de</strong> la palabrareservada throws seguida <strong>de</strong>l nombre <strong>de</strong> la excepción. La segunda es que paralanzar la excepción utilizamos la palabra reservada throw y creamos una nuevainstancia <strong>de</strong> la excepción <strong>con</strong> new, recuerda que una excepción al fin y al cabono es más que una instancia <strong>de</strong> una clase.Nuestra excepción se gestiona como cualquier otra ya <strong>de</strong>finida en el paqueteestándar <strong>de</strong> <strong>Java</strong>, mediante el bloque try{...} catch{...} finally{...}, taly como se muestra en el Listado 5.6. En la línea 7 <strong>de</strong> este listado se muestracómo recuperar el texto <strong>de</strong>scriptivo <strong>de</strong> la excepción que proporcionamos en la<strong>de</strong>finición <strong>de</strong> la clase TemperaturaNoValidaException. Un método útil pararecuperar toda la traza <strong>de</strong> ejecución <strong>de</strong> nuestro programa hasta el momentoen el que se produzco la excepción es printStackTrace() <strong>de</strong>finido en la claseThrowable que es la clase <strong>de</strong> la que heredan todas las excepciones en <strong>Java</strong>.1 for ( int c e l s i u s = 0 ; c e l s i u s < 1 0 1 ; c e l s i u s += 5) {2 try {3 f h a r e n h e i t = 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 ( c e l s i u s ) ;4 reamur = c o n v e r s o r . celsiusAReamur ( c e l s i u s ) ;5 System . out . p r i n t l n ( c e l s i u s + " \ t " + f h a r e n h e i t + " \ t " + reamur ) ;6 } catch ( TemperaturaNoValidaException e ) {7 System . out . p r i n t l n ( e . getMessage ( ) ) ;8 }9 }Listado 5.6: Definición <strong>de</strong> una excepción propiaLecturas recomendadasEl capítulo 8 <strong>de</strong> la referencia [2] presenta todos los <strong>de</strong>talles <strong>de</strong> la gestión<strong>de</strong> excepciones y cómo crear excepciones propias.


Capítulo 6Pruebas unitarias <strong>con</strong> JUnitContenidos6.1. ¿Qué son las pruebas unitarias? . . . . . . . . . . . 946.1.1. Principios FIRST para el diseño <strong>de</strong> pruebas unitarias 946.2. Pruebas unitarias <strong>con</strong> JUnit . . . . . . . . . . . . . 956.2.1. Creación <strong>de</strong> clases <strong>de</strong> prueba . . . . . . . . . . . . . 956.2.2. La anotación @Test . . . . . . . . . . . . . . . . . . 966.2.3. Las anotaciones @Before y @After . . . . . . . . . . 986.2.4. Las anotaciones @BeforeClass y @AfterClass . . . . 996.2.5. Pruebas <strong>con</strong> batería <strong>de</strong> datos <strong>de</strong> entrada . . . . . . . 1006.2.6. Ejecutar varias clases <strong>de</strong> prueba. Test Suites . . . . 1016.3. Cobertura <strong>de</strong> las pruebas . . . . . . . . . . . . . . . 1026.3.1. EclEmma y su plug-in para Eclipse . . . . . . . . . . 103IntroducciónLlegados a este punto ya somos capaces <strong>de</strong> escribir aplicaciones <strong>Java</strong> utilizandolos principios <strong>de</strong> la POO. Sabemos cómo <strong>de</strong>finir clases, como utilizar la herenciao la composición para ampliar el comportamiento <strong>de</strong> nuestras clases. Inclusosomos capaces <strong>de</strong> <strong>con</strong>trolar las situaciones anómalas que pue<strong>de</strong>n darse durantela ejecución <strong>de</strong> nuestras aplicaciones a través <strong>de</strong> la gestión <strong>de</strong> excepciones.El siguiente paso que usualmente se suele dar es comprobar la vali<strong>de</strong>z <strong>de</strong>lcódigo realizando pruebas. A veces, estas pruebas son caseras, probamos unoscuantos valores <strong>de</strong> entrada en nuestra aplicación, valores <strong>de</strong> los que <strong>con</strong>ocemoscual es la salida esperada, y <strong>con</strong>fiamos que en el resto <strong>de</strong> casos nuestro códigoesté libre <strong>de</strong> errores. Y en este momento es cuando la <strong>con</strong>fianza se <strong>con</strong>vierte enengaño, nuestro código está plagado <strong>de</strong> errores que no hemos sido capaces <strong>de</strong><strong>de</strong>tectar y que tar<strong>de</strong> o temprano saldrán a la luz sumiéndonos en la oscuridad,paradojas <strong>de</strong> la vida.La primera i<strong>de</strong>a que <strong>de</strong>bemos fijar es que hacer pruebas <strong>de</strong> código no <strong>de</strong>beser una opción, es un requerimiento, por <strong>de</strong>fecto, en todo <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong>informáticos.93


94CAPÍTULO 6. PRUEBAS UNITARIAS CON JUNITLa segunda i<strong>de</strong>a que <strong>de</strong>bemos fijar es que las pruebas <strong>de</strong> código no <strong>de</strong>benser manuales, si no automatizadas. Si son manuales, por aburrimiento o falta <strong>de</strong>tiempo acabaremos por no hacerlas. Las pruebas automatizadas forman parte<strong>de</strong>l código <strong>de</strong>l proyecto, son tan importantes como el código que se está probandoy por lo tanto <strong>de</strong>bemos <strong>de</strong>dicarles el mismo empeño que al <strong>de</strong>sarrollo <strong>de</strong>l código<strong>de</strong> nuestra aplicación.En este capítulo no se va a mostrar cómo diseñar buenas pruebas <strong>de</strong> código,y lo que es más importante, no se va a mostrar cómo escribir código que se puedaprobar fácilmente, en la sección <strong>de</strong> referencias <strong>de</strong> este capítulo se dan algunostítulos que son <strong>de</strong> lectura obligada a todo <strong>de</strong>sarrollador que quiera poner enpráctica la prueba <strong>de</strong> código en sus <strong>proyectos</strong>.En este capítulo se va a mostrar cómo utilizar una herramienta para comprobarcódigo. Esta excelente herramienta es JUnit.6.1. ¿Qué son las pruebas unitarias?Las pruebas unitarias se realizan sobre una clase, para probar su comportamiento<strong>de</strong> modo aislado, in<strong>de</strong>pendientemente <strong>de</strong>l resto <strong>de</strong> clases <strong>de</strong> la aplicación. Esterequisito a veces no se cumple, piensa en el código <strong>de</strong> una clase que acce<strong>de</strong> auna base <strong>de</strong> datos, y que la prueba <strong>de</strong> la clase se base en el resultado que serecupera <strong>de</strong> la base <strong>de</strong> datos, resulta imposible comprobar esta clase <strong>de</strong> modoaislado, aunque existen técnicas (como los Mock Objects) que minimizan estas<strong>de</strong>pen<strong>de</strong>ncias.6.1.1. Principios FIRST para el diseño <strong>de</strong> pruebas unitariasCuando se diseñan pruebas unitarias es importante seguir los principios FIRST.Cada una <strong>de</strong> las letras <strong>de</strong> esta palabra inglesa está relacionada <strong>con</strong> un <strong>con</strong>cepto.Veámoslos:Fast : La ejecución <strong>de</strong>l código <strong>de</strong> pruebas <strong>de</strong>be ser rápida. Si las pruebas <strong>con</strong>sumen<strong>de</strong>masiado tiempo acabaremos por no hacerlas.In<strong>de</strong>pen<strong>de</strong>nt : Una prueba no pue<strong>de</strong> <strong>de</strong>pen<strong>de</strong>r <strong>de</strong> otras. Cada prueba <strong>de</strong>be serunitaria, <strong>de</strong>be po<strong>de</strong>r realizarse <strong>de</strong> modo aislado.Repetable : Las pruebas se <strong>de</strong>ben po<strong>de</strong>r repetir en cualquier momento y lacantidad <strong>de</strong> veces que sea necesario. El resultado <strong>de</strong> una prueba <strong>de</strong>be sersiempre el mismo.Self-validating : Sólo hay dos posibles resultados <strong>de</strong> una prueba: ✭✭La pruebapasó <strong>con</strong> éxito✮✮ o ✭✭La prueba falló✮✮.Timely : Las pruebas han <strong>de</strong> escribirse en el momento <strong>de</strong> escribir el código, yno al final <strong>de</strong> toda la fase <strong>de</strong> <strong>de</strong>sarrollo 11 La metodología <strong>de</strong> <strong>de</strong>sarrollo Test Driven Development (TDD) lleva este principio alinicio <strong>de</strong>l proceso <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> tal modo que las pruebas <strong>de</strong> código se escriben antes queel propio código que se intenta probar.


6.2. PRUEBAS UNITARIAS CON JUNIT 956.2. Pruebas unitarias <strong>con</strong> JUnitJUnit es una herramienta para realizar pruebas unitarias automatizadas. JUnitestá integrada en Eclipse, no es necesario <strong>de</strong>scargarse ningún paquete ni instalarun nuevo plug-in para utilizarla. Eclipse facilita la creación <strong>de</strong> pruebas unitarias.Para mostrar <strong>con</strong> un ejemplo cómo se escriben pruebas unitarias <strong>de</strong>código <strong>con</strong> JUnit vamos a utilizar las clases ConversorTemperaturas yTemperaturaNoValidaException que vimos en el capítulo anterior.6.2.1. Creación <strong>de</strong> clases <strong>de</strong> pruebaPara crear una clase <strong>de</strong> prueba en Eclipse seleccionamos File → New → Other...,en la ventana <strong>de</strong> diálogo que se abrirá seleccionamos <strong>Java</strong> → JUnit →JUnit Test Case, se abrirá una nueva ventana <strong>de</strong> diálogo, en la parte superiorseleccionamos New JUnit 4 test, introducimos el nombre para la clase <strong>de</strong>prueba y su paquete, y por comodidad, en la parte inferior <strong>de</strong> esta ventanapulsamos Browse y seleccionamos la clase que <strong>de</strong>seamos probar, que en nuestrocaso es ConversorTemperaturas, y pulsamos Next, en la nueva ventana veremosque se nos ofrece la posibilidad <strong>de</strong> seleccionar los métodos que queremosprobar, en nuestro caso vamos a seleccionar celsiusAFharenheit(double) ycelsiusAReamur(double) y pulsamos Finish. La clase <strong>de</strong> prueba que se creará automáticamenteserá la que se muestra en el Listado 6.1.Cabe <strong>de</strong>stacar varios puntos <strong>de</strong> este Listado:1. Fíjate en el uso <strong>de</strong>l import static <strong>de</strong> la línea 3, es útil para no tenerque incluir el nombre <strong>de</strong> la clase Assert cuando utilizamos alguno <strong>de</strong> susmétodos estáticos, como fail. Un import static me permite utilizartodos los métodos static <strong>de</strong> una clase sin necesidad <strong>de</strong> anteponer almétodo el nombre <strong>de</strong> la clase 2 .2. Observa que se han creado dos métodos <strong>de</strong> prueba, una para cada métodoque seleccionamos sobre la clase a probar, y que la signatura <strong>de</strong> ambos espublic void nombreDelMetodo(). Los métodos <strong>de</strong> prueba <strong>de</strong>ben ser públicos noretornar ningún valor y su lista <strong>de</strong> argumentos <strong>de</strong>be estar vacía.3. Fíjate que sobre cada uno <strong>de</strong> los métodos <strong>de</strong> prueba aparece la anotación@Test que indica al compilador que es un método <strong>de</strong> prueba.4. Por <strong>de</strong>fecto, cada uno <strong>de</strong> los métodos <strong>de</strong> prueba tiene una llamada afail("Mesaje <strong>con</strong> <strong>de</strong>scripción.".1 package t e s t ;23 import s t a t i c org . j u n i t . A s s e r t . ∗ ;45 import org . j u n i t . Test ;67 public c l a s s TestConversorTemperaturas {2 Los static import se introdujeron en la versión 5 <strong>de</strong> <strong>Java</strong>, y aunque son cómodos <strong>de</strong>utilizar, el uso <strong>de</strong> JUnit es un caso, pue<strong>de</strong>n provocar <strong>con</strong>fusión en el programador, ya queal no aparecer el nombre <strong>de</strong> la clase tendremos la duda <strong>de</strong> si el método pertenece a la claseactual o a un clase <strong>de</strong> la que se ha hecho un static import. En general, el uso <strong>de</strong> los staticimport está <strong>de</strong>sa<strong>con</strong>sejado.


96CAPÍTULO 6. PRUEBAS UNITARIAS CON JUNIT89 @Test10 public f i n a l void t e s t C e l s i u s A F h e r e n h e i t ( ) {11 f a i l ( " Not yet i m p l e m e n t e d " ) ;12 }1314 @Test15 public f i n a l void testCelsiusAReamur ( ) {16 f a i l ( " Not yet i m p l e m e n t e d " ) ;17 }1819 }Listado 6.1: Código generado automáticamente por Eclipse para una clase <strong>de</strong>prueba.6.2.2. La anotación @TestComo ya se ha dicho, la anotación @Test sirve para indicar que un <strong>de</strong>terminadométodo es un método <strong>de</strong> prueba. Vamos a escribir el primer código <strong>de</strong> pruebatal y como se muestra en el Listado 6.2. Fíjate que también hemos añadidothrows TemperaturaNoValidaException para indicar que no queremos gestionar estaposible excepción en el código <strong>de</strong>l método <strong>de</strong> prueba.1 @Test2 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throwsTemperaturaNoValidaException {3 ConversorTemperaturas c o n v e r s o r = new ConversorTemperaturas ( ) ;4 a s s e r t E q u a l s ( 3 2 , 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 ( 0 ) , 0) ;5 }Listado 6.2: Un método <strong>de</strong> prueba.Lo primero que hacemos en el método <strong>de</strong> prueba es crear una instancia<strong>de</strong> la clase ConversorTemperaturas, y <strong>de</strong>spués utilizar el método assertEquals(valorEsperado, valorObtenido, error). Este método comprueba que la diferencia entre elvalorEsperado y el valorObtenido es menor que error. Si es así, se ha pasadola prueba, <strong>de</strong> lo <strong>con</strong>trario la prueba falla. En nuestro caso, se está aseverandoque la diferencia entre el valor que <strong>de</strong>vuelve el método celsiusAFharenheit(0)y el valor 32 es cero. Escribamos el código para la segunda <strong>de</strong> las pruebas tal ycomo se muestra en el Listado 6.3.1 @Test2 public void testCelsiusAReamur ( ) throws TemperaturaNoValidaException {3 ConversorTemperaturas c o n v e r s o r = new ConversorTemperaturas ( ) ;4 a s s e r t E q u a l s ( 0 , c o n v e r s o r . celsiusAReamur ( 0 ) , 0) ;5 }Listado 6.3: Un segundo método <strong>de</strong> prueba.De modo análogo al primer método <strong>de</strong> prueba, en este caso estamos aseverandoque la diferencia entre el valor que <strong>de</strong>vuelve la llamada al métodocelsiusAReamur(0) y el valor 0 es cero.Para ejecutar las pruebas <strong>de</strong>s<strong>de</strong> Eclipse pulsa el botón <strong>de</strong>recho <strong>de</strong>l ratónsobre la clase <strong>de</strong> prueba y en el menú emergente selecciona la opción Run As →JUnit Test, verás que se abre una nueva vista <strong>con</strong> el resultado <strong>de</strong> la ejecución<strong>de</strong> las pruebas, que en nuestro caso es Runs: 2/2 Errors: 0 Failures: 0 que nos


6.2. PRUEBAS UNITARIAS CON JUNIT 97indica que se han realizado 2 pruebas, ninguna <strong>de</strong> ellas a provocado un error yninguna <strong>de</strong> ellas a provocado un fallo.¿Cual es la diferencia entre un fallo y un error en el <strong>con</strong>texto <strong>de</strong> las pruebasunitarias <strong>con</strong> JUnit?. Un fallo es una aseveración que no se cumple, unerror es una excepción durante la ejecución <strong>de</strong>l código. Generemos un fallo <strong>de</strong>modo artificial para ver el resultado, cambiemos la línea assertEquals(32, <strong>con</strong>versor.celsiusAFharenheit(0), 0); por esta otra assertEquals(0, <strong>con</strong>versor.celsiusAFharenheit(0), 0);,y ejecutemos <strong>de</strong> nuevo la prueba, en este caso obtendremos un fallo que nos informaráque el valor esperado <strong>de</strong> la prueba era 0 mientras que el valor obtenidoes 32.0.Añadamos otro método <strong>de</strong> prueba que genere un error, para ver la diferencia<strong>con</strong> un fallo, tal y como se muestra en el Listado 6.4.1 public void testTemperaturaNoValidaFharenheit ( ) throwsTemperaturaNoValidaException {2 ConversorTemperaturas c o n v e r s o r = new ConversorTemperaturas ( ) ;3 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;4 }Listado 6.4: Un método <strong>de</strong> prueba que genera un error.Al ejecutar <strong>de</strong> nuevo las pruebas esta vez obtendremos la excepción La temperaturano pue<strong>de</strong> ser menor que -273 o C. ¿Y si lo que queremos es precisamentecomprobar que se lanza la excepción?, es <strong>de</strong>cir, ¿Y si nuestra pruebapasa precisamente si se genera la excepción? Para ello basta <strong>con</strong> añadir el atributoexpected=TemperaturaNoValidaException.class a la anotación @Testquedando <strong>de</strong> este modo @Test(expected=TemperaturaNoValidaException.class). Si ejecutamos<strong>de</strong> nuevo las pruebas veremos que todas ellas pasan.Otra técnica que no utiliza el atributo expected <strong>de</strong> la anotación @Test paracomprobar que se produce una excepción es la mostrada en el Listado 6.5.Esta vez el método está etiquetado únicamente <strong>con</strong> @Test, y <strong>de</strong>trás <strong>de</strong> la línea<strong>de</strong> código que esperamos que produzca la excepción escribimos fail("Paratemperaturas por encima <strong>de</strong> -273 la prueba <strong>de</strong>be pasar."). Si la excepciónse produce al ejecutarse la línea 5, la ejecución <strong>de</strong> la prueba <strong>con</strong>tinuará enel bloque catch (TemperaturaNoValidaException e) y la prueba pasará, quees lo que esperamos.Si no se produce la excepción en la línea 5, se ejecutará la sentencia fail(...)y la prueba no pasará, cosa que será indicativa <strong>de</strong> que algo ha ido mal ya lo queintentamos probar es que la excepción sí que se produce.1 @Test2 public void testTemperaturaNoValidadFahrenheit ( ) {3 ConversorTemperaturas c o n v e r s o r = new ConversorTemperaturas ( ) ;4 try {5 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;6 f a i l ( " Para t e m p e r a t u r a s por e n c i m a <strong>de</strong> -273 la p r u e b a <strong>de</strong>be p a s a r . " ) ;7 } catch ( TemperaturaNoValidaException e ) {8 }9 }Listado 6.5: Un método <strong>de</strong> prueba que genera un error.Este segundo método <strong>de</strong> prueba <strong>de</strong> excepciones es el recomendado, ya quees más fácil interpretar qué es lo que se está intentando probar.


98CAPÍTULO 6. PRUEBAS UNITARIAS CON JUNIT6.2.3. Las anotaciones @Before y @AfterSi revisas el código <strong>de</strong> los tres métodos <strong>de</strong> prueba anteriores verás que lo primeroque hacemos es crear una instancia <strong>de</strong> la clase ConversorTemperaturas. JUnitnos proporciona un mecanismo para extraer el código que se repite en todoslos métodos, y que <strong>de</strong>be ejecutarse antes <strong>de</strong> cualquiera <strong>de</strong> ellos, a través <strong>de</strong> lasanotaciones @Before y @After. Si anotamos un método <strong>con</strong> @Before su códigoserá ejecutado antes <strong>de</strong> cada uno <strong>de</strong> los métodos <strong>de</strong> prueba, si tenemos tresmétodos <strong>de</strong> prueba será ejecutado antes <strong>de</strong> cada uno <strong>de</strong> los tres métodos. Por sulado, si anotamos un método <strong>con</strong> @After será ejecutado <strong>de</strong>spués <strong>de</strong> la ejecución<strong>de</strong> cada uno <strong>de</strong> los métodos <strong>de</strong> prueba. Por lo tanto, po<strong>de</strong>mos usar la anotación@Before para iniciar todas las infraestructuras necesarias a la ejecución <strong>de</strong> laspruebas y la anotación @After para limpiar estas infraestructuras.En nuestro caso, la clase <strong>de</strong> prueba quedaría tal y como se muestra en elListado 6.6.1 import s t a t i c org . j u n i t . A s s e r t . ∗ ;23 import org . j u n i t . A f t e r ;4 import org . j u n i t . B e f o r e ;5 import org . j u n i t . Test ;67 import c o n v e r s o r . ConversorTemperaturas ;8 import c o n v e r s o r . TemperaturaNoValidaException ;910 public c l a s s TestConversorTemperaturas2 {11 private ConversorTemperaturas c o n v e r s o r ;1213 @Before14 public void creaConversorTemperaturas ( ) {15 c o n v e r s o r = new ConversorTemperaturas ( ) ;16 }1718 @After19 public void <strong>de</strong>struyeCnversorTemperarturas ( ) {20 c o n v e r s o r = null ;21 }2223 @Test24 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throwsTemperaturaNoValidaException {25 a s s e r t E q u a l s ( 3 2 , 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 ( 0 ) , 0) ;26 }2728 @Test29 public void testCelsiusAReamur ( ) throws TemperaturaNoValidaException {30 a s s e r t E q u a l s ( 0 , c o n v e r s o r . celsiusAReamur ( 0 ) , 0) ;31 }3233 @Test ( expected=TemperaturaNoValidaException . c l a s s )34 public void testTemperaturaNoValidaFharenheit ( ) throwsTemperaturaNoValidaException {35 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;36 }37 }Listado 6.6: Uso <strong>de</strong> las anotaciones @Before y @After.Las anotaciones @Before y @After las pue<strong>de</strong>s utilizar tantas veces comote sea necesario, pue<strong>de</strong> haber más <strong>de</strong> un método anotado <strong>con</strong> alguna <strong>de</strong> estasanotaciones. Todos los métodos que estén anotados <strong>con</strong> @Before se ejecutaránantes <strong>de</strong> cada uno <strong>de</strong> los métodos <strong>de</strong> prueba y todos los métodos que esténanotados <strong>con</strong> @After se ejecutarán <strong>de</strong>spués <strong>de</strong> cada uno <strong>de</strong> los métodos <strong>de</strong>


6.2. PRUEBAS UNITARIAS CON JUNIT 99prueba.6.2.4. Las anotaciones @BeforeClass y @AfterClassPo<strong>de</strong>mos mejorar un poco más nuestra clase <strong>de</strong> prueba <strong>con</strong> el uso <strong>de</strong> dos nuevasetiquetas @BeforeClass y @AfterClass. Fíjate que la clase que estamos probandoConversorTemperaturas no tiene estado, y por lo tanto el resultado <strong>de</strong>las llamadas a sus métodos es in<strong>de</strong>pendiente <strong>de</strong>l or<strong>de</strong>n en el que se hagan, por loque no es necesario crear una instancia nueva antes <strong>de</strong> cada una <strong>de</strong> las pruebas,si no que la misma instancia nos sirve para las tres pruebas.Si anotamos un método <strong>de</strong> una clase <strong>de</strong> prueba <strong>con</strong> @BeforeClass ese métodose ejecutará una única vez antes <strong>de</strong> la ejecución <strong>de</strong> cualquier método <strong>de</strong>prueba. Por otro lado, si anotamos un método <strong>de</strong> una clase <strong>de</strong> prueba <strong>con</strong>@AfterClass el método será ejecutado una única vez <strong>de</strong>spués <strong>de</strong> haberse ejecutadotodos los métodos <strong>de</strong> prueba, tal y como se muestra en el Listado 6.7.1 import s t a t i c org . j u n i t . A s s e r t . a s s e r t E q u a l s ;23 import org . j u n i t . A f t e r C l a s s ;4 import org . j u n i t . B e f o r e C l a s s ;5 import org . j u n i t . Test ;67 import c o n v e r s o r . ConversorTemperaturas ;8 import c o n v e r s o r . TemperaturaNoValidaException ;910 public c l a s s TestConversorTemperaturas3 {11 private s t a t i c ConversorTemperaturas c o n v e r s o r ;1213 @BeforeClass14 public s t a t i c void creaConversorTemperaturas ( ) {15 c o n v e r s o r = new ConversorTemperaturas ( ) ;16 }1718 @AfterClass19 public s t a t i c void <strong>de</strong>struyeCnversorTemperarturas ( ) {20 c o n v e r s o r = null ;21 }2223 @Test24 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throwsTemperaturaNoValidaException {25 a s s e r t E q u a l s ( 3 2 , 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 ( 0 ) , 0) ;26 }2728 @Test29 public void testCelsiusAReamur ( ) throws TemperaturaNoValidaException {30 a s s e r t E q u a l s ( 0 , c o n v e r s o r . celsiusAReamur ( 0 ) , 0) ;31 }3233 @Test ( expected=TemperaturaNoValidaException . c l a s s )34 public void testTemperaturaNoValidaFharenheit ( ) throwsTemperaturaNoValidaException {35 c o n v e r s o r . c e l s i u s A F h a r e n h e i t ( −400) ;36 }37 }Listado 6.7: Uso <strong>de</strong> las anotaciones @BeforeClass y @AfterClass.Fíjate en el importante <strong>de</strong>talle que aparece en el Listado 6.7, los métodosanotados <strong>con</strong> @BeforeClass y @AfterClass <strong>de</strong>ben ser ambos static y por lotanto, los atributos a los que acce<strong>de</strong>n también <strong>de</strong>ben ser static, tal y comovimos en 2.6.


100CAPÍTULO 6. PRUEBAS UNITARIAS CON JUNIT6.2.5. Pruebas <strong>con</strong> batería <strong>de</strong> datos <strong>de</strong> entradaCada uno <strong>de</strong> los métodos <strong>de</strong> prueba <strong>de</strong> los ejemplos anteriores utiliza un trío <strong>de</strong>datos, valor esperado, valor real y error para comprobar cada uno <strong>de</strong> los casos<strong>de</strong> prueba. Si queremos escribir una nueva prueba para otro trío <strong>de</strong> valores estedioso crear un método sólo para él. JUnit proporciona un mecanismo paraprobar baterías <strong>de</strong> valores en vez <strong>de</strong> únicamente tríos aislados.Lo primero que <strong>de</strong>bemos hacer es anotar la clase <strong>de</strong> prueba <strong>con</strong>@RunWith(Parameterized.class) indicando que va a ser utilizada para realizarbaterías <strong>de</strong> pruebas. La clase <strong>de</strong> prueba ha <strong>de</strong> <strong>de</strong>clarar un atributo porcada uno <strong>de</strong> los parámetros <strong>de</strong> la prueba, y un <strong>con</strong>structor <strong>con</strong> tantos argumentoscomo parámetros en cada prueba. Finalmente necesitamos <strong>de</strong>finir un métodoque <strong>de</strong>vuelva la colección <strong>de</strong> datos a probar anotado <strong>con</strong> @Parameters . De estemodo, cada uno <strong>de</strong> los métodos <strong>de</strong> prueba será llamado para cada una <strong>de</strong> lastuplas <strong>de</strong> valores <strong>de</strong> prueba.En el Listado 6.8 se muestra un ejemplo <strong>de</strong> clase <strong>de</strong> prueba para una batería<strong>de</strong> pruebas sobre la clase ConversorTemperaturas.1 import s t a t i c org . j u n i t . A s s e r t . ∗ ;23 import j a v a . u t i l . Arrays ;4 import j a v a . u t i l . C o l l e c t i o n ;56 import org . j u n i t . A f t e r C l a s s ;7 import org . j u n i t . B e f o r e C l a s s ;8 import org . j u n i t . Test ;9 import org . j u n i t . runner . RunWith ;10 import org . j u n i t . r u n n e r s . Parameterized ;11 import org . j u n i t . r u n n e r s . Parameterized . Parameters ;1213 import c o n v e r s o r . ConversorTemperaturas ;14 import c o n v e r s o r . TemperaturaNoValidaException ;1516 @RunWith( Parameterized . c l a s s )17 public c l a s s TestConversorTemperaturas4 {18 private double c e l s i u s ;19 private double f h a r e n h e i t ;20 private double reamur ;21 private double e r r o r ;22 private s t a t i c ConversorTemperaturas c o n v e r s o r ;2324 public TestConversorTemperaturas4 ( double c e l s i u s , double f h a r e n h e i t ,double reamur , double e r r o r ) {25 t h i s . c e l s i u s = c e l s i u s ;26 t h i s . f h a r e n h e i t = f h a r e n h e i t ;27 t h i s . reamur = reamur ;28 t h i s . e r r o r = e r r o r ;29 }3031 @Parameters32 public s t a t i c C o l l e c t i o n d atos ( ) {33 return Arrays . a s L i s t (new Object [ ] [ ] {34 { 0 . 0 , 3 2 . 0 , 0 . 0 , 0 . 0 } , // { c e l s i u s , f h a r e n h e i t , reamur , e r r o r }35 {15 , 5 9 . 0 , 1 2 . 0 , 0 . 0 } ,36 {30 , 8 6 . 0 , 2 4 . 0 , 0 . 0 } ,37 {50 , 1 2 2 . 0 , 4 0 . 0 , 0 . 0 } ,38 {90 , 1 9 4 . 0 , 7 2 . 0 , 0 . 0 }39 }) ;40 }4142 @BeforeClass43 public s t a t i c void i n i c i a C o n v e r s o r ( ) {44 c o n v e r s o r = new ConversorTemperaturas ( ) ;45 }4647 @AfterClass


6.2. PRUEBAS UNITARIAS CON JUNIT 10148 public s t a t i c void e l i m i n a C o n v e r s o r ( ) {49 c o n v e r s o r = null ;50 }5152 @Test53 public void t e s t C e l s i u s A F h a r e n h e i t ( ) throwsTemperaturaNoValidaException {54 a s s e r t E q u a l s ( f h a r e n h e i t , 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 ( c e l s i u s ) , e r r o r );55 }5657 @Test58 public void testCelsiusAReamur ( ) throws TemperaturaNoValidaException {59 a s s e r t E q u a l s ( reamur , c o n v e r s o r . celsiusAReamur ( c e l s i u s ) , e r r o r ) ;60 }61 }Listado 6.8: Ejemplo <strong>de</strong> <strong>de</strong>finición <strong>de</strong> una clase que realiza una batería <strong>de</strong>pruebas.De modo resumido, estos son los pasos para <strong>de</strong>finir una clase que ejecutabaterías <strong>de</strong> pruebas:1. Anotar la clase <strong>de</strong> prueba <strong>con</strong> @RunWith(Parameterized.class).2. Declarar un atributo en la clase por cada parámetro <strong>de</strong> prueba.3. Definir un <strong>con</strong>structor <strong>con</strong> tantos argumentos como parámetros <strong>de</strong> prueba.4. Definir un método que <strong>de</strong>vuelva una colección <strong>con</strong> todas las tuplas <strong>de</strong>prueba, y anotarlo <strong>con</strong> Parameters.Internamente y <strong>de</strong> modo esquemático, lo que JUnit hace en el caso <strong>de</strong> lasbaterías <strong>de</strong> pruebas es crear una instancia <strong>de</strong> la clase <strong>de</strong> prueba a partir <strong>de</strong>l<strong>con</strong>structor <strong>con</strong> tantos argumentos como parámetros en cada una <strong>de</strong> las tuplas<strong>de</strong> prueba, y seguidamente llama a cada uno <strong>de</strong> los métodos <strong>de</strong> prueba.6.2.6. Ejecutar varias clases <strong>de</strong> prueba. Test SuitesLo común, como hemos visto, es tener varias clases <strong>de</strong> prueba ya que a vecesno tiene sentido una única clase don<strong>de</strong> se realicen todas las pruebas. Piensapor ejemplo en las pruebas parametrizadas, quizás tengas algún caso <strong>de</strong> pruebasobre el que no tenga sentido realizar pruebas parametrizadas, como por ejemplocomprobar que se produce una excepción.Por lo tanto, si tenemos varias clases <strong>de</strong> prueba, ¿Cómo po<strong>de</strong>mos ejecutartodas las pruebas, o al menos algunas <strong>de</strong> ellas sin tener que ejecutarla cada clase<strong>de</strong> modo in<strong>de</strong>pendiente? Para ello existe un mecanismo en JUnit llamado TestSuites, que no son más que agrupaciones <strong>de</strong> clases <strong>de</strong> prueba que se ejecutanuna tras otra.Básicamente lo que hacemos es anotar una clase para que JUnit la re<strong>con</strong>ozcacomo una suite <strong>de</strong> pruebas y <strong>con</strong> otra anotación añadimos a esta clase todas lasclases <strong>de</strong> prueba que nos interese ejecutar, tal y como se muestra en el Listado6.9.1 import org . j u n i t . runner . RunWith ;2 import org . j u n i t . r u n n e r s . S u i t e ;3 import org . j u n i t . r u n n e r s . S u i t e . S u i t e C l a s s e s ;4


102CAPÍTULO 6. PRUEBAS UNITARIAS CON JUNIT5 @RunWith( S u i t e . c l a s s )6 @ S u i t e C l a s s e s ({7 TestConversorTemperaturas . class ,8 TestConversorTemperaturas2 . class ,9 TestConversorTemperaturas3 . class ,10 TestConversorTemperaturas4 . c l a s s11 })12 public c l a s s A l l T e s t s {13 }Listado 6.9: Ejemplo <strong>de</strong> una suite <strong>de</strong> pruebas Test Suite.De este modo bien po<strong>de</strong>mos ejecutar una única clase <strong>de</strong> prueba para probarel funcionamiento correcto <strong>de</strong> una clase en particular <strong>de</strong> todas las que formannuestro proyecto, o bien po<strong>de</strong>mos ejecutar toda la suite <strong>de</strong> clases <strong>de</strong> prueba paraprobar todo nuestro proyecto.6.3. Cobertura <strong>de</strong> las pruebasUna duda que nos surge al realizar pruebas unitarias es la cantidad <strong>de</strong> líneas <strong>de</strong>código que han cubierto las pruebas, ¿Ha quedado algún fragmento <strong>de</strong> códigoque no se ha ejecutado ni una sola vez para todas las pruebas? ¿Cómo po<strong>de</strong>mossaberlo?.Lo i<strong>de</strong>al es que nuestras pruebas cubran el 100 % <strong>de</strong>l código <strong>de</strong> la claseque estamos probando. Pero no caigas en el engaño <strong>de</strong> pensar que por cubrir<strong>con</strong> pruebas el 100 % <strong>de</strong>l código estás cubriendo todos los casos posibles <strong>de</strong> laejecución <strong>de</strong> ese código, ni mucho menos. Pue<strong>de</strong>s cubrir el 100 % <strong>de</strong> la ejecución<strong>de</strong>l código <strong>con</strong> casos triviales que nunca fallarán y no sacarán a la luz los posibleserrores <strong>de</strong> tu código.Para que veas <strong>con</strong> claridad la diferencia entre cobertura <strong>de</strong> código y pruebasexhaustivas el Listado 6.10 te muestra un método a probar y un par <strong>de</strong> métodos<strong>de</strong> prueba. Los métodos <strong>de</strong> prueba cubren el 100 % <strong>de</strong>l código <strong>de</strong>l método quese está probando pero, ¿Qué pasa si alguna <strong>de</strong> las referencias que se pasan almétodo <strong>de</strong> prueba es null? Evi<strong>de</strong>ntemente el método que se está probando<strong>con</strong>tiene un error que no será <strong>de</strong>scubierto por las pruebas aunque estas esténcubriendo el 100 % <strong>de</strong>l código.1 // Método que s e va a probar2 p u b l i c i n t quienEsMayor ( Persona primera , Persona segunda ) {3 i f ( primera . edad > segunda . edad ) r e t u r n −1;4 i f ( primera . edad < segunda . edad ) r e t u r n 1 ;5 e l s e r e t u r n 0 ;6 }78 // Tres métodos <strong>de</strong> prueba9 @Test10 p u b l i c void masViejoElPrimero ( ) {11 Persona primera = new Persona ( ) ;12 primera . setEdad ( 1 0 0 ) ;13 Persona segunda = new Persona ( ) ;14 segunda . setEdad ( 5 0 ) ;15 a s s e r t E q u a l s ( −1 , quienEsMayor ( primera , segunda ) , 0) ;16 }1718 @Test19 p u b l i c void masViejoElSegundo ( ) {20 Persona primera = new Persona ( ) ;21 primera . setEdad ( 5 0 ) ;22 Persona segunda = new Persona ( ) ;23 segunda . setEdad ( 1 0 0 ) ;


6.3. COBERTURA DE LAS PRUEBAS 10324 a s s e r t E q u a l s ( 1 , quienEsMayor ( primera , segunda ) , 0) ;25 }2627 @Test28 p u b l i c void mismaEdad ( ) {29 Persona primera = new Persona ( ) ;30 primera . setEdad ( 5 0 ) ;31 Persona segunda = new Persona ( ) ;32 segunda . setEdad ( 5 0 ) ;33 a s s e r t E q u a l s ( 0 , quienEsMayor ( primera , segunda ) , 0) ;34 }Listado 6.10: Los tres métodos cubren el 100 % <strong>de</strong>l código <strong>de</strong>l método que seestá probando, pero este método <strong>con</strong>tiene un error ya que no se comprueba quelas referencias sean distintas <strong>de</strong> nullExiste mo<strong>de</strong>los teóricos que dan las pautas a seguir para garantizar que laspruebas son exhaustivas, <strong>de</strong> modo que se <strong>con</strong>templan todos los posibles casos<strong>de</strong> fallo <strong>de</strong> nuestro código. La referencia [5] presenta <strong>de</strong> modo exhaustivo laspruebas <strong>de</strong> código que se han <strong>de</strong> realizar para garantizar que se cubre el 100 %<strong>de</strong> los posibles caminos <strong>de</strong> ejecución.6.3.1. EclEmma y su plug-in para EclipseAfortunadamente existen excelentes herramientas que mi<strong>de</strong>n, <strong>de</strong> modo automático,la cobertura <strong>de</strong> nuestras pruebas unitarias. Una <strong>de</strong> esas herramientas, aunqueno la única es Ecl-Emma <strong>de</strong> la que existe un plug-in para Eclipse y cuya páginaweb es http://www.eclemma.org/. Para instalar este plug-in basta seguir losmismo pasos que se mostraron en la Sección 4.5, pero siendo la dirección <strong>de</strong>lplug-in la que se indica en la página web <strong>de</strong> la herramienta.Una vez instalado este plugin te aparecerá un nuevo botón en Eclipse a laizquierda <strong>de</strong>l botón ejecutar. Si pulsas este botón cuando está seleccionada unaclase <strong>de</strong> prueba, se abrirá una nueva vista <strong>de</strong> nombre Coverage en la que se temostrará todos los resultados <strong>de</strong> la cobertura <strong>de</strong> la prueba, y lo que es <strong>de</strong> granayuda, <strong>de</strong> cada una <strong>de</strong> las líneas <strong>de</strong> código <strong>de</strong> la clase que se está probandose coloreará su fondo en ver<strong>de</strong>, si la línea ha sido cubierta completamente porla prueba; amarilla, si ha sido cubierta sólo parcialmente; o roja, si no ha sidocubierta.En la vista Coverage se muestra para cada clase probada, una tabla <strong>con</strong> elporcentaje <strong>de</strong> líneas <strong>de</strong> código cubiertas por la prueba.Sin duda, la herramienta Ecl-Emma y su plugin para Eclipse son excelentesherramientas que <strong>con</strong>tribuyen a aumentar la calidad <strong>de</strong> nuestro código.Lecturas recomendadas.Un excelente libro, <strong>de</strong> autores españoles, don<strong>de</strong> se trata <strong>de</strong> un modo completolas pruebas <strong>de</strong> software es la referencia [5]. De lectura obligada si sequiere poner en práctica las pruebas <strong>de</strong> software.Otra excelente referencia <strong>de</strong> autores españoles es [6]. En la primera parte<strong>de</strong> este libro se expone <strong>con</strong> claridad los principios <strong>de</strong>l Diseño <strong>de</strong> SoftwareDirigido por Pruebas. En la segunda parte <strong>de</strong>l libro se aplica lo expuestoen la primera parte al <strong>de</strong>sarrollo <strong>de</strong> una aplicación <strong>de</strong> ejemplo. Aunque los


104CAPÍTULO 6. PRUEBAS UNITARIAS CON JUNITlenguajes <strong>de</strong> programación que muestran los ejemplos <strong>con</strong> .Net y Python,la aplicación a <strong>Java</strong> <strong>con</strong> JUnit es directa.El capítulo 10 <strong>de</strong> [13] presenta las cómo realizar pruebas unitarias <strong>con</strong>JUnit, y en el capítulo 12 muestra como trabajar <strong>con</strong> Cobertura para analizarel grado <strong>de</strong> cobertura <strong>de</strong> nuestras pruebas. Cobertura es otra excelenteherramienta para el análisis <strong>de</strong> cobertura <strong>de</strong> las pruebas unitarias.Otro excelente título que <strong>de</strong>bería figurar en todos las estanterías <strong>de</strong> unbuen <strong>de</strong>sarrollador es Clean co<strong>de</strong> <strong>de</strong> Robert C. Martin, también <strong>con</strong>ocidocomo uncle Bob. En el capítulo 9 <strong>de</strong> esta referencia [10] está <strong>de</strong>dicado alas buenas prácticas en el diseño <strong>de</strong> test unitarios <strong>de</strong> prueba.


Capítulo 7Entrada y SalidaContenidos7.1. Flujos (Streams) . . . . . . . . . . . . . . . . . . . . 1067.2. Flujos <strong>de</strong> bytes . . . . . . . . . . . . . . . . . . . . . 1077.3. Flujos <strong>de</strong> caracteres . . . . . . . . . . . . . . . . . . 1087.4. Conexión entre flujos <strong>de</strong> bytes y <strong>de</strong> caracteres . . 1097.5. El sistema <strong>de</strong> ficheros y flujos a ficheros . . . . . . 1107.5.1. El sistema <strong>de</strong> ficheros . . . . . . . . . . . . . . . . . 1107.5.2. Flujos a ficheros . . . . . . . . . . . . . . . . . . . . 1107.5.2.1. Flujos <strong>de</strong> bytes a ficheros . . . . . . . . . . 1107.5.2.2. Flujos <strong>de</strong> caracteres a ficheros . . . . . . . 1117.6. Serialización . . . . . . . . . . . . . . . . . . . . . . . 112IntroducciónYa <strong>con</strong>ocemos como <strong>de</strong>finir clases que <strong>con</strong>tienen datos y métodos que nos permitentrabajar <strong>con</strong> las instancias <strong>de</strong> estas clases. En muchos casos, una vez quehemos trabajado <strong>con</strong> nuestros datos nos interesa almacenarlos <strong>de</strong> manera permanente,<strong>de</strong> tal modo que sea posible recuperar nuestros datos más tar<strong>de</strong> paraseguir trabajando sobre ellos.Todo lenguaje <strong>de</strong> programación proporciona una serie <strong>de</strong> mecanismos pararealizar operaciones <strong>de</strong> entrada y salida <strong>de</strong> datos. Decimos que los datos son <strong>de</strong>entrada cuando llegan a nuestra aplicación <strong>de</strong>s<strong>de</strong> una fuente <strong>de</strong> datos, y que son<strong>de</strong> salida cuando nuestra aplicación envía datos a algún sumi<strong>de</strong>ro <strong>de</strong> datos.El lenguaje <strong>de</strong> programación <strong>Java</strong> nos proporciona un paquete, <strong>con</strong> una grancantidad <strong>de</strong> clases, para po<strong>de</strong>r realizar entrada/salida en nuestras aplicaciones.Verás, que las operaciones <strong>de</strong> entrada/salida son susceptibles <strong>de</strong> lanzar grancantidad <strong>de</strong> excepciones que vamos a tener que gestionar tal y como vimos enel capítulo 5.La potencia <strong>de</strong> la aproximación <strong>de</strong> <strong>Java</strong> a las operaciones <strong>de</strong> entrada/salidaes que <strong>Java</strong> utiliza un <strong>con</strong>cepto transversal <strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong>l dispositivosobre el que se trabaja. In<strong>de</strong>pendientemente <strong>de</strong> si la salida es hacia un fichero,105


106CAPÍTULO 7. ENTRADA Y SALIDAFigura 7.1: El <strong>con</strong>cepto <strong>de</strong> flujo representado gráficamente.un Socket o una <strong>con</strong>exión <strong>de</strong> Internet, el mecanismo <strong>de</strong> entrada/salida es elmismo: el uso <strong>de</strong> Flujos (Streams).7.1. Flujos (Streams)Los flujos en <strong>Java</strong> son los canales por don<strong>de</strong> transita la información. Los flujospue<strong>de</strong>n ser <strong>de</strong> entrada, <strong>de</strong> salida, o tener ambas direcciones. Utilizaremos flujos<strong>de</strong> entrada cuando a nuestras aplicaciones lleguen datos, es <strong>de</strong>cir, cuando queramosleer datos que nos llegan <strong>de</strong>s<strong>de</strong> alguna fuente <strong>de</strong> datos. Por el <strong>con</strong>trario,utilizaremos flujos <strong>de</strong> salida cuando nuestras aplicaciones quieran enviar datosa algún sumi<strong>de</strong>ro <strong>de</strong> datos.La potencia <strong>de</strong> los flujos está relacionada <strong>con</strong> su in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> los dispositivos<strong>de</strong> entrada/salida a los que se estén <strong>con</strong>ectando. Des<strong>de</strong> el punto <strong>de</strong> vista<strong>de</strong> nuestro código, no importa que el dispositivo <strong>de</strong> salida sea una <strong>con</strong>sola enpantalla, un Socket, o un fichero en nuestro disco duro, el mecanismo <strong>de</strong> salidasiempre es el mismo. Por otro lado, no importa que el dispositivo <strong>de</strong> entrada seael teclado, una <strong>con</strong>exión a una URL, o un fichero en nuestro disco duro, el mecanismo<strong>de</strong> entrada siempre es el mismo. Las operaciones <strong>de</strong> entrada/salida en<strong>Java</strong> siempre se realizan a través <strong>de</strong> flujos que son in<strong>de</strong>pendientes <strong>de</strong> las fuenteso sumi<strong>de</strong>ros <strong>de</strong> datos. En la Figura 7.1 se muestra gráficamente el <strong>con</strong>cepto <strong>de</strong>flujo.En <strong>Java</strong> existen dos gran<strong>de</strong>s categorías <strong>de</strong> flujos, cada una <strong>de</strong> ellas <strong>con</strong> suspropias clases para realizar operaciones <strong>de</strong> entrada salida: los flujos <strong>de</strong> bytes ylos flujos <strong>de</strong> caracteres. Utilizaremos unos u otros para realizar operaciones <strong>de</strong>entrada/salida <strong>de</strong>pendiendo <strong>de</strong> la naturaleza <strong>de</strong> los datos que recibamos <strong>de</strong>s<strong>de</strong>una fuente <strong>de</strong> datos o enviemos a un sumi<strong>de</strong>ro <strong>de</strong> datos.En las siguientes secciones se va a presentar una gran cantidad <strong>de</strong> nuevasclases, lo que implica que vas a ver muchos nombres <strong>de</strong> clase nuevos. Al principiopue<strong>de</strong> parecer abrumador, pero presta atención al nombre <strong>de</strong> las clases y verásque es muy significativo, veremos los <strong>de</strong>talles en la nominación <strong>de</strong> las clases enlas siguientes secciones. Por otro lado, también verás que existe simetría entrelos nombres <strong>de</strong> las clases que realizan operaciones <strong>de</strong> lectura y las que realizan


7.2. FLUJOS DE BYTES 107Figura 7.2: Parte <strong>de</strong> la jerarquía <strong>de</strong> clases para flujos <strong>de</strong> bytes.operaciones <strong>de</strong> escritura. Y finalmente, también verás que existe simetría en elnombre <strong>de</strong> las clases que correspon<strong>de</strong>s a flujos <strong>de</strong> bytes y los nombres <strong>de</strong> lasclases que correspon<strong>de</strong>n a flujos <strong>de</strong> caracteres. Esta doble simetría y el criteriopara nominar a las clases te resultará <strong>de</strong> gran ayuda para re<strong>con</strong>ocer cual es elcometido <strong>de</strong> una clase simplemente a través <strong>de</strong> su nombre.7.2. Flujos <strong>de</strong> bytesLos flujos <strong>de</strong> bytes nos permiten leer bytes <strong>de</strong>s<strong>de</strong> una fuente <strong>de</strong> datos o escribirbytes hacia un sumi<strong>de</strong>ro <strong>de</strong> datos, es <strong>de</strong>cir nos permiten la lectura/escritura <strong>de</strong>datos binarios. Las clases que nos permiten leer/escribir sobre flujos <strong>de</strong> bytesexisten en <strong>Java</strong> <strong>de</strong>s<strong>de</strong> las primeras versiones <strong>de</strong>l lenguaje, y por ello, dispositivos<strong>de</strong> entrada como el teclado, o dispositivos <strong>de</strong> salida como una <strong>con</strong>sola en pantallason ambos flujos <strong>de</strong> bytes, aunque lo que finalmente se lee o escriba a ellos seancaracteres.Existe simetría en el modo <strong>de</strong> nombrar a las clases que realizan operaciones<strong>de</strong> lectura sobre flujos <strong>de</strong> bytes y las que realizan operaciones <strong>de</strong> escritura. Si laoperación es <strong>de</strong> lectura, el nombre <strong>de</strong> la clase <strong>con</strong>tendrá la palabra Input, si elflujo es <strong>de</strong> escritura, la clase <strong>con</strong>tendrá la palabra Output.Todas las clases que realizan operaciones <strong>de</strong> lectura <strong>de</strong> bytes extien<strong>de</strong>n a laclase abstract InputStrem, por su lado, todas las clases que realizan operaciones<strong>de</strong> escritura <strong>de</strong> bytes extien<strong>de</strong>n a la clase abstract OutputStrem. Fíjateque ambas clases son abstract y por lo tanto no se pue<strong>de</strong>n instanciar directamente.En la Figura 7.2 se muestra algunas clases <strong>de</strong> la jerarquía <strong>de</strong> flujos <strong>de</strong>bytes.La clase DataInputStream permite abrir un fichero para leer tipos <strong>de</strong> datosprimitivos, así por ejemplo, esta clase proporciona el método float readFloat()


108CAPÍTULO 7. ENTRADA Y SALIDAFigura 7.3: Parte <strong>de</strong> la jerarquía <strong>de</strong> clases para flujos <strong>de</strong> caracteres.que <strong>de</strong>vuelve un número real <strong>de</strong> precisión simple leído <strong>de</strong>s<strong>de</strong> un flujo, y booleanreadBoolean() que <strong>de</strong>vuelve un booleano leído <strong>de</strong>s<strong>de</strong> un flujo <strong>de</strong> bytes.De modo análogo, la clase DataOutputStream permite abrir un flujo para escribiren él tipos <strong>de</strong> datos primitivos, y en este caso <strong>con</strong>tamos <strong>con</strong> métodos comovoid writeFloat(float f) para escribir en un flujo <strong>de</strong> byte un número real <strong>de</strong>precisión sencilla y final void writeBoolean(boolean b) para escribir datosbooleanos sobre un flujo <strong>de</strong> bytes.Las clases BufferedInputStream y BufferedOutputStream efectúan lecturay escritura <strong>de</strong> bytes <strong>de</strong>s<strong>de</strong> un flujo utilizando una memoria intermedia (buffer)<strong>con</strong> el objeto <strong>de</strong> acelerar el proceso.7.3. Flujos <strong>de</strong> caracteresLa Figura 7.3 muestra una pequeña parte <strong>de</strong> la jerarquía <strong>de</strong> clases existenteen <strong>Java</strong> para los flujos <strong>de</strong> caracteres. Fíjate en la analogía <strong>con</strong> la Figura 7.2.De nuevo, tenemos un par <strong>de</strong> clases abstractas, abstract Rea<strong>de</strong>r en el caso <strong>de</strong>lectura <strong>de</strong> flujos <strong>de</strong> caracteres y abstract Writer en el caso <strong>de</strong> escritura.De nuevo, y también <strong>de</strong> modo análogo a los flujos <strong>de</strong> bytes, disponemos<strong>de</strong> dos subclases que proporcionan una memoria intermedia para mejorar elrendimiento <strong>de</strong>l proceso <strong>de</strong> lectura/escritura <strong>con</strong> flujos <strong>de</strong> caracteres. Estas clasesson BufferedRea<strong>de</strong>r que nos proporciona un flujo <strong>de</strong> lectura <strong>de</strong> caracteres <strong>con</strong>buffer, y BufferedWriter que nos proporciona un flujo <strong>de</strong> escritura <strong>de</strong> caracteres<strong>con</strong> buffer.En el caso <strong>de</strong> la clase BufferedRea<strong>de</strong>r, esta clase cuenta <strong>con</strong> el métodoString readLine() que nos permite leer ca<strong>de</strong>nas <strong>de</strong> caracteres.


7.4.CONEXIÓN ENTRE FLUJOS DE BYTES Y DE CARACTERES 1097.4. Conexión entre flujos <strong>de</strong> bytes y <strong>de</strong> caracteresComo acabas <strong>de</strong> ver en las secciones anteriores, para un <strong>de</strong>terminado tipo <strong>de</strong>flujo existe varias clases <strong>de</strong>finidas en el árbol <strong>de</strong> jerarquía, <strong>de</strong> modo que las claseshijas van añadiendo nueva funcionalidad sobre la que proporciona la clase padre.Tomando como ejemplo los flujos <strong>de</strong> entrada <strong>de</strong> caracteres, la clase Rea<strong>de</strong>rproporciona métodos para leer caracter a caracter <strong>de</strong>s<strong>de</strong> alguna fuente <strong>de</strong> datos,y su clase hija BufferedRea<strong>de</strong>r aña<strong>de</strong> un buffer intermedio en la lectura <strong>de</strong>modo que po<strong>de</strong>mos leer líneas <strong>de</strong> caracteres (String) <strong>de</strong>s<strong>de</strong> la fuente <strong>de</strong> datos.Por otro lado los <strong>con</strong>structores <strong>de</strong> BufferedRea<strong>de</strong>r son:BufferedRea<strong>de</strong>r(Rea<strong>de</strong>r in)BufferedRea<strong>de</strong>r(Rea<strong>de</strong>r in, int sz)ambos necesitan una referencia a Rea<strong>de</strong>r para invocarse. La nueva instancia<strong>de</strong> BufferedRea<strong>de</strong>r se <strong>con</strong>struye envolviendo a la instancia <strong>de</strong> Rea<strong>de</strong>r.Y esta es la i<strong>de</strong>a clave <strong>de</strong>l trabajo <strong>con</strong> flujos en <strong>Java</strong>, obtener <strong>de</strong> algún modoflujos <strong>de</strong> tipo básico e ir <strong>con</strong>struyendo sobre ellos nuevas instancias hasta llegara un flujo que nos proporcione la funcionalidad que necesitamos.Por otro lado, el primer tipo <strong>de</strong> flujos que se introdujo en <strong>Java</strong> <strong>de</strong>s<strong>de</strong> laversión 1.0 fueron los flujos <strong>de</strong> bytes, y <strong>de</strong>s<strong>de</strong> la versión 1.1 aparecieron losflujos <strong>de</strong> caracteres. No es por lo tanto <strong>de</strong> extrañar que tanto el teclado, comola pantalla/<strong>con</strong>sola sean flujos <strong>de</strong> bytes.Con todo esto, la pregunta que nos surge es ¿Cómo se <strong>con</strong>ectan los flujos <strong>de</strong>caracteres <strong>con</strong> los flujos <strong>de</strong> bytes para que, por ejemplo, podamos leer ca<strong>de</strong>nas<strong>de</strong> caracteres directamente <strong>de</strong>s<strong>de</strong> el teclado? La respuesta a esta pregunta esque <strong>Java</strong> proporciona clases <strong>de</strong> <strong>con</strong>exión entre ambos tipos <strong>de</strong> flujos:InputStreamRea<strong>de</strong>r toma una instancia <strong>de</strong> InputStream, flujo <strong>de</strong> entrada <strong>de</strong>bytes, y sobre él que crear un flujo <strong>de</strong> lectura <strong>de</strong> caracteres.OutputStreamWriter toma una instancia <strong>de</strong> OutputStream, flujo <strong>de</strong> salida <strong>de</strong>bytes, y sobre él que crea un flujo <strong>de</strong> escritura <strong>de</strong> caracteres.El Listado 7.1 muestra un ejemplo <strong>de</strong> uso <strong>de</strong> esta técnica para leer ca<strong>de</strong>nas<strong>de</strong> caracteres <strong>de</strong>s<strong>de</strong> teclado:1 InputStream i s = System . i n ; // El t e c l a d o e s <strong>Java</strong> e s System . i n2 InputStreamRea<strong>de</strong>r i s r = new InputStreamRea<strong>de</strong>r ( i s ) ; // Lo <strong>de</strong>coramos comoun f l u j o <strong>de</strong> c a r a c t e r e s3 BufferedRea<strong>de</strong>r br = new BufferedRea<strong>de</strong>r ( i s r ) ; // Lo <strong>de</strong>coramos <strong>con</strong> unf l u j o <strong>con</strong> memoria i n t e r m e d i a4 S t r i n g l i n e a = br . r e a d L i n e ( ) ; // Ya po<strong>de</strong>mos l e e r ca<strong>de</strong>nas <strong>de</strong> t e x t o <strong>de</strong>s<strong>de</strong>e l t e c l a d o .Listado 7.1: Técnica para leer caracteres <strong>de</strong>s<strong>de</strong> tecladoEn la línea 1 <strong>de</strong>l Listado 7.1 simplemente <strong>de</strong>finimos la referencia is hacia elteclado, que es el flujo <strong>de</strong> entrada <strong>de</strong> byte <strong>de</strong>s<strong>de</strong> el que queremos leer. En la línea2, <strong>con</strong>vertimos el flujo <strong>de</strong> entrada bytes a un flujo <strong>de</strong> entrada <strong>de</strong> caracteres <strong>con</strong> laayuda <strong>de</strong> la clase InputStreamRea<strong>de</strong>r, en este momento ya podríamos leer caracteres<strong>de</strong>s<strong>de</strong> el teclado, pero es más eficiente utilizar una memoria intermedia.


110CAPÍTULO 7. ENTRADA Y SALIDAEn la línea 3 estamos creando una instancia <strong>de</strong> la clase BufferedRea<strong>de</strong>r sobreel flujo <strong>de</strong> entrada <strong>de</strong> caracteres (InputStreamRea<strong>de</strong>r), para po<strong>de</strong>r finalmenteleer ca<strong>de</strong>nas <strong>de</strong> caracteres <strong>con</strong> la ayuda <strong>de</strong>l método String readLine(), tal ycomo se muestra en la línea 4 1 .7.5. El sistema <strong>de</strong> ficheros y flujos a ficherosUn caso particular <strong>de</strong> fuente y sumi<strong>de</strong>ro <strong>de</strong> datos son los ficheros. Des<strong>de</strong> nuestrosprogramas po<strong>de</strong>mos leer los datos <strong>con</strong>tenidos en un fichero, sean estos datos <strong>de</strong>tipo binarios o caracteres, y po<strong>de</strong>mos escribir datos a un fichero, sea estos datos<strong>de</strong> tipo binarios o caracteres.Como el acceso para realizar entrada/salida es in<strong>de</strong>pendiente <strong>de</strong>l dispositivo,lo que necesitaremos en este caso es algún medio para acce<strong>de</strong>r al sistema <strong>de</strong>ficheros. Para ello <strong>Java</strong> nos proporciona la clase File. Esta clase nos da acceso alsistema <strong>de</strong> ficheros y sobre esta clase po<strong>de</strong>mos <strong>con</strong>struir flujos <strong>de</strong> entrada/salidapar tipos <strong>de</strong> datos tanto binarios como caracteres.7.5.1. El sistema <strong>de</strong> ficherosLa clase File nos da acceso al sistema <strong>de</strong> ficheros, <strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong>l sistemaoperativo sobre el que se ejecute. Gracias a esta clase, po<strong>de</strong>mos obtenerinformación tal como la lista <strong>de</strong> ficheros o directorios bajo un directorio dado,o comprobar si el camino <strong>con</strong> el que se <strong>con</strong>struye la instancia <strong>de</strong> File hacereferencia a un fichero o a un directorio.7.5.2. Flujos a ficherosLos flujos a ficheros nos permiten acce<strong>de</strong>r a la información <strong>con</strong>tenida en los ficheros<strong>de</strong> nuestro sistema <strong>con</strong> el fin <strong>de</strong> leer <strong>de</strong>s<strong>de</strong> ellos o escribir información haciaellos. De nuevo, po<strong>de</strong>mos distinguir dos tipos <strong>de</strong> flujos a ficheros <strong>de</strong>pendiendo<strong>de</strong> si la información <strong>con</strong>tenida en ellos es <strong>de</strong> tipo binario o caracteres.7.5.2.1. Flujos <strong>de</strong> bytes a ficherosLa clase FileInputStream nos permite crear un flujo <strong>de</strong> lectura hacia un ficheropara leer <strong>de</strong>s<strong>de</strong> él datos <strong>de</strong> tipo binario. Po<strong>de</strong>mos instanciar esta clase a partir<strong>de</strong> una referencia a File o bien a partir <strong>de</strong> un String que represente el caminohasta el fichero.De modo análogo, la clase FileOutputStream nos permite crear un flujo <strong>de</strong>escritura hacia un fichero para escribir en él datos <strong>de</strong> tipo binario. Po<strong>de</strong>mosinstanciar esta clase también a partir <strong>de</strong> una referencia a File o bien a partir<strong>de</strong> un String que represente el camino hasta el fichero. En el momento <strong>de</strong> lacreación <strong>de</strong>l flujo po<strong>de</strong>mos indicar si queremos <strong>con</strong>servar el posible <strong>con</strong>tenido<strong>de</strong>l fichero en el momento <strong>de</strong> la creación <strong>de</strong>l flujo a través <strong>de</strong> un argumento <strong>de</strong>tipo booleano en el <strong>con</strong>structor.1 En el Capítulo 8 se mostrará un clase <strong>de</strong> utilidad Scanner que facilita enormemente lalectura <strong>de</strong> datos <strong>de</strong>s<strong>de</strong> teclado, y en general <strong>de</strong>s<strong>de</strong> cualquier flujo <strong>de</strong> entrada. No obstante loque aquí se ha mostrado es un ejemplo <strong>de</strong>l mecanismo general <strong>de</strong> <strong>con</strong>versión entre flujos <strong>de</strong>caracteres y flujos <strong>de</strong> bytes


7.5. EL SISTEMA DE FICHEROS Y FLUJOS A FICHEROS 1117.5.2.2. Flujos <strong>de</strong> caracteres a ficherosLa clase FileRea<strong>de</strong>r nos permite crear un flujo <strong>de</strong> lectura hacia un fichero,para leer <strong>de</strong>s<strong>de</strong> él datos <strong>de</strong> tipo caracter. De modo análogo al caso <strong>de</strong>FileInputStream, po<strong>de</strong>mos instanciar esta clase s partir <strong>de</strong> una referencia aFile o bien a partir <strong>de</strong> un String que represente el camino hasta el fichero.Finalmente, la clase FileWriter nos permite crear un flujo <strong>de</strong> escritura <strong>de</strong>caracteres hacia un fichero para escribir en él datos <strong>de</strong> tipo caracter. De modoanálogo a la clase FileOutputStream, po<strong>de</strong>mos instanciar esta clase a partir <strong>de</strong>una referencia File, <strong>de</strong> un String que indique el camino al fichero, e indicar enel momento <strong>de</strong> la creación si el fichero <strong>con</strong>serva o no el posible <strong>con</strong>tenido.Como ejemplo <strong>de</strong>l manejo <strong>de</strong> ficheros, el Listado 7.2 muestra cómo abrir unflujo a un fichero <strong>de</strong> caracteres para leer <strong>de</strong>s<strong>de</strong> él línea a línea su <strong>con</strong>tenido ymostrarlo por <strong>con</strong>sola.1 import j a v a . i o . BufferedRea<strong>de</strong>r ;2 import j a v a . i o . F i l e ;3 import j a v a . i o . FileNotFoundException ;4 import j a v a . i o . F i l e R e a d e r ;5 import j a v a . i o . IOException ;67 public c l a s s LecturaFlujoTexto {8 public LecturaFlujoTexto ( ) {9 super ( ) ;10 }1112 private void e j e c u t a ( S t r i n g camino ) {13 F i l e f i c h e r o = new F i l e ( camino ) ;14 F i l e R e a d e r f l u j o L e c t u r a ;15 BufferedRea<strong>de</strong>r f l u j o B u f f e r = null ;16 try {17 try {18 f l u j o L e c t u r a = new F i l e R e a d e r ( f i c h e r o ) ;19 f l u j o B u f f e r = new BufferedRea<strong>de</strong>r ( f l u j o L e c t u r a ) ;20 S t r i n g l i n e a ;21 while ( ( l i n e a = f l u j o B u f f e r . r e a d L i n e ( ) ) != null ) {22 System . out . p r i n t l n ( l i n e a ) ;23 }24 } f i n a l l y {25 i f ( f l u j o B u f f e r != null )26 f l u j o B u f f e r . c l o s e ( ) ;27 }28 } catch ( FileNotFoundException e ) {29 e . p r i n t S t a c k T r a c e ( ) ;30 } catch ( IOException e ) {31 e . p r i n t S t a c k T r a c e ( ) ;32 }33 }3435 public s t a t i c void main ( S t r i n g [ ] a r g s ) {36 new LecturaFlujoTexto ( ) . e j e c u t a ( a r g s [ 0 ] ) ;37 }38 }Listado 7.2: Lectura <strong>de</strong>s<strong>de</strong> un flujo <strong>de</strong> texto hacia un ficheroComo curiosidad <strong>de</strong>l Listado 7.2, fíjate que hay un bloque try{...}finally{...} en las líneas 17-27 que está incluido <strong>de</strong>ntro <strong>de</strong> otro bloquetry{...}. El uso <strong>de</strong> estos bucles anidados facilita la lectura <strong>de</strong> código, y suejecución es la siguiente: si se produce alguna excepción durante el trabajo <strong>con</strong>el fichero (líneas <strong>de</strong> código 18-23), se ejecutará el bloque finally{...} <strong>con</strong> loque se cerrará el fichero, y la excepción se propagará al bloque try{...} externo,que es quien tiene los bloques catch{...} para atrapar a cada una <strong>de</strong>


112CAPÍTULO 7. ENTRADA Y SALIDAlas excepciones posibles. Este modo <strong>de</strong> codificación es ampliamente utilizado y<strong>con</strong>viene que lo incorpores como técnica <strong>de</strong> escritura <strong>de</strong> tu propio código.7.6. SerializaciónLa Serialización es el mecanismo por el cual <strong>Java</strong> es capaz <strong>de</strong> <strong>con</strong>vertir un objetoen una secuencia <strong>de</strong> bytes. De este modo, po<strong>de</strong>mos crear un flujo a partir <strong>de</strong>la secuencia <strong>de</strong> bytes que representa al objeto para escribirlo en un fichero oenviarlo a través <strong>de</strong> un Socket por ejemplo.El <strong>con</strong>cepto <strong>de</strong> serialización es extremadamente potente, si somos capaces<strong>de</strong> obtener una secuencia <strong>de</strong> bytes <strong>de</strong>l objeto y sobre ella crear un flujo, po<strong>de</strong>mosenviar el objeto a cualquier dispositivo que lo acepte. Y <strong>de</strong> modo análogo,podríamos <strong>con</strong>ectarnos a una fuente <strong>de</strong> datos a través <strong>de</strong> un flujo <strong>de</strong> entraday obtener objetos <strong>de</strong>s<strong>de</strong> ella. De hecho, esta técnica es tan potente que es lapieza sobre la que <strong>de</strong>scansan otras tecnologías <strong>Java</strong> como la invocación remota<strong>de</strong> método (Remote Method Innvocation - RMI ), y gran parte <strong>de</strong> las tecnologías<strong>Java</strong> en su edición Enterprise 2 .En esta sección vamos a presentar la serialización y cómo utilizarla paraalmacenar un objeto en un fichero, y en el Capítulo 15 veremos cómo utilizar laserialización para enviar y recibir objetos a través <strong>de</strong> un Socket.Para indicar que un objeto es Serializable <strong>de</strong>be implementar la interfaceSerializable. Esta interface no <strong>de</strong>clara ningún método, es únicamente unamarca semántica para que <strong>Java</strong> sepa que en algún momento po<strong>de</strong>mos quererserializar el objeto. Cuando el objeto serializable se <strong>con</strong>vierta en una secuencia<strong>de</strong> bytes, esta secuencia, a<strong>de</strong>más <strong>de</strong> incluir el valor <strong>de</strong> los atributos <strong>de</strong> la instanciaincluye más información, como la clase <strong>de</strong> la instancia y un número <strong>de</strong>serie <strong>de</strong> la clase, para po<strong>de</strong>r hacer un <strong>con</strong>trol <strong>de</strong> versiones <strong>de</strong> la clase. Por <strong>con</strong>vención,este número <strong>de</strong> <strong>con</strong>trol se <strong>de</strong>clara como private static final longserialVersionUID. El valor <strong>de</strong> este atributo lo po<strong>de</strong>mos fijar nosotros mismos,pero eso no garantiza que otro programador pueda utilizar el mismo número <strong>de</strong>serie para una clase completamente distinta. <strong>Java</strong> nos proporciona una herramienta,integrada en el JDK, que genera números <strong>de</strong> versión a partir <strong>de</strong> la clasecompilada, esta herramienta es serialver, su uso es serialver [-classpath]nombreClase y un ejemplo <strong>de</strong> uso es:$ serialver -classpath . serializacion.Personaserializacion.Persona: static final long serialVersionUID =7360368493593648647L;En este caso la herramientaserialver ha generado el número <strong>de</strong> serie7360368493593648647L para la clase Persona. Y es muy improbable queserialver genere el mismo número <strong>de</strong> versión para cualquier otra clase, loque nos garantiza que este número <strong>de</strong> versión es único e i<strong>de</strong>ntifica a la clasePersona.2 <strong>Java</strong> 2 Enterprise Edition está enfocada al <strong>de</strong>sarrollo <strong>de</strong> aplicaciones <strong>de</strong> servidor. Sonaplicaciones que se no se ejecutan en las máquinas <strong>de</strong> un cliente, si no en un servidor remoto.Usualmente, el ciente acce<strong>de</strong> al servidor a través <strong>de</strong> un navegador web, aunque no es la únicaopción posible


7.6.SERIALIZACIÓN 113Eclipse es capaz <strong>de</strong> invocar a serialver <strong>de</strong> manera transparente. Si nuestranueva clase implementa la interface Serializable y olvidamos incluir el atributoserialVersionUID, Eclipse nos mostrará un aviso. Si corregimos el avisoque nos da Eclipse seleccionando la opción Add generated serial verion ID,se añadirá el número <strong>de</strong> serie obtenido <strong>con</strong> serialver.El Listado 7.3 muestra la clase Persona que implementa la interfaceSerializable y a la que se le ha añadido el número <strong>de</strong> versión generado <strong>con</strong>serialver.1 package s e r i a l i z a c i o n ;23 import j a v a . i o . S e r i a l i z a b l e ;45 public c l a s s Persona implements S e r i a l i z a b l e {6 private s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 7360368493593648647L ;7 S t r i n g nombre ;8 S t r i n g a p e l l i d o s ;9 S t r i n g t e l e f o n o ;1011 Persona ( ) { }1213 Persona ( 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 ) {14 t h i s . nombre = nombre ;15 t h i s . a p e l l i d o s = a p e l l i d o s ;16 t h i s . t e l e f o n o = t e l e f o n o ;17 }1819 S t r i n g getNombre ( ) {20 return nombre ;21 }2223 S t r i n g g e t A p e l l i d o s ( ) {24 return a p e l l i d o s ;25 }2627 S t r i n g g e t T e l e f o n o ( ) {28 return t e l e f o n o ;29 }3031 @Overri<strong>de</strong>32 public S t r i n g t o S t r i n g ( ) {33 return " P e r s o n a [ a p e l l i d o s = " + a p e l l i d o s + " , n o m b r e = " + nombre34 + " , t e l e f o n o = " + t e l e f o n o + " ] " ;35 }36 }Listado 7.3: La clase Persona lista para ser serializadaEl Listado 7.4 muestra un ejemplo <strong>de</strong> como serializar esta clase y la secuencia<strong>de</strong> bytes, y cómo almacenar la secuencia <strong>de</strong> bytes obtenida en un fichero parasu recuperación posterior.1 package s e r i a l i z a c i o n ;23 import j a v a . i o . FileNotFoundException ;4 import j a v a . i o . FileOutputStream ;5 import j a v a . i o . IOException ;6 import j a v a . i o . ObjectOutput ;7 import j a v a . i o . ObjectOutputStream ;8 import j a v a . i o . OutputStream ;910 public c l a s s E s c r i t o r P e r s o n a {11 public E s c r i t o r P e r s o n a ( ) {12 super ( ) ;13 }1415 private void e j e c u t a ( S t r i n g f i c h e r o ) {


114CAPÍTULO 7. ENTRADA Y SALIDA16 OutputStream s t r e a m E s c r i t u r a ;17 ObjectOutput s t r e a m E s c r i t u r a P e r s o n a = null ;18 try {19 try {20 s t r e a m E s c r i t u r a = new FileOutputStream ( f i c h e r o ) ;21 s t r e a m E s c r i t u r a P e r s o n a = new ObjectOutputStream ( s t r e a m E s c r i t u r a ) ;22 s t r e a m E s c r i t u r a P e r s o n a . w r i t e O b j e c t (new Persona ( " J a m e s " , " G o s l i n g " , "555 123 456 " ) ) ;23 } f i n a l l y {24 i f ( s t r e a m E s c r i t u r a P e r s o n a != null ) s t r e a m E s c r i t u r a P e r s o n a . c l o s e ( ) ;25 }26 } catch ( FileNotFoundException e ) {27 e . p r i n t S t a c k T r a c e ( ) ;28 } catch ( IOException e ) {29 e . p r i n t S t a c k T r a c e ( ) ;30 }31 }3233 public s t a t i c void main ( S t r i n g [ ] a r g s ) {34 new E s c r i t o r P e r s o n a ( ) . e j e c u t a ( a r g s [ 0 ] ) ;35 }3637 }Listado 7.4: Serialización <strong>de</strong> la clase Persona hacia un fichero <strong>de</strong> bytesSi ejecutamos el programa <strong>Java</strong> anterior, introduciendo por línea <strong>de</strong> instruccionescomo nombre <strong>de</strong> fichero persona.ser 3 se creará un fichero <strong>de</strong> tipobinario cuyo <strong>con</strong>tenido es una instancia <strong>de</strong> la clase Persona cuyos atributostienen los valores asignados.El Listado 7.5 muestra un ejemplo completo <strong>de</strong> cómo leer un objeto serializadoalmacenado en un fichero <strong>de</strong> tipo binario.1 package s e r i a l i z a c i o n ;23 import j a v a . i o . F i l e I n p u t S t r e a m ;4 import j a v a . i o . FileNotFoundException ;5 import j a v a . i o . IOException ;6 import j a v a . i o . InputStream ;7 import j a v a . i o . ObjectInput ;8 import j a v a . i o . ObjectInputStream ;910 public c l a s s LectorPersona {11 public LectorPersona ( ) {12 super ( ) ;13 }1415 private void e j e c u t a ( S t r i n g f i c h e r o ) {16 InputStream streamLectura ;17 ObjectInput streamLecturaPersona = null ;18 try {19 try {20 streamLectura = new F i l e I n p u t S t r e a m ( f i c h e r o ) ;21 streamLecturaPersona = new ObjectInputStream ( streamLectura ) ;22 Persona persona = ( Persona ) streamLecturaPersona . readObject ( ) ;23 System . out . p r i n t l n ( persona ) ;24 } f i n a l l y {25 i f ( streamLecturaPersona != null ) streamLecturaPersona . c l o s e ( ) ;26 }27 } catch ( FileNotFoundException e ) {28 e . p r i n t S t a c k T r a c e ( ) ;29 } catch ( IOException e ) {30 e . p r i n t S t a c k T r a c e ( ) ;31 } catch ( ClassNotFoundException e ) {32 e . p r i n t S t a c k T r a c e ( ) ;33 }3 Por <strong>con</strong>vención, para los ficheros que <strong>con</strong>tienen datos <strong>de</strong> objetos serializados se utiliza laextensión .ser


7.6.SERIALIZACIÓN 11534 }3536 public s t a t i c void main ( S t r i n g [ ] a r g s ) {37 new LectorPersona ( ) . e j e c u t a ( a r g s [ 0 ] ) ;38 }3940 }Listado 7.5: Des-serialización <strong>de</strong> la clase Persona <strong>de</strong>s<strong>de</strong> un fichero <strong>de</strong> bytesFíjate cómo, <strong>de</strong> nuevo, se han utilizado bloques try{...} anidados parafacilitar la gestión <strong>de</strong> excepciones.En el capítulo 15 se mostrará otro ejemplo <strong>de</strong> serialización <strong>de</strong> objetos peroesta vez se enviará la secuencia <strong>de</strong> bytes que representa al objeto a través <strong>de</strong>los flujos que proporciona un Socket, <strong>con</strong> lo que podremos escribir un servidorcapaz <strong>de</strong> enviarnos, a través <strong>de</strong> una red <strong>de</strong> comunicación <strong>de</strong> datos, objetosserializados y podremos recuperarlos en un cliente.Ejercicios1. Amplia la funcionalidad <strong>de</strong> la aplicación <strong>de</strong> la agenda, para que sea posiblealmacenar los datos <strong>de</strong> los <strong>con</strong>tactos en un fichero <strong>de</strong> texto parasu posterior recuperación. Escribe tanto el código <strong>de</strong> escritura como <strong>de</strong>lectura.2. Sigue ampliando la funcionalidad <strong>de</strong> tu aplicación <strong>de</strong> la agenda para quesea posible serializarla a un fichero <strong>de</strong> tipo binario para su posterior recuperación.Escribe tanto el código <strong>de</strong> escritura como el <strong>de</strong> lectura.Lecturas recomendadasLa referencia [2] <strong>de</strong>dica todo el Capítulo 15 al estudio <strong>de</strong>l mecanismo <strong>de</strong>entrada/salida en <strong>Java</strong>.La referencia [3] <strong>de</strong>dica también todo el Capítulo 14 al estudio <strong>de</strong>l mecanismo<strong>de</strong> entrada/salida en <strong>Java</strong>. En particular las secciones <strong>de</strong>dicadas ala serialización <strong>de</strong> objetos son muy interesantes.


116CAPÍTULO 7. ENTRADA Y SALIDA


Capítulo 8Algunas clases <strong>de</strong> utilidad<strong>de</strong>l paquete estándarContenidos8.1. La clase Scanner . . . . . . . . . . . . . . . . . . . . 1188.2. Trabajo <strong>con</strong> ca<strong>de</strong>nas <strong>de</strong> caracteres . . . . . . . . . 1208.2.1. La clase String . . . . . . . . . . . . . . . . . . . . . 1208.2.2. Las clases StringBuffer y StringBuil<strong>de</strong>r . . . . . . 1218.3. Clases recubridoras . . . . . . . . . . . . . . . . . . 1228.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . . 1248.5. Trabajo <strong>con</strong> fechas . . . . . . . . . . . . . . . . . . . 1288.5.1. La clase Date . . . . . . . . . . . . . . . . . . . . . . 1288.5.2. Las clases Calendar y GregorianCalendar . . . . . . 1298.6. Matemáticas . . . . . . . . . . . . . . . . . . . . . . 1298.6.1. La clase Math . . . . . . . . . . . . . . . . . . . . . . 1298.6.2. La clase Random . . . . . . . . . . . . . . . . . . . . . 130IntroducciónLa edición estándar <strong>de</strong> <strong>Java</strong> proporciona una amplísima colección <strong>de</strong> clases ya<strong>de</strong>finidas. Estas clases ya <strong>de</strong>finidas son <strong>de</strong> gran utilidad, a medida que comoprogramadores vamos <strong>con</strong>ociendo nuevas clases <strong>de</strong> esta colección nuestra productividadaumenta.En este Capítulo presentamos un pequeño grupo <strong>de</strong> clases <strong>de</strong> gran utilidad,aunque existen otras muchas tan útiles como las aquí presentadas. Obviamente,por cuestión <strong>de</strong> espacio, sólo presentamos las que creemos más interesantesllegados a este punto.117


118CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDAR8.1. La clase ScannerEn el Capítulo 7 comentamos que en <strong>Java</strong> tanto la <strong>con</strong>sola como el teclado sonflujos <strong>de</strong> bytes. Esto se <strong>de</strong>be a que en la versión 1.0 <strong>de</strong> <strong>Java</strong> no existían flujos <strong>de</strong>caracteres. Para guardar la compatibilidad hacia atrás, tanto el teclado como la<strong>con</strong>sola siguen siendo flujos <strong>de</strong> caracteres en las versiones actuales <strong>de</strong> <strong>Java</strong>.Como finalmente lo que nos interesa leer <strong>de</strong>s<strong>de</strong> el teclado son caracteres,tenemos que aplicar la técnica <strong>de</strong>l recubrimiento que vimos en el Capítulo 7 para<strong>con</strong>seguir leer ca<strong>de</strong>nas <strong>de</strong> caracteres. El uso <strong>de</strong> la clase <strong>de</strong> utilidad Scanner nosoculta los flujos y nos permite leer directamente <strong>de</strong> flujos, en particular <strong>de</strong>s<strong>de</strong>el teclado.La clase Scanner está presente en <strong>Java</strong> <strong>de</strong>s<strong>de</strong> la versión 5. Esta clase permiteanalizar una ca<strong>de</strong>na <strong>de</strong> texto utilizando para ello expresiones regulares 1 .Las ca<strong>de</strong>nas se pue<strong>de</strong>n proporcionar directamente en el momento <strong>de</strong> crear lainstancia <strong>de</strong> clase Scanner o a través <strong>de</strong> un flujo.Supongamos que tenemos un fichero <strong>de</strong> texto que almacena datos <strong>de</strong> unaagenda <strong>de</strong> teléfonos <strong>con</strong> el formato Persona: Nombre: Apellidos: Telefono,un ejemplo <strong>de</strong> <strong>con</strong>tenido <strong>de</strong>l fichero es 2 :Persona: LUISA: GARCIA MORENO: 313372295Persona: ROSARIO: GONZALEZ ESTEBAN: 560248322Persona: MANUEL: SANZ GARCIA: 571365702Persona: FRANCISCO: VAZQUEZ FERRER: 690109651Persona: VICTOR: MU~NOZ LOPEZ: 500661266El Listado 8.1 muestra un ejemplo <strong>de</strong> uso <strong>de</strong> la clase Scanner para leer líneaa línea <strong>de</strong> este fichero. El método hasNext() nos sirve para comprobar si haymás elementos a <strong>de</strong>volver.1 package u t i l i d a d ;23 import j a v a . i o . F i l e ;4 import j a v a . i o . FileNotFoundException ;5 import j a v a . u t i l . Scanner ;67 public f i n a l c l a s s U t i l i d a d S c a n n e r {8 private s t a t i c f i n a l S t r i n g FICHERO = " a g e n d a . txt " ;9 private U t i l i d a d S c a n n e r ( ) {10 super ( ) ;11 }1213 private void e j e c u t a ( ) {14 try {15 Scanner s c a n n e r = new Scanner (new F i l e (FICHERO) ) ;16 while ( s c a n n e r . hasNext ( ) )17 System . out . p r i n t l n ( s c a n n e r . nextLine ( ) ) ;18 } catch ( FileNotFoundException e ) {19 e . p r i n t S t a c k T r a c e ( ) ;20 }21 }2223 public s t a t i c void main ( S t r i n g [ ] a r g s ) {24 new U t i l i d a d S c a n n e r ( ) . e j e c u t a ( ) ;25 }1 Para más información sobre qué son expresiones regulares el lector pue<strong>de</strong> <strong>con</strong>sultar http://en.wikipedia.org/wiki/Regular_expression2 Estos datos se han generado aleatoriamente tomando como base los datos estadísticos <strong>de</strong>lInstituto Nacional <strong>de</strong> Estadística. Estos datos estadísticos se pue<strong>de</strong>n <strong>con</strong>sultar en la direcciónhttp://www.ine.es


8.1. LA CLASE SCANNER 1192627 }Listado 8.1: Lectura <strong>de</strong> líneas <strong>de</strong> texto <strong>de</strong>s<strong>de</strong> un fichero <strong>con</strong> el uso <strong>de</strong> la claseScannerLa clase Scanner tiene métodos para po<strong>de</strong>r leer tipos <strong>de</strong> datos primitivostal y como se muestra en el Listado 8.2.1 package u t i l i d a d ;23 import j a v a . i o . F i l e ;4 import j a v a . i o . FileNotFoundException ;5 import j a v a . u t i l . Scanner ;67 public f i n a l c l a s s U t i l i d a d S c a n n e r 2 {8 private s t a t i c f i n a l S t r i n g FICHERO = " a g e n d a . txt " ;910 private U t i l i d a d S c a n n e r 2 ( ) {11 super ( ) ;12 }1314 private void e j e c u t a ( ) {15 try {16 Scanner s c a n n e r = new Scanner (new F i l e (FICHERO) ) ;17 while ( s c a n n e r . hasNext ( ) ) {18 a n a l i z a L i n e a ( s c a n n e r . nextLine ( ) ) ;19 }20 } catch ( FileNotFoundException e ) {21 e . p r i n t S t a c k T r a c e ( ) ;22 }23 }2425 private void a n a l i z a L i n e a ( S t r i n g l i n e a ) {26 Scanner s c a n n e r = new Scanner ( l i n e a ) ;27 s c a n n e r . u s e D e l i m i t e r ( " : " ) ;28 S t r i n g persona , nombre , a p e l l i d o s ;29 int t e l e f o n o ;30 persona = s c a n n e r . next ( ) ;31 nombre = s c a n n e r . next ( ) ;32 a p e l l i d o s = s c a n n e r . next ( ) ;33 t e l e f o n o = s c a n n e r . n e x t I n t ( ) ;34 System . out . p r i n t l n ( nombre + " , " + a p e l l i d o s + " , " + t e l e f o n o ) ;35 }3637 public s t a t i c void main ( S t r i n g a r g s [ ] ) {38 new U t i l i d a d S c a n n e r 2 ( ) . e j e c u t a ( ) ;39 }40 }Listado 8.2: Lectura <strong>de</strong> líneas <strong>de</strong> texto <strong>de</strong>s<strong>de</strong> un fichero <strong>con</strong> el uso <strong>de</strong> la claseScannerEl resultado <strong>de</strong> la ejecución <strong>de</strong> este código es el siguiente:FRANCISCO JOSE,ALVAREZ MARTIN,90727037ROBERTO,CASTRO RUIZ,945953372MANUEL,PEREZ HERRERA,520908284JULIA,ALVAREZ ORTEGA,482596843TOMAS,VARGAS MARTINEZ,691825532Si <strong>con</strong>struimos la instancia <strong>de</strong> Scanner sobre el flujo <strong>de</strong> bytes que representaal teclado System.in, <strong>con</strong> la clase Scanner podremos leer tipos <strong>de</strong> datosprimitivos, tal y como muestra el Listado 8.3.


120CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDAR1 Scanner l e c t o r T e c l a d o = new Scanner ( System . i n ) ;2 System . out . p r i n t ( " I n t r o d u c e un e n t e r o : " ) ;3 int e n t e r o = l e c t o r T e c l a d o . n e x t I n t ( ) ;4 System . out . p r i n t l n ( " I n t r o d u c e un real : " ) ;5 f l o a t r e a l = l e c t o r T e c l a d o . n e x t F l o a t ( ) ;6 System . out . p r i n t l n ( " E n e r o = " + e n t e r o + " ; real = " + r e a l ) ;Listado 8.3: La clase Scanner nos permite leer tipos primitivos <strong>de</strong>s<strong>de</strong> teclado.La clase Scanner nos permite leer <strong>de</strong>s<strong>de</strong> cualquier flujo <strong>de</strong> entrada, no sólo<strong>de</strong>s<strong>de</strong> el teclado.8.2. Trabajo <strong>con</strong> ca<strong>de</strong>nas <strong>de</strong> caracteresYa <strong>con</strong>oces una clase que representa y manipula ca<strong>de</strong>nas <strong>de</strong> caracteres, la claseString. No obstante, <strong>Java</strong> proporciona clases más eficientes para trabajar <strong>con</strong>ca<strong>de</strong>nas, ya que la clase String es inmutable y al manipular las ca<strong>de</strong>nas <strong>de</strong>texto que representa, se crean nuevas instancias <strong>de</strong> esta clase, <strong>con</strong> el coste quesupone la creación <strong>de</strong> objetos. A efectos prácticos la inmutabilidad <strong>de</strong> la claseString significa que cuando <strong>con</strong>catenamos dos ca<strong>de</strong>nas el resultado es una nuevaca<strong>de</strong>na, no se amplia ninguna <strong>de</strong> las ca<strong>de</strong>nas originales para albergar la nuevaca<strong>de</strong>na.1 S t r i n g ca<strong>de</strong>naConcatenada = " Hola " + " , como e s t á s " ;Listado 8.4: ”La <strong>con</strong>catenación <strong>de</strong> dos ca<strong>de</strong>nas crea una nueva ca<strong>de</strong>na.”En el Listado 8.4 se crean tres objetos <strong>de</strong> tipo String, el primero <strong>de</strong> ellos<strong>con</strong>tiene la ca<strong>de</strong>na <strong>de</strong> caracteres Hola, el segundo <strong>con</strong>tiene , como estás y eltercero <strong>con</strong>tiene Hola, como estás.8.2.1. La clase StringLa clase String representa una ca<strong>de</strong>na <strong>de</strong> caracteres inmutable, es <strong>de</strong>cir, unavez creada no se pue<strong>de</strong> modificar la secuencia <strong>de</strong> caracteres. Por la tanto es útilutilizar esta clase cuando no vamos a hacer manipulaciones <strong>con</strong>tinuadas sobrela ca<strong>de</strong>na que representa.El único operador sobrecargado en <strong>Java</strong> es el operador + cuando se aplicasobre ca<strong>de</strong>nas <strong>con</strong> el significado <strong>de</strong> <strong>con</strong>catenarlas, tal y como muestra el Listado8.5. Al <strong>con</strong>catenar dos ca<strong>de</strong>nas se crea una nueva ca<strong>de</strong>na para almacenar elresultado, <strong>de</strong> ahí la ineficiencia al utilizar el operador + sobre String.1 S t r i n g primera = " Hola " ;2 S t r i n g segunda = " m u n d o . "3 S t r i n g r e s u l t a d o = primera + segunda ;Listado 8.5: Uso <strong>de</strong>l operador + para <strong>con</strong>catenar dos ca<strong>de</strong>nas <strong>de</strong> caracteres.Para comparar dos ca<strong>de</strong>nas <strong>de</strong> caracteres, caracter a caracter, no <strong>de</strong>bemoscometer el error <strong>de</strong> utilizar el operador == ya que este operador compara laigualdad <strong>de</strong> dos referencias. Para comparar dos ca<strong>de</strong>na <strong>de</strong> caracteres utilizamosel método public boolean equals(Object o), que compara el String actual<strong>con</strong> la representación como String <strong>de</strong>l objeto que se le pasa como argumento. El


8.2. TRABAJO CON CADENAS DE CARACTERES 121método equals(Object o) distingue entre mayúsculas y minúsculas, si queremoscomparar dos ca<strong>de</strong>nas <strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong>l uso <strong>de</strong> mayúsculas/minúsculasutilizaremos el método public boolean equalsIgnoreCase(String s).Para averiguar el número <strong>de</strong> caracteres <strong>de</strong> una ca<strong>de</strong>na utilizamos el métodopublic int length(). Si queremos <strong>con</strong>vertir todas las letras <strong>de</strong> una ca<strong>de</strong>na aminúsculas utilizamos el método public String toLowerCase(), y el métodopublic String toUpperCase en caso <strong>de</strong> que la queramos en mayúsculas.La clase String también posee el método sobrecargado static StringvalueOf(boolean/char/int/long/float/double) para <strong>con</strong>vertir tipos <strong>de</strong> datosprimitivos a su representación como ca<strong>de</strong>nas <strong>de</strong> caracteres.Un método interesante que nos permite trocear una ca<strong>de</strong>na <strong>de</strong> caracteres apartir <strong>de</strong> una subca<strong>de</strong>na <strong>con</strong>tenida en ellas es String split(String s), don<strong>de</strong>el argumento es una expresión regular. El Listado 8.6 muestra un ejemplo <strong>de</strong> uso<strong>de</strong>l método split, fíjate que estamos dividiendo la ca<strong>de</strong>na original buscando elpatrón representado por otra ca<strong>de</strong>na, ", ".1 S t r i n g i n i c i a l = " Esta ca<strong>de</strong>na , c o n t i e n e comas , por la que q u i e r o t r o c e a r. " ;2 S t r i n g t r o z o s [ ] = i n i c i a l . s p l i t ( " , " ) ;3 for ( S t r i n g t r o z o : t r o z o s )4 System . out . p r i n t l n ( t r o z o ) ;Listado 8.6: Uso <strong>de</strong>l método split para trocear una ca<strong>de</strong>na.El resultado <strong>de</strong> la ejecución <strong>de</strong> este código es:Esta ca<strong>de</strong>na<strong>con</strong>tiene comaspor la que quiero trocear.Para po<strong>de</strong>r dar formato a ca<strong>de</strong>nas al estilo <strong>de</strong> C, la clase String nos proporcionael método public static String format(String ca<strong>de</strong>na, Object...argumentos. El Listado 8.7 muestra un sencillo caso <strong>de</strong> uso.1 System . out . p r i n t l n ( S t r i n g . format ( " El v a l o r <strong>de</strong> PI es : \ %2.2 f " , 3 . 1 4 1 5 ) ) ;Listado 8.7: Ejemplo <strong>de</strong> formato usando el método format <strong>de</strong> la clase StringEl resultado <strong>de</strong> la ejecución <strong>de</strong>l Listado 8.7:El valor <strong>de</strong> PI es: 3,14Si se necesitan formatos más sofisticados, la clase Formatter es <strong>de</strong> granayuda.8.2.2. Las clases StringBuffer y StringBuil<strong>de</strong>rLa clase StringBuffer también representa una ca<strong>de</strong>na <strong>de</strong> caracteres comola clase String pero esta vez la ca<strong>de</strong>na que representa pue<strong>de</strong> cambiar.Esta clase es la recomendada si, por ejemplo, queremos <strong>con</strong>catenardos ca<strong>de</strong>nas, ya que el resultado no crea una nueva ca<strong>de</strong>na, si no quese modifica la original para representar la ca<strong>de</strong>na <strong>con</strong>catenada final. Paraello la clase StringBuffer posee el método sobrecargado StringBuffer


122CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDARappend(boolean/int/long/float/double/String/StringBuffer) que aña<strong>de</strong>la representación como String <strong>de</strong>l argumento a la ca<strong>de</strong>na actual.La clase StringBuffer posee otros métodos interesantes <strong>de</strong> manipulación.Por ejemplo, el método int in<strong>de</strong>xOf(String s) <strong>de</strong>vuelve la posición<strong>de</strong> la primera ocurrencia <strong>de</strong> la ca<strong>de</strong>na s <strong>de</strong>ntro <strong>de</strong> la ca<strong>de</strong>naoriginal. El método sobrecargado StringBuffer insert(int offset,boolean/char/int/long/float/double/String) inserta la representación <strong>de</strong>lsegundo argumento en la ca<strong>de</strong>na original a partir <strong>de</strong>l offset indicado en el primerargumento. El Listado 8.8 muestra un ejemplo <strong>de</strong> uso <strong>de</strong> estos métodos.1 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 ( " Hola . " ) ;2 sb . i n s e r t ( sb . in<strong>de</strong>xOf ( " . " ) , " <strong>Java</strong> " ) ;3 System . out . p r i n t l n ( sb ) ;Listado 8.8: Uso <strong>de</strong> los métodos in<strong>de</strong>xOf y insert <strong>de</strong> la clase StringBufferLos métodos que manipulan la representación <strong>de</strong> la ca<strong>de</strong>na <strong>de</strong>ntro <strong>de</strong> laclase StringBuffer están sincronizados, luego se pue<strong>de</strong>n utilizar en aplicacionesen las que varios hilos están accediendo a la misma referencia <strong>de</strong> la claseStringBuffer. Veremos el uso <strong>de</strong> hilos y lo que significa que un método esté sincronizadoen el Capítulo 14.Por su parte, la clase StringBuil<strong>de</strong>r funciona exactamente igual que la claseStringBuffer, <strong>de</strong> hecho los métodos que proporciona la clase StringBuil<strong>de</strong>rson exactamente los mismo que la clase StringBuffer, pero esta vez ninguno<strong>de</strong> ellos está sincronizado por razones <strong>de</strong> eficiencia 3 .8.3. Clases recubridorasComo ya sabes, en <strong>Java</strong> existen dos gran<strong>de</strong>s grupos <strong>de</strong> tipos <strong>de</strong> datos, los tipos<strong>de</strong> datos primitivos y los tipos <strong>de</strong> datos referencia. Sin embargo, <strong>Java</strong> proporcionaclases que recubren los tipos <strong>de</strong> datos primitivos para po<strong>de</strong>r trabajar <strong>con</strong>ellos a través <strong>de</strong> referencias, es <strong>de</strong>cir, como <strong>con</strong> cualquier otro objeto. Esto esespecialmente útil al trabajar <strong>con</strong> colecciones, tal y como veremos en la Sección8.4.Tal y como muestra la Tabla 8.1, para cada tipo primitivo existe una claserecubridora. Crear una clase recubridora a partir <strong>de</strong> un tipo primitivo es muysencillo, tal y como muestra el Listado 8.9, don<strong>de</strong> se crean clases recubridorastanto a partir <strong>de</strong> datos primitivos como a partir <strong>de</strong> la representación comoca<strong>de</strong>na <strong>de</strong> texto <strong>de</strong> los datos primitivos.1 I n t e g e r e n t e r o = new I n t e g e r ( 1 5 ) ;2 I n t e g e r e n t e r o S t r i n g = new I n t e g e r ( " 10 " ) ;3 Boolean booleanoVerda<strong>de</strong>ro = new Boolean ( true ) ;4 Boolean b o o l e a n o F a l s o = new Boolean ( " f a l s e " ) ;Listado 8.9: Ejemplo <strong>de</strong> creación <strong>de</strong> clases recubridoras.Para recuperar, como tipos primitivos, los valores que almacena una claserecubridora, estas proporcionan métodos tal y como muestra el Listado 8.103 Como veremos en el Capítulo 14, el acceso a métodos sincronizados tiene un sobrecostetemporal <strong>de</strong>bido al uso <strong>de</strong> cerrojos.


8.3. CLASES RECUBRIDORAS 123Tipo primitivovoidbooleancharbyteshortintlongfloatdoubleClase recubridorajava.lang.Voidjava.lang.Booleanjava.lang.Characterjava.lang.Bytejava.lang.Shortjava.lang.Integerjava.lang.Longjava.lang.Floatjava.lang.DoubleTabla 8.1: Para cada uno <strong>de</strong> los tipos primitivos <strong>de</strong> la columna <strong>de</strong> la izquierda,<strong>Java</strong> proporciona una clase recubridora, en la columna <strong>de</strong> la <strong>de</strong>recha.1 int e n t e r o P r i m i t i v o = e n t e r o . i n t V a l u e ( ) ;2 boolean b o o l e a n o P r i m i t i v o = booleanoVerda<strong>de</strong>ro . booleanValue ( ) ;Listado 8.10: Recuperación <strong>de</strong> los datos como tipos primitivos a partir <strong>de</strong> lasclases recubridoras.Sin embargo, en la mayoría <strong>de</strong> los casos es <strong>de</strong> gran utilidad hacer uso <strong>de</strong>lmecanismo <strong>de</strong> Autoboxing introducido en la versión 5 <strong>de</strong> <strong>Java</strong>. Este mecanismo<strong>con</strong>vierte, <strong>de</strong> modo automático y transparente para el usuario, tipos primitivosa recubridores en una asignación siempre que el tipo primitivo y la claserecubridora sean compatibles, tal y como muestra el Listado 8.11.1 I n t e g e r e n t e r o = 1 5 ;2 int e n t e r o P r i m i t i v o = e n t e r o ;3 Boolean booleanoVerda<strong>de</strong>ro = true ;4 boolean b o o l e a n o P r i m i t i v o = booleanoVerda<strong>de</strong>ro ;Listado 8.11: Ejemplos <strong>de</strong> autoboxing para la <strong>con</strong>versión entre tipos <strong>de</strong> datosprimitivos y sus correspondientes clases recubridoras.El mecanismo <strong>de</strong> Autoboxing es especialmente útil cuando se utilizan lasclases colección, tal y como veremos en la Sección 8.4.1 int e n t e r o = I n t e g e r . p a r s e I n t ( " 10 " ) ;2 double r e a l = Double . parseDouble ( " 3 . 1 4 1 5 9 2 " ) ;Listado 8.12: Métodos para obtener tipos primitivos a partir <strong>de</strong> ca<strong>de</strong>nas <strong>de</strong>caracteres.Un grupo <strong>de</strong> métodos especialmente útil cuando se procesan datos <strong>de</strong> entrada<strong>de</strong> tipo texto, como por ejemplo los parámetros <strong>de</strong> llamada a nuestraaplicación, y los queremos <strong>con</strong>vertir a tipos primitivos son los que muestra elListado 8.12, <strong>de</strong> modo análogo cada clase recubridora, tiene un método pararealizar la correspondiente <strong>con</strong>versión.


124CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDARFigura 8.1: Interfaces básicos presentes en el <strong>Java</strong> Collections Framework.InterfaceImplementaciónSet< E > HashSet< E >SortedSet< E > TreeSet< E >List< E > ArrayList< E >, LinkendList< E >, Vector< E >Queue< E > LinkendList< E >Map< E > HasMap< E >, HashTable< E >SortedMap< E > TreeMap< E >Tabla 8.2: Varias clases colección y los interfaces que implementan.8.4. ColeccionesLas clases que forman parte <strong>de</strong>l grupo <strong>de</strong> clases colección son aquellas que nossirven para almacenar referencias a objetos, e implementan estructuras <strong>de</strong> datostales como listas, tablas <strong>de</strong> dispersión, <strong>con</strong>juntos, etcétera. El <strong>con</strong>junto <strong>de</strong> interfaces,clases y algoritmos que nos permiten trabajar <strong>con</strong> estructuras <strong>de</strong> datos seagrupan bajo el <strong>Java</strong> Collections Framework.La Figura 8.4 muestra los interfaces básicos <strong>de</strong>finidos en el <strong>Java</strong> CollectionsFramework. La parte <strong>de</strong> la <strong>de</strong>recha <strong>de</strong> esta figura muestra los interfaces querepresentan la funcionalidad <strong>de</strong> las clase que implementan colecciones <strong>de</strong> referenciascomo ✭✭<strong>con</strong>juntos✮✮; mientras que en la parte <strong>de</strong>recha <strong>de</strong> la figura semuestran los interfaces que representan la funcionalidad <strong>de</strong> las clases que implementencolecciones don<strong>de</strong> cada elemento <strong>de</strong> la colección representa una parejaclave/valor.Las clases que representan colecciones <strong>de</strong> referencias pue<strong>de</strong>n implementaruno o más <strong>de</strong> estos interfaces. La Tabla 8.2 muestra algunas clases colección ylos interfaces que implementan.Fíjate que en la Tabla 8.2 hay una novedad, los símbolos < E > a <strong>con</strong>tinuación<strong>de</strong> las interfaces o las clases, esta sintaxis significa que la interface o la clasealmacena un tipo Genérico. En el momento <strong>de</strong> creación <strong>de</strong> la clase <strong>de</strong>bemos especificarcual es el tipo <strong>de</strong> los elementos que va a <strong>con</strong>tener la clase. Veremos <strong>con</strong><strong>de</strong>talle cómo utilizar genéricos en nuestra propias clases en el Capítulo 9. En


8.4. COLECCIONES 125este capítulo sólo veremos como trabajar <strong>con</strong> ellos en el momento <strong>de</strong> creación <strong>de</strong>las clases y las ventajas que supone el uso <strong>de</strong> genéricos, que fueron introducidosen la versión 5 <strong>de</strong> <strong>Java</strong>. Aunque, es posible instanciar las clases colección sin especificarel tipo <strong>de</strong> los elementos que <strong>con</strong>tendrá. En este caso, el compilador sólomostrará un aviso <strong>con</strong> el siguiente texto ArrayList is a raw type. References togeneric type ArrayList< E > should be parameterized no un error. Sin embargo,es muy a<strong>con</strong>sejable <strong>de</strong>clarar el tipo <strong>de</strong> los elementos al crear la colección.En particular, la clase ArrayList< E > representa una secuencia in<strong>de</strong>xada<strong>de</strong> elementos, cada uno <strong>de</strong> ellos ocupa una posición <strong>de</strong>ntro <strong>de</strong> la estructura, yse pue<strong>de</strong> acce<strong>de</strong>r a un elementos <strong>de</strong>ntro <strong>de</strong> la estructura a través <strong>de</strong> su índice.Un ejemplo <strong>de</strong> cómo usar las clases colección se muestra en los siguienteslistados: el Listado 8.13 muestra la interface Figura que <strong>de</strong>clara un método paraque las clases que la implementen <strong>de</strong>finan la funcionalidad <strong>de</strong>l cálculo <strong>de</strong>l área<strong>de</strong> una figura. El Listado 8.14 <strong>de</strong>fine la clase Circulo <strong>de</strong> cuyas instancias po<strong>de</strong>moscalcular el área. Análogamente, el Listado 8.15 <strong>de</strong>fine la clase Rectangulo<strong>de</strong> cuyas instancias también po<strong>de</strong>mos calcular el área. Finalmente, el Listado8.16 <strong>de</strong>fine la clase TrianguloRectangulo <strong>de</strong> cuyas instancias también po<strong>de</strong>moscalcular el área. El Listado 8.17 muestra un ejemplo <strong>de</strong> cómo utilizar la claseArrayList< F igura > para <strong>con</strong>tener referencias a clases que implementen lainterface Figura, a ella po<strong>de</strong>mos añadir círculos, cuadrados y triángulos, y lospo<strong>de</strong>mos recuperar utilizando un bucle for...each.1 package c o l e c c i o n e s . f i g u r a s ;23 public interface Figura {4 public s t a t i c f i n a l double PI = 3 . 1 4 1 5 9 2 ;5 public double getArea ( ) ;6 }Listado 8.13: Interface que <strong>de</strong>clara la funcionalidad <strong>de</strong>l cálculo <strong>de</strong>l área <strong>de</strong> unafigura geométrica.1 package c o l e c c i o n e s . f i g u r a s ;23 public c l a s s C i r c u l o implements Figura {4 private double r a d i o ;56 public C i r c u l o ( ) {7 super ( ) ;8 }910 public C i r c u l o ( double r a d i o ) {11 t h i s . r a d i o = r a d i o ;12 }1314 @Overri<strong>de</strong>15 public double getArea ( ) {16 return PI∗ r a d i o ∗ r a d i o ;17 }1819 @Overri<strong>de</strong>20 public S t r i n g t o S t r i n g ( ) {21 S t r i n g B u i l d e r b u i l d e r = new S t r i n g B u i l d e r ( ) ;22 b u i l d e r . append ( " C i r c u l o [ r a d i o = " ) ;23 b u i l d e r . append ( r a d i o ) ;24 b u i l d e r . append ( " ] " ) ;25 b u i l d e r . append ( " Area = " ) ;26 b u i l d e r . append ( getArea ( ) ) ;27 return b u i l d e r . t o S t r i n g ( ) ;28 }


126CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDAR29 }Listado 8.14: Esta clase representa un círculo <strong>de</strong>l que se pue<strong>de</strong> calcular su área.1 package c o l e c c i o n e s . f i g u r a s ;23 public c l a s s Rectangulo implements Figura {4 private double base ;5 private double a l t u r a ;67 public Rectangulo ( ) {8 super ( ) ;9 }1011 public Rectangulo ( double base , double a l t u r a ) {12 super ( ) ;13 t h i s . base = base ;14 t h i s . a l t u r a = a l t u r a ;15 }1617 @Overri<strong>de</strong>18 public double getArea ( ) {19 return base ∗ a l t u r a ;20 }2122 @Overri<strong>de</strong>23 public S t r i n g t o S t r i n g ( ) {24 S t r i n g B u i l d e r b u i l d e r = new S t r i n g B u i l d e r ( ) ;25 b u i l d e r . append ( " R e c t a n g u l o [ a l t u r a = " ) ;26 b u i l d e r . append ( a l t u r a ) ;27 b u i l d e r . append ( " , base = " ) ;28 b u i l d e r . append ( base ) ;29 b u i l d e r . append ( " ] " ) ;30 b u i l d e r . append ( " Area = " ) ;31 b u i l d e r . append ( getArea ( ) ) ;32 return b u i l d e r . t o S t r i n g ( ) ;33 }34 }Listado 8.15: Esta clase representa un rectángulo <strong>de</strong>l que se pue<strong>de</strong> calcular suárea.1 package c o l e c c i o n e s . f i g u r a s ;23 public c l a s s T r i a n g u l oRectangulo implements Figura {4 private double base ;5 private double a l t u r a ;67 public T r i a n g u l o Rectangulo ( ) {8 super ( ) ;9 }1011 public T r i a n g u l o Rectangulo ( double base , double a l t u r a ) {12 super ( ) ;13 t h i s . base = base ;14 t h i s . a l t u r a = a l t u r a ;15 }1617 @Overri<strong>de</strong>18 public double getArea ( ) {19 return base ∗ a l t u r a / 2 ;20 }2122 @Overri<strong>de</strong>23 public S t r i n g t o S t r i n g ( ) {24 S t r i n g B u i l d e r b u i l d e r = new S t r i n g B u i l d e r ( ) ;25 b u i l d e r . append ( " T r i a n g u l o R e c t a n g u l o [ a l t u r a = " ) ;26 b u i l d e r . append ( a l t u r a ) ;27 b u i l d e r . append ( " , base = " ) ;


8.4. COLECCIONES 12728 b u i l d e r . append ( base ) ;29 b u i l d e r . append ( " ] " ) ;30 b u i l d e r . append ( " Area = " ) ;31 b u i l d e r . append ( getArea ( ) ) ;32 return b u i l d e r . t o S t r i n g ( ) ;33 }34 }Listado 8.16: Esta clase representa un triángulo rectángulo <strong>de</strong>l que se pue<strong>de</strong>calcular su área.1 package c o l e c c i o n e s . f i g u r a s ;23 import j a v a . u t i l . A r r a y L i s t ;45 public f i n a l c l a s s P r i n c i p a l {6 public s t a t i c void main ( S t r i n g [ ] a r g s ) {7 ArrayList f i g u r a s = new ArrayList () ;8 f i g u r a s . add (new C i r c u l o ( 1 ) ) ;9 f i g u r a s . add (new Rectangulo ( 1 , 2) ) ;10 f i g u r a s . add (new T r i a n g u l oRectangulo ( 1 , 2) ) ;11 for ( Figura f i g u r a : f i g u r a s )12 System . out . p r i n t l n ( f i g u r a ) ;13 }14 }Listado 8.17: Ejemplo <strong>de</strong> uso <strong>de</strong> la clase ArrayList< F igura >.La ejecución <strong>de</strong> esta pequeña aplicación muestra el siguiente resultado:Circulo [radio=1.0] Area=3.141592Rectangulo [altura=2.0, base=1.0] Area=2.0TrianguloRectangulo [altura=2.0, base=1.0] Area=1.0Si en el ArrayList< F igura > <strong>de</strong>l Listado 8.17 intentásemos añadir unainstancia <strong>de</strong> una clase que no implementa el interface Figura, obtendríamosun error en tiempo <strong>de</strong> compilación. Si no indicásemos el tipo <strong>de</strong> los datos quemaneja la colección en el momento <strong>de</strong> la creación <strong>de</strong>l ArrayList, el compiladorno hubiese <strong>de</strong>tectado el error, y se produciría en tiempo <strong>de</strong> compilación al extraerel elemento erróneo y mo<strong>de</strong>larlo a la interface común Figura, tal y como muestrael Listado 8.18 y el resultado <strong>de</strong> su ejecución.1 package c o l e c c i o n e s . f i g u r a s ;23 import j a v a . u t i l . A r r a y L i s t ;45 public c l a s s P r i n c i p a l 2 {6 public s t a t i c void main ( S t r i n g [ ] a r g s ) {7 A r r a y L i s t f i g u r a s = new A r r a y L i s t ( ) ;8 f i g u r a s . add (new C i r c u l o ( 1 ) ) ;9 f i g u r a s . add (new Rectangulo ( 1 , 2) ) ;10 f i g u r a s . add (new T r i a n g u l oRectangulo ( 1 , 2) ) ;11 f i g u r a s . add (new I n t e g e r ( 1 ) ) ;12 for ( Object f i g u r a : f i g u r a s )13 System . out . p r i n t l n ( ( Figura ) f i g u r a ) ;14 }15 }Listado 8.18: Ejemplo <strong>de</strong> uso <strong>de</strong> la clase ArrayList sin especificar el tipo <strong>de</strong> loselementos <strong>de</strong> la colección. Se producirá un error en tiempo <strong>de</strong> ejecución.


128CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDARCirculo [radio=1.0] Area=3.141592Rectangulo [altura=2.0, base=1.0] Area=2.0TrianguloRectangulo [altura=2.0, base=1.0] Area=1.0Exception in thread "main" java.lang.ClassCastException:java.lang.Integer cannot be cast to colecciones.figuras.Figura atcolecciones.figuras.Principal.main(Principal.java:13)Otros métodos útiles <strong>de</strong> la clase ArrayList< E > (que comparte <strong>con</strong> el resto<strong>de</strong> clases que implementan la interface List) son: E get(int posicion), <strong>de</strong>vuelveel elementos en la posición indicada; void clear() elimina todos los elementos;boolean <strong>con</strong>tains(Object o), <strong>de</strong> vuelve true si el elementos está enla lista y false en caso <strong>con</strong>trario; boolean isEmpty(), <strong>de</strong>vuelve true si no elArrayList< E > no <strong>con</strong>tiene ningún elemento y false en caso <strong>con</strong>trario; intsize(), <strong>de</strong>vuelve el número <strong>de</strong> elementos.8.5. Trabajo <strong>con</strong> fechasEl paquete <strong>de</strong> clases <strong>de</strong> utilidad <strong>de</strong> <strong>Java</strong> nos proporciona un <strong>con</strong>junto <strong>de</strong> clasespara trabajar <strong>con</strong> fechas y especificar el formato cuando la fecha se muestrecomo texto.8.5.1. La clase DateLa clase Date representa un instante <strong>de</strong>l tiempo <strong>con</strong> una precisión <strong>de</strong> milisegundos.Para obtener el instante <strong>de</strong> tiempo actual, simplemente creamos unainstancia <strong>de</strong> esta clase a partir <strong>de</strong> su <strong>con</strong>structor por <strong>de</strong>fecto. La información sealmacena como un entero long cuyo origen <strong>de</strong> tiempos es el 1 <strong>de</strong> Enero <strong>de</strong> 1970a las 00:00:00 horas.Muchos <strong>de</strong> los métodos <strong>de</strong> esta clase están obsoletos, en particular, todosaquellos métodos relacionados <strong>con</strong> el trabajo <strong>con</strong> fechas. Para trabajar <strong>con</strong> fechas,por ejemplo para saber qué día <strong>de</strong> la semana será mi próximo cumpleaños,se utiliza la clase GregorianCalendar tal y como veremos en la Sección 8.5.2.Un sencillo ejemplo <strong>de</strong> uso <strong>de</strong> la clase Date se muestra en el Listado 8.19, loque mostraría por <strong>con</strong>sola:Ahora: Fri Jul 02 10:19:40 CEST 2010don<strong>de</strong> se está utilizando un formato anglosajón para mostrar la fecha.1 System . out . p r i n t l n ( " A h o r a : " + new Date ( ) ) ;Listado 8.19: Uso <strong>de</strong> la clase Date para mostrar el instante actual.La clase SimpleDateFormat nos permite <strong>de</strong>finir el formato <strong>con</strong> el que queremosmostrar las fechas. Para una <strong>de</strong>scripción <strong>de</strong> los símbolos que se pue<strong>de</strong>nutilizar al especificar el formato, se pue<strong>de</strong> <strong>con</strong>sultar la documentación <strong>de</strong> estaclase en esta dirección http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html, aquí sólo mostraremos el ejemplo <strong>de</strong>l Listado8.20, don<strong>de</strong> se está utilizando EEEE para mostrar el día <strong>de</strong> la semana, dd paramostrar el ordinal <strong>de</strong>l día <strong>de</strong>ntro <strong>de</strong>l mes, MMMM para mostrar el nombre


8.6.MATEMÁTICAS 129<strong>de</strong>l mes completo, yyyy para mostrar el año, hh para mostrar la hora en formato24 horas, mm para mostrar los minutos <strong>de</strong> la hora, y ss para mostrar lossegundos <strong>de</strong>ntro <strong>de</strong>l minuto. Con este formato, el texto obtenido es:Ahora: viernes 02 <strong>de</strong> julio <strong>de</strong> 2010 (10:30:29)1 SimpleDateFormat s d f = new SimpleDateFormat ( " EEEE dd ’ <strong>de</strong> ’ MMMM ’ <strong>de</strong> ’ yyyy’( ’ hh ’: ’ mm ’: ’ ss ’) ’ " ) ;2 System . out . p r i n t l n ( " A h o r a : " + s d f . format (new Date ( ) ) ) ;Listado 8.20: Uso <strong>de</strong> la clase SimpleDateFormat para <strong>de</strong>finir el formato <strong>de</strong> unafecha como una ca<strong>de</strong>na <strong>de</strong> texto.8.5.2. Las clases Calendar y GregorianCalendarLa clase Calendar nos permite <strong>con</strong>vertir instantes temporales, representadospor la clase Date, a un fecha expresada en días, meses, año, horas, minutos,segundos. La clase Calendar es abstracta, por lo que no se pue<strong>de</strong> instanciar.La clase Calendar nos proporciona la funcionalidad mínima para trabajar <strong>con</strong>fechas, y otras extensiones <strong>de</strong> esta clase implementan calendarios <strong>con</strong>cretos. Estees el caso <strong>de</strong> la clase GregorianCalendar que implementa el cálculo <strong>de</strong> fechasen el calendario gregoriano. Esta clase nos permite, por ejemplo, saber qué día<strong>de</strong> la semana será <strong>de</strong>ntro <strong>de</strong> 40 días, tal y como muestra el Listado 8.21.1 SimpleDateFormat s d f = new SimpleDateFormat ( " EEEE dd ’ <strong>de</strong> ’ MMMM ’ <strong>de</strong> ’yyyy " ) ;2 GregorianCalendar c a l e n d a r i o = new GregorianCalendar ( ) ;3 System . out . p r i n t l n ( " A h o r a . " + s d f . format ( c a l e n d a r i o . getTime ( ) ) ) ;4 c a l e n d a r i o . add ( Calendar .DAY OF YEAR, 40) ;5 System . out . p r i n t l n ( " D e n t r o <strong>de</strong> 40 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 <strong>con</strong> fechascomo, por ejemplo, sumar una cantidad <strong>de</strong> días a la fecha actual para <strong>con</strong>ocerla nueva fechaTodas las <strong>con</strong>stantes para indicar el día, mes, etcétera están <strong>de</strong>finidas en laclase Calendar.8.6. MatemáticasEn el paquete estándar <strong>de</strong> <strong>Java</strong> en<strong>con</strong>tramos algunas clases para hacer cálculosmatemáticos, algunas <strong>de</strong> ellas son la clase Math y la clase Random.8.6.1. La clase MathLa clase Math nos proporciona algunas <strong>de</strong> las funciones matemáticas trigonométricas,logarítmicas y otras <strong>de</strong> utilidad. Todos los métodos <strong>de</strong> esta claseson static, por lo que no hace falta crear una instancia para po<strong>de</strong>r utilizarlos.El Listado 8.22 muestra algunos ejemplos sencillos <strong>de</strong> las funciones matemáticasque proporciona la clase Math.


130CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDAR1 System . out . p r i n t l n ( S t r i n g . format ( " El seno <strong>de</strong> %2.2 f es %1.3 f " , Math . PI/4 , Math . s i n ( Math . PI /4) ) ) ;2 System . out . p r i n t l n ( S t r i n g . format ( " La raíz c u a d r a d a <strong>de</strong> 2 es %1.4 f " ,Math . s q r t ( 2 ) ) ) ;3 System . out . p r i n t l n ( S t r i n g . format ( " El l o g a r i t m o n a t u r a l <strong>de</strong> %1.3 f es%1.3 f " , Math . E, Math . l o g ( Math . E) ) ) ;Listado 8.22: Algunas funciones <strong>de</strong> la clase Math.El resultado <strong>de</strong> la ejecución <strong>de</strong>l Listado 8.22 es el siguiente:El seno <strong>de</strong> 0,79 es 0,707La raíz cuadrada <strong>de</strong> 2 es 1,4142El logaritmo natural <strong>de</strong> 2,718 es 1,0008.6.2. La clase RandomLa clase Random genera secuencias aleatorias <strong>de</strong> números o valores booleanos. Lasecuencia <strong>de</strong> números es reproducible si al crear la instancia <strong>de</strong> Random utilizamosla misma semilla. Los valores <strong>de</strong> las secuencias son equiprobables, exceptosi utilizamos el método double nextGaussian(), en cuyo caso la secuencia generadasigue una distribución aleatoria <strong>de</strong> media 0 y <strong>de</strong>sviación estándar 1. ElListado 8.23 muestra algunos ejemplos <strong>de</strong> uso <strong>de</strong> los métodos que proporcionaesta clase.1 Random e q u i p r o b a b l e s = new Random ( ) ;2 System . out . p r i n t l n ( " Una 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 <strong>de</strong> n ú m e r o se n t r e 0 y 100. " ) ;3 for ( int i = 0 ; i < 1 0 ; i ++)4 System . out . p r i n t ( e q u i p r o b a b l e s . n e x t I n t ( 1 0 0 ) + " ; " ) ;56 System . out . p r i n t l n ( " Una 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 <strong>de</strong> n ú m e r o s . " ) ;7 for ( int i = 0 ; i < 1 0 ; i ++)8 System . out . p r i n t ( S t r i n g . format ( " %.2 f ; " , e q u i p r o b a b l e s . nextGaussian( ) ) ) ;Listado 8.23: Secuencias <strong>de</strong> valores aleatorios generados por la clase Random.Cuestiones.1. Si lees el API <strong>de</strong> la clase Calendar observarás que aunque esta clase es abstracta,posee el método public static Calendar getInstance() que<strong>de</strong>vuelve una referencia a Calendar perfectamente funcional y <strong>con</strong> la quepo<strong>de</strong>mos trabajar <strong>con</strong> fechas como si <strong>de</strong> la clase GregorianCalendar setratase. ¿Cómo es esto posible siendo la clase Calendar abstracta?.Ejercicios.1. Escribe un programa que calcule la cuota mensual <strong>de</strong> una hipoteca por elsistema francés, dada por la siguiente fórmula:capital · nmensualidad =11 −(1 + n) (12·años)


8.6.MATEMÁTICAS 131don<strong>de</strong> n = interes1200 .Lecturas recomendadas.El Capítulo 17 <strong>de</strong> la referencia [2] muestra nuevas clases <strong>de</strong> utilidad.


132CAPÍTULO 8. ALGUNAS CLASES DE UTILIDAD DEL PAQUETE ESTÁNDAR


Capítulo 9Programación <strong>con</strong> genéricosContenidos9.1. ¿Qué son los tipos <strong>de</strong> datos genéricos? . . . . . . . 1339.2. Métodos genéricos . . . . . . . . . . . . . . . . . . . 1349.3. Clases genéricas . . . . . . . . . . . . . . . . . . . . 1359.4. Ampliación <strong>de</strong>l tipo genérico . . . . . . . . . . . . 1389.4.1. Tipos genéricos <strong>con</strong> límite superior . . . . . . . . . . 1399.4.2. Comodines . . . . . . . . . . . . . . . . . . . . . . . 1399.5. Borrado <strong>de</strong> tipo y compatibilidad <strong>con</strong> código heredado. . . . . . . . . . . . . . . . . . . . . . . . . . 141IntroducciónEn este capítulo se presenta la programación <strong>con</strong> genéricos, que fue introducidaen <strong>Java</strong> en la versión 5. Los genéricos nos permiten <strong>de</strong>finir clases quetrabajarán <strong>con</strong> instancias <strong>de</strong> objetos <strong>de</strong> los que no especificamos su tipo enel momento <strong>de</strong> la <strong>de</strong>finición <strong>de</strong> la clase. El tipo <strong>de</strong> las referencias que la clasemanejará se especifica en el momento <strong>de</strong> crear las instancias <strong>de</strong> la clase genérica.El <strong>con</strong>cepto <strong>de</strong> programación <strong>con</strong> genéricos es muy potente, como has vistoen la sección 8.4 <strong>de</strong>dicada a las clases colección. Las clases colección son <strong>con</strong>tenedores<strong>de</strong> referencias <strong>de</strong> las que se especifica su tipo en el momento <strong>de</strong> crearla instancia <strong>de</strong> la clase colección. Como vimos, una ventaja <strong>de</strong>l uso <strong>de</strong> genéricoses asegurar que las referencias <strong>con</strong> las que trabaja la clase genérica son <strong>de</strong> tipocompatible <strong>con</strong> el especificado en el momento <strong>de</strong> la instanciación <strong>de</strong> la clasegenérica, <strong>de</strong> lo <strong>con</strong>trario, obtendremos un error en tiempo <strong>de</strong> compilación, errorque pasaría <strong>de</strong>sapercibido sin el uso <strong>de</strong> tipos genéricos.9.1. ¿Qué son los tipos <strong>de</strong> datos genéricos?Sin <strong>de</strong>tenernos en <strong>de</strong>talles, en la sección 8.4 vimos cómo utilizar las clases colección,y vimos que estas clases pue<strong>de</strong>n trabajar <strong>con</strong> cualquier tipo <strong>de</strong> datos,basta <strong>con</strong> indicarlo en el momento <strong>de</strong> instanciar la clase colección. Vimos la133


134CAPÍTULO 9. PROGRAMACIÓN CON GENÉRICOSgran ventaja que esto suponía al no trabajar <strong>con</strong> referencias a la clase Objectque <strong>de</strong>bíamos mo<strong>de</strong>lar al tipo a<strong>de</strong>cuado al extraer los elementos <strong>de</strong> la colección.Al trabajar sin genéricos po<strong>de</strong>mos cometer errores (introducir una referencia <strong>de</strong>tipo incompatible <strong>con</strong> el resto <strong>de</strong> elementos en la colección), <strong>de</strong> los que no nosdaremos cuenta en tiempo <strong>de</strong> compilación, causando graves problemas durantela ejecución <strong>de</strong> nuestras aplicaciones. El uso <strong>de</strong> genéricos hace posible la <strong>de</strong>tección<strong>de</strong> estos errores <strong>de</strong> incompatibilidad <strong>de</strong> tipos durante la fase <strong>de</strong> compilaciónhaciendo que nuestro código sea más robusto.Un tipo <strong>de</strong> datos genérico es un tipo <strong>de</strong> datos que no se especifica, únicamentese indica que se utilizará algún tipo <strong>de</strong> dato pero no se indica el tipo <strong>con</strong>cretohasta que no se utiliza. Los tipos <strong>de</strong> datos genéricos se pue<strong>de</strong>n utilizar en la<strong>de</strong>finición <strong>de</strong> un método, o en la <strong>de</strong>finición <strong>de</strong> una clase.9.2. Métodos genéricosUn método <strong>de</strong>finido en una clase pue<strong>de</strong> trabajar <strong>con</strong> un tipo genérico aunque laclase no lo haga. El Listado 9.1 muestra un ejemplo <strong>de</strong> un método que trabaja<strong>con</strong> un tipo genérico. Fíjate en la <strong>de</strong>claración <strong>de</strong>l método private voidmuestraNombreClase(T t), la indica que se va a utilizar un tipo genérico,<strong>de</strong> modo que la lista <strong>de</strong> argumentos (T t) se interpreta como una referencia<strong>de</strong> tipo genérico T. El modo <strong>de</strong> uso es una simple llamada al método comopor ejemplo metodoGenerico.muestraNombreClase(new Float(1));, en estecaso, el tipo genérico se sustituye por el tipo particular Float, <strong>de</strong> modo queel método se reescribe como private void muestraNombreClase(Float t).1 package g e n e r i c o s ;23 import t i p o s . Persona ;45 public f i n a l c l a s s MetodoGenerico {6 private MetodoGenerico ( ) {7 super ( ) ;8 }910 private void muestraNombreClase (T t ) {11 System . out . p r i n t l n ( " Soy una i n s t a n c i a <strong>de</strong> la c l a s e : " + t . g e t C l a s s ( ) .getCanonicalName ( ) ) ;12 }1314 public s t a t i c void main ( S t r i n g [ ] a r g s ) {15 MetodoGenerico metodoGenerico = new MetodoGenerico ( ) ;16 metodoGenerico . muestraNombreClase (new F l o a t ( 1 ) ) ;17 metodoGenerico . muestraNombreClase (new Persona ( ) ) ;18 }19 }Listado 9.1: Definición y uso <strong>de</strong> un método genérico.Sintaxis <strong>de</strong> <strong>Java</strong>Para indicar que un método trabaja <strong>con</strong> tipos genéricos se escribe < T > entreel modificador <strong>de</strong> acceso y el tipo <strong>de</strong> dato <strong>de</strong> retorno <strong>de</strong>l método.Lo que ocurre en el momento <strong>de</strong> la compilación en el ejemplo anterior, esque se sustituye el símbolo <strong>de</strong> tipo genérico por el tipo <strong>con</strong>creto (Float) en


9.3. CLASES GENÉRICAS 135el ejemplo <strong>de</strong>l Listado 9.1.Si el método trabaja <strong>con</strong> un par <strong>de</strong> tipos genéricos que pue<strong>de</strong>n ser diferentesse indica como private void metodo(T t, U u).Convención <strong>de</strong> codificaciónSe usa la letra para indicar el primer tipo genérico, si es necesario indicarmás tipos genéricos se toman las letras mayúsculas que siguen a T, por ejemplo. La <strong>con</strong>vención utiliza las siguientes letras para indicar tipos genéricos:E, Indica Elemento, como en el caso <strong>de</strong> las clase Colección.K, Indica Clave, como en el caso <strong>de</strong> los Mapas.N, Indica Numero.T, S, U, V, etc. Indica Tipo.V, Indica Valor, como en el caso <strong>de</strong> los Mapas.9.3. Clases genéricasSupongamos que queremos <strong>de</strong>finir una clase que represente una medida tomadapor un sensor, <strong>de</strong> modo que cada medida almacena el valor <strong>de</strong>l dato medidoy un comentario <strong>de</strong>scriptivo. Inicialmente no <strong>con</strong>ocemos los tipos <strong>de</strong> medidasque nos pue<strong>de</strong> <strong>de</strong>volver un sensor, existen sensores <strong>de</strong> temperatura que mi<strong>de</strong>ntemperaturas, y también sensores <strong>de</strong> viento que mi<strong>de</strong>n la intensidad y dirección<strong>de</strong>l viento, por lo que no po<strong>de</strong>mos <strong>de</strong>cidir en el momento <strong>de</strong> la <strong>de</strong>finición <strong>de</strong> laclase Sensor la naturaleza <strong>de</strong>l dato que representa la medida. ¿Cómo lo po<strong>de</strong>mosresolver? Evi<strong>de</strong>ntemente, utilizando genéricos. La i<strong>de</strong>a es <strong>de</strong>jar sin especificar eltipo <strong>de</strong> datos que representa la medida tal y como se muestra en el Listado 9.2.1 package s e n s o r e s ;23 public abstract c l a s s Sensor {4 protected T medida ;5 private S t r i n g d e s c r i p c i o n ;67 public T getMedicion ( ) {8 return medida ;9 }1011 public f i n a l void s e t D e s c r i p c i o n ( S t r i n g d e s c r i p c i o n ) {12 t h i s . d e s c r i p c i o n = d e s c r i p c i o n ;13 }1415 public S t r i n g g e t D e s c r i p c i o n ( ) {16 return d e s c r i p c i o n ;17 }18 }Listado 9.2: La clase Sensor no especifica el tipo <strong>de</strong> dato que proporciona, seindica <strong>con</strong> .Ahora es el momento <strong>de</strong> crear algunos sensores <strong>con</strong>cretos. El primer sensorque implementaremos es un sensor <strong>de</strong> temperaturas. Ya que la temperatura sepue<strong>de</strong> especificar como un número real, elegimos la clase Float para representar


136CAPÍTULO 9. PROGRAMACIÓN CON GENÉRICOSla medida <strong>de</strong> temperatura <strong>de</strong> modo que nuestra clase SensorTemperatura lapo<strong>de</strong>mos <strong>de</strong>finir tal y como muestra el Listado 9.3.1 package s e n s o r e s ;23 import j a v a . u t i l . Random ;45 public c l a s s SensorTemperatura extends Sensor {6 private s t a t i c f i n a l f l o a t TEMPERATURA MAXIMA = 4 5 ;7 private Random temperatura = new Random ( ) ;89 public SensorTemperatura ( ) {10 super ( ) ;11 medida = new F l o a t (TEMPERATURA MAXIMA∗ temperatura . n e x t F l o a t ( ) ) ;12 s e t D e s c r i p c i o n ( ) ;13 }1415 public f i n a l void s e t D e s c r i p c i o n ( ) {16 super . s e t D e s c r i p c i o n ( " Dato <strong>de</strong> t e m p e r a t u r a en g r a d o s C e l s i u s " ) ;17 }18 }Listado 9.3: La clase SensorTemperatura <strong>de</strong>fine un sensor capaz <strong>de</strong> registrartemperaturas representadas como FloatPara que se vea <strong>con</strong> mayor claridad el uso <strong>de</strong> genéricos, vamos a completar elejemplo <strong>de</strong>finiendo un nuevo sensor, esta vez un sensor <strong>de</strong> velocidad <strong>de</strong>l vientocapaz <strong>de</strong> tomar datos <strong>de</strong> la intensidad y dirección <strong>de</strong>l viento. Esta vez ningúntipo <strong>de</strong> dato primitivo nos sirve para representar la medida <strong>de</strong> la velocidad <strong>de</strong>lviento, ya que este tiene intensidad y dirección, así que <strong>de</strong>finimos una nuevaclase que represente la medida <strong>de</strong> la velocidad <strong>de</strong>l viento, tal y como muestra elListado 9.4.1 package s e n s o r e s ;23 public c l a s s V e l o c i d a d V i e n t o {4 private f l o a t i n t e n s i d a d ;5 private D i r e c c i o n d i r e c c i o n ;67 public c l a s s D i r e c c i o n {8 f l o a t vx ;9 f l o a t vy ;1011 public D i r e c c i o n ( f l o a t vx , f l o a t vy ) {12 t h i s . vx = vx ;13 t h i s . vy = vy ;14 }1516 public S t r i n g t o S t r i n g ( ) {17 return " [ " + vx + " , " + vy + " ] " ;18 }19 }2021 public V e l o c i d a d V i e n t o ( ) {22 super ( ) ;23 }2425 public V e l o c i d a d V i e n t o ( f l o a t i n t e n s i d a d , f l o a t vx , f l o a t vy ) {26 t h i s . i n t e n s i d a d = i n t e n s i d a d ;27 d i r e c c i o n = new D i r e c c i o n ( vx , vy ) ;28 }2930 public double g e t I n t e n s i d a d ( ) {31 return i n t e n s i d a d ;32 }3334 public D i r e c c i o n g e t D i r e c c i o n ( ) {


9.3. CLASES GENÉRICAS 13735 return d i r e c c i o n ;36 }3738 @Overri<strong>de</strong>39 public S t r i n g t o S t r i n g ( ) {40 return " I n t e n s i d a d : " + i n t e n s i d a d + " D i r e c c i ó n : " + d i r e c c i o n ;41 }42 }Listado 9.4: La clase VelocidadVieto <strong>de</strong>fine una clase que almacena laintensidad y la dirección <strong>de</strong> una medida <strong>de</strong>l viento.Con esta nueva clase, la <strong>de</strong>finición <strong>de</strong> un sensor <strong>de</strong> velocidad <strong>de</strong>l viento es lamostrada en el Listado 9.51 package s e n s o r e s ;23 import j a v a . u t i l . Random ;45 import s e n s o r e s . V e l o c i d a d V i e n t o . D i r e c c i o n ;67 public c l a s s S e n s o r V e l o c i d a d V i e n t o extends Sensor {8 private s t a t i c f i n a l f l o a t VELOCIDAD MAXIMA = 1 0 0 ;9 private Random v i e n t o = new Random ( ) ;1011 public S e n s o r V e l o c i d a d V i e n t o ( ) {12 super ( ) ;13 setMedida ( ) ;14 s e t D e s c r i p c i o n ( ) ;15 }1617 public f i n a l void s e t D e s c r i p c i o n ( ) {18 super . s e t D e s c r i p c i o n ( " Mi<strong>de</strong> la v e l o c i d a d y d i r e c c i ó n <strong>de</strong>l v i e n t o . " ) ;1920 }2122 private f i n a l void setMedida ( ) {23 f l o a t angulo = ( f l o a t ) ( v i e n t o . n e x t F l o a t ( ) ∗ Math . PI ) ;24 f l o a t seno = ( f l o a t ) ( Math . s i n ( angulo ) ) ;25 f l o a t coseno = ( f l o a t ) ( Math . c o s ( angulo ) ) ;26 medida = new V e l o c i d a d V i e n t o (VELOCIDAD MAXIMA∗ v i e n t o . n e x t F l o a t ( ) , seno, coseno ) ;27 }28 }Listado 9.5: La clase SensorVelocidadVieto <strong>de</strong>fine un sensor capaz <strong>de</strong> registrarla velocidad <strong>de</strong>l viento.En ambos casos <strong>de</strong> sensores, estamos generando <strong>de</strong> modo aleatorio el valor <strong>de</strong>ldato leído, y este no se modifica durante la ejecución <strong>de</strong> la aplicación. Cuandoveamos en el Capítulo 14 cómo trabajar <strong>con</strong> Hilos en <strong>Java</strong>, reescribiremos elcódigo <strong>de</strong> esta aplicación para que sea más realista. Finalmente, un ejemplo <strong>de</strong>luso <strong>de</strong> estas clase se muestra en el Listado 9.6.1 package p r i n c i p a l ;23 import s e n s o r e s . SensorTemperatura ;4 import s e n s o r e s . S e n s o r V e l o c i d a d V i e n t o ;56 public f i n a l c l a s s P r i n c i p a l {7 private P r i n c i p a l ( ) {8 super ( ) ;9 }1011 private void e j e c u t a ( ) {12 SensorTemperatura sensorT = new SensorTemperatura ( ) ;13 System . out . p r i n t l n ( " T e m p e r a t u r a : " + sensorT . getMedicion ( ) ) ;


138CAPÍTULO 9. PROGRAMACIÓN CON GENÉRICOS14 S e n s o r V e l o c i d a d V i e n t o sensorV = new S e n s o r V e l o c i d a d V i e n t o ( ) ;15 System . out . p r i n t l n ( " V i e n t o : " + sensorV . getMedicion ( ) ) ;16 }1718 public s t a t i c void main ( S t r i n g [ ] a r g s ) {19 P r i n c i p a l p r i n c i p a l = new P r i n c i p a l ( ) ;20 p r i n c i p a l . e j e c u t a ( ) ;21 }22 }Listado 9.6: Este listado muestra cómo usar los sensores los tipos <strong>de</strong> sensoresanteriormente <strong>de</strong>finidos.Un ejemplo <strong>de</strong>l resultado <strong>de</strong> la ejecución <strong>de</strong>l código <strong>de</strong>l Listado 9.6 es:Temperatura: 1.4107025Viento: Intensidad: 40.831844 Dirección: [0.7000265, -0.7141168]Siguiendo el mismo esquema, basta <strong>de</strong>finir el tipo <strong>de</strong> dato que mi<strong>de</strong> el sensor,po<strong>de</strong>mos crear cualquier tipo <strong>de</strong> sensor extendiendo a la clase Sensor, yautomáticamente será capaz <strong>de</strong> <strong>de</strong>volvernos el dato que mi<strong>de</strong>.9.4. Ampliación <strong>de</strong>l tipo genéricoPara seguir profundizando en las posibilida<strong>de</strong>s <strong>de</strong> los tipos genéricos, retomemosel ejemplo <strong>de</strong> la Sección 8.4 <strong>de</strong> las figuras: círculos, triángulos rectángulosy rectángulos <strong>de</strong> las que po<strong>de</strong>mos calcular su área. Sabemos que la claseArrayList es un <strong>con</strong>tenedor que trabaja <strong>con</strong> genéricos, luego po<strong>de</strong>moscrear un ArrayList para almacenar en él cualquier tipo <strong>de</strong> figura quese pueda dibujar, tal y como muestra el Listado 9.7.1 // Una l i s t a <strong>de</strong> f i g u r a s .2 ArrayList f i g u r a s = new ArrayList () ;3 // C í r c u l o s , t r i á n g u l o s r e c t á n g u l o s y r e c t á n g u l o s son f i g u r a s .4 f i g u r a s . add (new C i r c u l o ( ) ) ;5 f i g u r a s . add (new T r i a n g u l o R e c t a n g u l o ( ) ) ;6 f i g u r a s . add (new Rectangulo ( ) ) ;Listado 9.7: Una lista <strong>de</strong> figuras.Ahora creamos una lista sólo para almacenar círculos, y una vez creada, yaque la clase Circulo implementa el interface Figura, intentamos asignar ala lista <strong>de</strong> figuras figuras la lista que sólo almacena círculos circulos, tal ycomo muestra el Listado 9.8.7 // Ahora un A r r a y L i s t s ó l o <strong>de</strong> c í r c u l o s8 ArrayList c i r c u l o s = new ArrayList () ;9 c i r c u l o s . add (new C i r c u l o ( 3 ) ) ;10 c i r c u l o s . add (new C i r c u l o ( 5 ) ) ;11 // f i g u r a s = c i r c u l o s ; // ERROR ! ! !Listado 9.8: Una lista <strong>de</strong> círculos.Para nuestra sorpresa, en la línea <strong>de</strong> código 11 hay un error, no po<strong>de</strong>mosasignar a un ArrayList un ArrayList, aunque la claseCirculo implemente el interface Figura, la <strong>con</strong>versión <strong>de</strong> tipos no es posible.Si pudiésemos hacer la asignación, podríamos añadir a la lista <strong>de</strong> círculoscualquier otra figura a través <strong>de</strong> la referencia figuras.


9.4.AMPLIACIÓN DEL TIPO GENÉRICO 139Si intentamos escribir un método capaz <strong>de</strong> mostrar el área <strong>de</strong> todas las figuras<strong>de</strong> una lista tal y como muestra el Listado 9.9, no tendremos ningún error cuandomostremos las áreas <strong>de</strong> las figuras <strong>de</strong> la referencia figuras, pero obtendremosel mismo error que en el Listado 9.8 si lo intentamos <strong>con</strong> la referencia circulos.El motivo es el mismo que antes, el tipo ArrayList es diferente al tipoArrayList.20 private void muestraAreas ( ArrayList l i s t a ) {21 for ( Figura elemento : l i s t a ) {22 System . out . p r i n t l n ( elemento . g e t C l a s s ( ) . getCanonicalName ( ) + " : " +elemento . getArea ( ) ) ;23 }24 }Listado 9.9: Un método que recibe una lista <strong>de</strong> elementos <strong>de</strong> tipo interfaceFigura.¿Cómo po<strong>de</strong>mos resolver esta encrucijada? ¿Cómo po<strong>de</strong>mos indicar que queremosuna lista <strong>de</strong> cualquier cosa que implemente la Figura para po<strong>de</strong>r asignara una lista <strong>de</strong> figuras unas veces listas <strong>de</strong> sólo círculos y otras veces listas <strong>de</strong>sólo rectángulos?<strong>Java</strong> nos da una solución para este caso, los tipos genéricos <strong>con</strong> límite superior.9.4.1. Tipos genéricos <strong>con</strong> límite superiorEl Listado 9.10 muestra un método en el que se da un límite superior al tipo<strong>de</strong> datos genérico. indica que los elementos que se pue<strong>de</strong>nalmacenar en la colección pue<strong>de</strong>n ser <strong>de</strong> cualquier subtipo <strong>de</strong> Figura. Fíjateque aunque Figura es una interface se utiliza la palabra reservada extendsy no implements. De este modo, po<strong>de</strong>mos llamar a este método <strong>con</strong> cualquierArrayList siempre que sus elementos implementen la interface Figura.22 private void muestraAreas2 ( ArrayList l i s t a ) {23 for ( Figura elemento : l i s t a ) {24 System . out . p r i n t l n ( elemento . g e t C l a s s ( ) . getCanonicalName ( ) + " : " +elemento . getArea ( ) ) ;25 }26 }Listado 9.10: Un método que recibe una lista genérica <strong>de</strong> elementos queimplementan la interface Figura.De este modo po<strong>de</strong>mos restringir los tipos <strong>con</strong>cretos <strong>de</strong> las clases genéricas.En el Listado 9.10 estamos restringiendo el tipo <strong>con</strong>creto <strong>de</strong> la clase genérica aalgún tipo que extienda, o implemente, el tipo Figura.9.4.2. ComodinesLa línea 13 <strong>de</strong>l Listado 9.11 presenta una nueva <strong>con</strong>strucción <strong>de</strong>l lenguaje:ArrayList


140CAPÍTULO 9. PROGRAMACIÓN CON GENÉRICOS(o extien<strong>de</strong>n) el interface Figura. De nuevo, fíjate que aunque, como en estecaso, las clases finales implementan un interfaz, el ArrayList utiliza la palabrareservada extends, dicho <strong>de</strong> otro modo se utiliza siempre extends <strong>con</strong> elsignificado <strong>de</strong> subtipo sea este por extensión (extends) o por implementación(implements) <strong>de</strong> una interface.12 // Una l i s t a <strong>con</strong> l í m i t e s u p e r i o r13 ArrayList


9.5. BORRADO DE TIPO Y COMPATIBILIDAD CON CÓDIGO HEREDADO1419.5. Borrado <strong>de</strong> tipo y compatibilidad <strong>con</strong> códigoheredadoComo has podido ver, la programación <strong>con</strong> genéricos es muy potente, <strong>de</strong> hecho,todas las clases <strong>de</strong>l <strong>Java</strong> Collection Framework usan tipos genéricos. Sin embargo,existe un problema <strong>de</strong> incompatibilidad <strong>con</strong> código heredado anterior a laversión 5 <strong>de</strong> <strong>Java</strong> en las que no existen tipos genéricos. ¿Cómo se resuelve? <strong>de</strong>uno modo transparente para el programador, el compilador <strong>de</strong> <strong>Java</strong> sustituyelos tipos genéricos por verda<strong>de</strong>ros tipos cuando se <strong>de</strong>terminan estos en tiempo<strong>de</strong> compilación, es <strong>de</strong>cir, si el método <strong>de</strong>l Listado 9.10 en el código se llama unavez <strong>con</strong> un ArrayList y otra vez <strong>con</strong> un ArrayList,se crean dos versiones <strong>de</strong> este método <strong>con</strong> cada uno <strong>de</strong> estos dos tipos <strong>con</strong>cretos.A esta técnica se la llama Borrado <strong>de</strong> tipo.Ejercicios.1. Crea otros tipos <strong>de</strong> sensores, por ejemplo un sensor <strong>de</strong> presión que mida lapresión atmosférica, y un sensor <strong>de</strong> color que mida colores. En este últimocaso, elige el espacio <strong>de</strong> colores RGB.2. Crea una clase genérica Garage que permita almacenar en ellas cualquiertipo <strong>de</strong> vehículos Coches, Furgonetas y Motos por ejemplo, es una ubicaciónespecificada por un número real (como la plaza <strong>de</strong> garage) o el DNI<strong>de</strong>l usuario especificado como un String.Lecturas recomendadas.En esta dirección http://java.sun.com/docs/books/tutorial/java/generics/in<strong>de</strong>x.html pue<strong>de</strong>s en<strong>con</strong>trar la referencia básica <strong>de</strong> Sun sobreel uso <strong>de</strong> genéricos en <strong>Java</strong>.En esta dirección http://java.sun.com/docs/books/tutorial/extra/generics/in<strong>de</strong>x.html pue<strong>de</strong>s en<strong>con</strong>trar otra interesante y <strong>de</strong>tallada referencia,<strong>de</strong> Gilad Bracha, a la programación <strong>con</strong> genéricos en <strong>Java</strong>.


142CAPÍTULO 9. PROGRAMACIÓN CON GENÉRICOS


Capítulo 10Construcción <strong>de</strong> <strong>proyectos</strong><strong>con</strong> AntContenidos10.1. Qué es Ant . . . . . . . . . . . . . . . . . . . . . . . 14410.2. Definición <strong>de</strong>l proyecto . . . . . . . . . . . . . . . . 14410.2.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . 14510.2.2. Tareas . . . . . . . . . . . . . . . . . . . . . . . . . . 14510.3. Compilar el código fuente <strong>de</strong> un proyecto . . . . . 14610.4. Propieda<strong>de</strong>s . . . . . . . . . . . . . . . . . . . . . . . 14610.5. Estructuras path-like . . . . . . . . . . . . . . . . . 14710.6. Ejecución <strong>de</strong> las Pruebas Unitarias . . . . . . . . . 14810.7. Generación <strong>de</strong> la documentación . . . . . . . . . . 15010.8. Empaquetado <strong>de</strong> la aplicación . . . . . . . . . . . . 15110.9. Ejecución y limpieza . . . . . . . . . . . . . . . . . . 151IntroducciónHasta ahora, hemos utilizado un entorno <strong>de</strong> <strong>de</strong>sarrollo integrado, como Eclipse,para realizar todas las tareas relativas a nuestro proyecto. Sabemos cómo compilarnuestro código, cómo generar la documentación <strong>con</strong> javadoc, cómo ejecutarnuestro código, cómo empaquetar nuestro código y cómo realizar pruebas sobrenuestro código <strong>con</strong> JUnit.Todas estas tareas han necesitado nuestra intervención, para ejecutar el código<strong>de</strong> nuestra aplicación hemos tenido que pulsar el botón correspondiente enEclipse, y <strong>de</strong>l mismo modo, hemos tenido que actuar sobre el IDE para ejecutarlas pruebas unitarias.Eclipse ha sido el IDE que hemos elegido, pero como ya hemos comentado,existen otros excelentes entornos <strong>de</strong> <strong>de</strong>sarrollo, como por ejemplo NetBeans 1 .La elección entre unos y otros acaba siendo cuestión <strong>de</strong> gustos y adaptación más1 Neatbeans se pue<strong>de</strong> <strong>de</strong>scargar para su instalación <strong>de</strong>s<strong>de</strong> http://netbeans.org/143


144CAPÍTULO 10. CONSTRUCCIÓN DE PROYECTOS CON ANTque <strong>de</strong> la funcionalidad que los entornos proporcionan, ya que todos ellos proporcionanuna funcionalidad parecida, al menos, suficiente para lo que nosotrosnecesitamos en este momento.En cada uno <strong>de</strong> estos entorno <strong>de</strong> <strong>de</strong>sarrollo, el modo <strong>de</strong> ejecutar una aplicación,o el modo <strong>de</strong> lanzar la herramienta <strong>de</strong> documentación cambia. Incluso elmodo en que el entorno <strong>de</strong> <strong>de</strong>sarrollo organiza las carpetas <strong>de</strong>l proyecto pue<strong>de</strong>cambiar, un entorno pue<strong>de</strong> usar el nombre src y otro source. Estos pequeñoscambios hacen necesaria la intervención <strong>de</strong>l <strong>de</strong>sarrollador para migrar <strong>proyectos</strong><strong>de</strong> un entorno a otro.Sería interesante que, <strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong>l entorno <strong>de</strong> <strong>de</strong>sarrollo, o inclusosi no utilizásemos ningún entorno <strong>de</strong> <strong>de</strong>sarrollo, fuese posible realizar las tareascomunes sobre un proyecto <strong>Java</strong> <strong>de</strong> modo estándar.La herramienta <strong>de</strong> <strong>con</strong>strucción <strong>de</strong> <strong>proyectos</strong> Ant nos proporciona precisamenteeso, un modo <strong>de</strong> trabajar <strong>con</strong> nuestros <strong>proyectos</strong> <strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia<strong>de</strong>l entorno <strong>de</strong> <strong>de</strong>sarrollo elegido, o incluso po<strong>de</strong>r trabajar directamente sobrenuestro proyecto <strong>de</strong>s<strong>de</strong> <strong>con</strong>sola.10.1. Qué es AntAnt es una herramienta <strong>de</strong> <strong>con</strong>strucción <strong>de</strong> <strong>proyectos</strong>. Pero su utilidad no se<strong>de</strong>tiene ahí, <strong>con</strong> Ant po<strong>de</strong>mos hacer mucho más que simplemente compilar elcódigo <strong>de</strong> nuestro proyecto, po<strong>de</strong>mos realizar, <strong>de</strong> modo automático, otras muchastareas como la ejecución <strong>de</strong> prueba sobre nuestro código, la generación <strong>de</strong>informes a partir <strong>de</strong> los resultados <strong>de</strong> las pruebas, la generación <strong>de</strong> la documentación<strong>de</strong> nuestro proyecto y un largo, largísimo etcétera. Ant es extensible, <strong>de</strong>modo que incluso po<strong>de</strong>mos <strong>de</strong>finir nuestras propias tareas, ya que Ant está escritoen <strong>Java</strong>.Ant es un proyecto <strong>de</strong> la Fundación Apache que pue<strong>de</strong>s en<strong>con</strong>trar en estadirección http://ant.apache.org, don<strong>de</strong> también se encuentran los sencillos<strong>de</strong>talles <strong>de</strong> instalación, basta <strong>con</strong> <strong>de</strong>scomprimir el fichero que se pue<strong>de</strong> <strong>de</strong>scargar<strong>de</strong>s<strong>de</strong> la página web <strong>de</strong> la Fundación Apache y establecer las correspondientesvariables <strong>de</strong> entorno.10.2. Definición <strong>de</strong>l proyectoLa <strong>de</strong>finición <strong>de</strong> un proyecto Ant siempre se hace en un fichero llamadobuild.xml. Como ejemplo <strong>de</strong> uso <strong>de</strong> Ant, vamos a utilizar el proyecto <strong>de</strong> <strong>con</strong>versión<strong>de</strong> temperaturas presentado en el Capítulo 5 y las pruebas unitariassobre este proyecto presentadas en el Capítulo 6.Dentro <strong>de</strong>l proyecto <strong>de</strong> <strong>con</strong>versión <strong>de</strong> temperaturas crea un fichero y llámalobuild.xml, el fichero <strong>de</strong> <strong>de</strong>scripción <strong>de</strong>l proyecto es un fichero xml. Si esteestándar tecnológico no te es familiar, interesa que antes <strong>de</strong> seguir a<strong>de</strong>lante <strong>con</strong>ozcaesta tecnología. En esta dirección http://www.w3.org/standards/xml/core en<strong>con</strong>trarás una introducción a esta tecnología proporcionada por el WorldWi<strong>de</strong> Web Consortium, el <strong>con</strong>sorcio que se encarga <strong>de</strong> los estándares Web 22 Este organismo es el encargado <strong>de</strong>l proceso <strong>de</strong> estandarización <strong>de</strong> las tecnologías Web, supágina web está repleta <strong>de</strong> información <strong>con</strong> respecto a estos estándares tecnológicos. Al igualque la página web <strong>de</strong> Sun sobre <strong>Java</strong> es la referencia básica en la web sobre el lenguaje <strong>de</strong>programación <strong>Java</strong>, la página web <strong>de</strong>l W3C es la referencia básica para los estándares web.


10.2.DEFINICIÓN DEL PROYECTO 145La etiqueta raíz bajo la cual se <strong>de</strong>fine todo el proyecto es que tieneun atributo obligatorio name <strong>con</strong> el que especificamos el nombre <strong>de</strong>l proyecto.El Listado 10.1 muestra la <strong>de</strong>finición básica <strong>de</strong> un proyecto.1 2 . . .3 Listado 10.1: Definición <strong>de</strong> un proyecto Ant.Si la sintaxis xml no te es familiar, fíjate que la etiqueta <strong>de</strong> apertura está cerrada <strong>con</strong> la etiqueta 3 .Un proyecto Ant está formado, por uno o más objetivos, y cada uno <strong>de</strong> estosobjetivos pue<strong>de</strong> estar formado por una o más tareas. Cada tarea se realizarán enel or<strong>de</strong>n en el que fue especificada <strong>de</strong>ntro <strong>de</strong>l objetivo. Veamos cómo se <strong>de</strong>fineun objetivo en Ant.10.2.1. ObjetivosPara <strong>de</strong>finir un objetivo utilizamos la etiqueta que <strong>de</strong> nuevo tiene unatributo obligatorio, el nombre <strong>de</strong>l objetivo. De nuevo, cada objetivo <strong>de</strong>be ircerrado <strong>con</strong> su correspondiente etiqueta <strong>de</strong> cierre tal y como muestra el Listado10.2.1 2 3 . . .4 5 Listado 10.2: Definición <strong>de</strong> un objetivo Ant.Cada uno <strong>de</strong> los objetivos <strong>con</strong>tiene una o más tareas que se ejecutarán enel or<strong>de</strong>n en que fueron <strong>de</strong>finidas cuando ejecutemos el correspondiente objetivo.Veamos cómo se especifican las tareas pertenecientes a un objetivo.10.2.2. TareasEn Ant existe una enorme cantidad <strong>de</strong> tareas pre<strong>de</strong>finidas, por ejemplo, existeuna tarea para compilar el código fuente <strong>de</strong> nuestra aplicación, y otra tareapara crear un fichero jar a partir <strong>de</strong>l código compilado <strong>de</strong> nuestra aplicación. Alo largo <strong>de</strong> este capítulo iremos <strong>de</strong>scribiendo las tareas más utilizadas. En estemomento, y como ejemplo, vamos a utilizar una tarea muy sencilla cuya funciónes simplemente mostrar un mensaje <strong>de</strong> texto en <strong>con</strong>sola, tal y como muestra elListado 10.3.1 2 3 Empezando <strong>con</strong> Ant .4 5 Listado 10.3: La tarea muestra un mensaje <strong>de</strong> texto por <strong>con</strong>sola.3 En un fichero xml bien formado toda etiqueta abierta <strong>de</strong>be estar cerrada <strong>con</strong> su correspondienteetiqueta.


146CAPÍTULO 10. CONSTRUCCIÓN DE PROYECTOS CON ANTEjecutar un objetivo <strong>de</strong>s<strong>de</strong> línea <strong>de</strong> instrucciones es muy sencillo, basta situarseen el directorio don<strong>de</strong> se encuentre el fichero build.xml y teclear antempezando, lo que invocará la ejecución <strong>de</strong>l objetivo empezando <strong>de</strong>finido en elfichero build.xml. El resultado <strong>de</strong> la ejecución será parecido a:Rayuela:ant oscar$ ant empezandoBuildfile: build.xmlempezando:[echo] Empezando <strong>con</strong> Ant.BUILD SUCCESSFULTotal time: 0 se<strong>con</strong>dsPasemos a ver cómo utilizar algunas otras tareas más útiles que .10.3. Compilar el código fuente <strong>de</strong> un proyectoLa etiqueta nos permite compilar código <strong>Java</strong>. Para po<strong>de</strong>r usar estaetiqueta <strong>de</strong>bemos indicar el directorio don<strong>de</strong> está el código fuente mediante elatributo srcdir, e indicar don<strong>de</strong> se escribirán los ficheros <strong>de</strong> clases compiladosmediante el atributo <strong>de</strong>stdir. El Listado 10.4 muestra el uso <strong>de</strong> la etiqueta.1 2 3 4 6 7 Listado 10.4: Compilar un proyecto usando Ant.Fíjate que previamente a la tarea <strong>de</strong> compilación <strong>de</strong> la línea 3, hemos utilizadola tarea mkdir para crear el directorio <strong>de</strong> <strong>de</strong>stino <strong>de</strong> las clases compiladas.Ahora ya pue<strong>de</strong>s compilar el código <strong>de</strong> tu proyecto invocando al objetivocompile bien <strong>de</strong>s<strong>de</strong> <strong>con</strong>sola, bien <strong>de</strong>s<strong>de</strong> la vista Ant <strong>de</strong> Eclipse.10.4. Propieda<strong>de</strong>sEl objetivo compile tal y como lo hemos <strong>de</strong>scrito tiene un in<strong>con</strong>veniente, y esque si <strong>de</strong>cidimos cambiar el fichero <strong>de</strong> <strong>de</strong>stino <strong>de</strong> los ficheros compilados porejemplo <strong>de</strong>s<strong>de</strong> el original ../excpeciones/build/classes, a otro directoriocomo por ejemplo ../excepciones/build/project/classes, tendremos quecambiar todas la ocurrencias <strong>de</strong>l <strong>de</strong>stino original. Para solucionar esta situación,po<strong>de</strong>mos utilizar las propieda<strong>de</strong>s que nos permiten asociar a un nombre un valor,y hacer referencia al valor a través <strong>de</strong> su nombre en cualquier lugar <strong>de</strong>l ficherobuild.xml, tal y como muestra el Listado 10.5.1 2


10.5. ESTRUCTURAS PATH-LIKE 1473 4 5 6 7 89 10 11 13 14 Listado 10.5: Uso <strong>de</strong> las propieda<strong>de</strong>s.En este caso, cada una <strong>de</strong> las propieda<strong>de</strong>s hace referencia a una direcciónrepresentada por el atributo location.10.5. Estructuras path-likeCon las propieda<strong>de</strong>s po<strong>de</strong>mos <strong>de</strong>finir valores a los que po<strong>de</strong>r hacer referencia porsu nombre. Las estructuras path-like son aún más potentes, y nos permiten<strong>de</strong>finir grupos <strong>de</strong> directorios o ficheros. Por ejemplo, en nuestro proyecto <strong>de</strong> laaplicación <strong>de</strong> <strong>con</strong>versión <strong>de</strong> temperaturas, tenemos programadas una serie <strong>de</strong>clases <strong>de</strong> pruebas unitarias que necesitamos compilar antes <strong>de</strong> ejecutar. Paracompilar las clases <strong>de</strong> pruebas unitarias necesitaremos la biblioteca junit.jar,a<strong>de</strong>más <strong>de</strong>l directorio don<strong>de</strong> se encuentran las clases <strong>de</strong> prueba que queremoscompilar.Para <strong>de</strong>finir grupos <strong>de</strong> directorios y ficheros Ant nos proporciona la etiqueta. El Listado 10.6 muestra el uso <strong>de</strong> esta etiqueta <strong>con</strong> el objetivo <strong>de</strong>compilar las clases <strong>de</strong> pruebas unitarias.1 2 3 4 5 6 7 8 9 10 11 12 13 1415 16 < f i l e s e t d i r=" ${ lib } " i n c l u d e s=" *. jar " />17 18 1920 21 22 24 2526 29 30


148CAPÍTULO 10. CONSTRUCCIÓN DE PROYECTOS CON ANT32 33 34 35 Listado 10.6: Uso <strong>de</strong> estructuras path-like para <strong>de</strong>finir la ruta a las bibliotecas<strong>de</strong>l proyecto.En las líneas 9, 11 y 13 <strong>de</strong>l Listado 10.6 estamos <strong>de</strong>finiendo las propieda<strong>de</strong>sque hacen referencia al directorio <strong>con</strong> el código fuente <strong>de</strong> las clases <strong>de</strong> prueba,al directorio <strong>de</strong>stino <strong>de</strong> las clases compiladas <strong>de</strong> prueba y al directorio don<strong>de</strong>están todas las bibliotecas <strong>de</strong>l proyecto respectivamente.Por su lado, entre las líneas 15-18, mediante una estructura path-like, estamos<strong>de</strong>finiendo don<strong>de</strong> están las bibliotecas necesarias para compilar las clases<strong>de</strong> prueba (junit.jar y harmcret.jar) y don<strong>de</strong> están las clases compiladas <strong>de</strong>lproyecto.Finalmente, entre las líneas 26-34 estamos <strong>de</strong>finiendo un objetivo para compilarlas clases <strong>de</strong> prueba.Fíjate en la línea 27, en esa línea estamos indicando que el objetivocompile-test <strong>de</strong>pen<strong>de</strong> <strong>de</strong> la tarea compile. Evi<strong>de</strong>ntemente, para po<strong>de</strong>r compilarlas clases <strong>de</strong> prueba, las clases a probar <strong>de</strong>ben estar previamente compiladas,mediante el atributo <strong>de</strong>pends <strong>de</strong> la etiqueta target se fuerza a cubrir los objetivosespecificados en antes <strong>de</strong> cubrir el objetivo actual.10.6. Ejecución <strong>de</strong> las Pruebas UnitariasEl Listado 10.7 muestra cómo ejecutar una batería <strong>de</strong> pruebas y grabar el resultadoa un fichero como un informe <strong>con</strong> formato <strong>de</strong> texto.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1819 20 21 < f i l e s e t d i r=" ${ lib } " i n c l u d e s=" *. jar " />22 23 2425 26 27 28 29 3031


10.6.EJECUCIÓN DE LAS PRUEBAS UNITARIAS 14932 33 35 3637 40 41 43 44 45 4647 50 51 52 55 56 57 59 60 61 Listado 10.7: Ejecutar la batería <strong>de</strong> pruebas unitarias.En el Listado 10.7 se ha añadido el atributo <strong>de</strong>fault="test" al proyectopara indicar que el objetivo test es el objetivo por <strong>de</strong>fecto, si no se seleccionaningún otro, este es el objetivo que se invoca al ejecutar Ant. También se hanañadido las propieda<strong>de</strong>s y estructuras path-like necesarias para la ejecución <strong>de</strong>las pruebas. Si únicamente queremos ejecutar algunas <strong>de</strong> las pruebas y no todala suite utilizaríamos la variante mostrada en el Listado 10.8, don<strong>de</strong> se muestransólo las líneas añadidas al fichero build.xml.1 2 34 . . .56 9 10 11 14 15 16 17 < f i l e s e t d i r=" ${ test . c l a s s e s . dir } ">18 19 20 21 22 Listado 10.8: Ejecutar sólo algunas <strong>de</strong> las pruebas.Fíjate, que en este último caso, a<strong>de</strong>más estamos indicando que el formato<strong>de</strong> los informes sea xml, esta posibilidad es interesante ya que a partir <strong>de</strong> estos


150CAPÍTULO 10. CONSTRUCCIÓN DE PROYECTOS CON ANTinformes podremos hacer sobre ellos una transformación para generarlos en formatohtml tal y como muestra el Listado 10.9, don<strong>de</strong>, <strong>de</strong> nuevo, sólo aparecenlas líneas añadidas al fichero build.xml.1 2 34 . . .56 9 10 < f i l e s e t d i r=" ${ r e p o r t s . xml . dir } ">11 12 13 15 16 < f a i l i f =" test . f a i l u r e s "17 message=" Se han p r o d u c i d o e r r o r e s en los t e s t s . " />18 Listado 10.9: Generar informes <strong>de</strong> las pruebas en formato xml.10.7. Generación <strong>de</strong> la documentaciónOtra tarea que se pue<strong>de</strong> automatizar es la generación <strong>de</strong> la documentación<strong>de</strong> nuestro proyecto tal y como muestra el Listado 10.10. La tarea tiene algunos atributos interesantes, como por ejemplo access que nos permiteindicar los métodos y atributos <strong>de</strong> los que se generará la documentación según sumodificador <strong>de</strong> acceso. En nuestro ejemplo, se generará documentación <strong>de</strong> todoslos métodos y atributos cuya visibilidad sea private o mayor. Otros atributosútiles son author, si su valor es true se añadirá información <strong>de</strong>l autor a ladocumentación, y version, si su valor es true se añadirá información <strong>de</strong> laversión.1 2 34 . . .56 9 15 16 17 18 19 20 Listado 10.10: Generación <strong>de</strong> la documentación <strong>de</strong>l proyecto.


10.8. EMPAQUETADO DE LA APLICACIÓN 15110.8. Empaquetado <strong>de</strong> la aplicaciónAnt También nos proporciona la tarea para empaquetar nuestra aplicación,tal y como muestra el Listado 10.11. En este caso, hemos empleado laetiqueta <strong>con</strong> el atributo para <strong>de</strong>finir el nombre <strong>de</strong>l ficheroempaquetado. La sintaxis <strong>de</strong> la tarea es bastante autoexplicativa, hay que indicarel nombre <strong>de</strong>l fichero empaquetado <strong>con</strong> el atributo <strong>de</strong>stfile <strong>de</strong> la etiquetajar. E indicar los ficheros que se van a incluir <strong>de</strong>ntro <strong>de</strong>l fichero empaquetadomediante una estructura path-like. Po<strong>de</strong>mos también indicar el <strong>con</strong>tenido <strong>de</strong>lfichero <strong>de</strong> manifiesto <strong>con</strong> la etiqueta .1 2 3 4 56 . . .78 11 12 13 < f i l e s e t d i r=" ${ b u i l d . c l a s s e s . dir } " />14 15 18 19 20 Listado 10.11: Empaquetado <strong>de</strong>l proyecto.10.9. Ejecución y limpiezaTambién po<strong>de</strong>mos ejecutar nuestra aplicación, como un fichero empaquetado,usando la tarea <strong>de</strong> Ant, tal y como se muestra en el Listado 10.12.1 4 7 Listado 10.12: Ejecución <strong>de</strong>l proyecto como un fichero empaquetado.Cabe <strong>de</strong>stacar que para ejecutar la aplicación se <strong>de</strong>be crear una nueva instancia<strong>de</strong> la máquina virtual, cosa que indicamos <strong>con</strong> el valor true <strong>de</strong>l atributofork <strong>de</strong> la tarea java.También po<strong>de</strong>mos borrar los directorios <strong>con</strong> las clases compiladas y los informespor si nos es <strong>de</strong> utilidad. El Listado 10.13 muestra el objetivo clean formadopor un <strong>con</strong>junto <strong>de</strong> tareas que borran los directorios <strong>de</strong>seados.1 2


152CAPÍTULO 10. CONSTRUCCIÓN DE PROYECTOS CON ANT3 4 5 Listado 10.13: Borrado <strong>de</strong> directorios.En el Apéndice A se muestra el listado completo <strong>de</strong>l fichero build.xml.Lecturas recomendadas.Sin duda la referencia básica sobre Ant se encuentra el la propia páginaweb <strong>de</strong>l proyecto, esta es la dirección directa http://ant.apache.org/manual/in<strong>de</strong>x.html.El capítulo 1 <strong>de</strong> la referencia [13] presenta la herramienta Ant <strong>de</strong> modo<strong>con</strong>ciso pero muy informativo.En español, es interesante el capítulo 3 <strong>de</strong> la referencia [5].


Capítulo 11Interfaces gráficas <strong>de</strong>usuarioContenidos11.1. APIs para la programación <strong>de</strong> interfaces gráficos<strong>de</strong> usuario en <strong>Java</strong>: AWT y Swing . . . . . . . . . 15411.2. Contenedores y Componentes . . . . . . . . . . . . 15511.3. Gestores <strong>de</strong> Aspecto (Layout Managers) . . . . . 15511.4. Detección <strong>de</strong> eventos: Escuchadores . . . . . . . . 15711.5. Algunos componentes Swing . . . . . . . . . . . . . 16211.5.1. JLabel, muestra texto o i<strong>con</strong>os . . . . . . . . . . . . 16211.5.2. JButton, botones que el usuario pue<strong>de</strong> pulsar . . . . 16211.5.3. JTextField, campos <strong>de</strong> introducción <strong>de</strong> texto . . . . 16311.5.4. JRadioButton, botones <strong>de</strong> opciones . . . . . . . . . . 16411.5.5. JCheckBox, botones <strong>de</strong> selección múltiple . . . . . . 16611.5.6. JList, listas <strong>de</strong> selección . . . . . . . . . . . . . . . . 16611.6. El patrón <strong>de</strong> diseño Mo<strong>de</strong>lo/Vista/Controlador . 168IntroducciónHasta ahora, la interacción <strong>con</strong> nuestras aplicaciones ha sido a través <strong>de</strong> <strong>con</strong>sola,los datos <strong>de</strong> entrada los tecleamos en <strong>con</strong>sola, y la respuesta la obteníamostambién directamente en <strong>con</strong>sola.Cada vez es menor el número <strong>de</strong> aplicaciones <strong>con</strong> interfaces <strong>de</strong> usuario basadosen <strong>con</strong>sola. Uno <strong>de</strong> los principales in<strong>con</strong>venientes <strong>de</strong> este tipo <strong>de</strong> interfaces<strong>de</strong> usuario es que son poco intuitivos y por lo tanto susceptibles <strong>de</strong> crear <strong>con</strong>fusiónen el usuario y como resultado que los datos <strong>de</strong> entrada al programa seanerróneos.Por <strong>con</strong>trapartida, las aplicaciones basadas en interfaces gráficos <strong>de</strong> usuarioson más intuitivas y la entrada <strong>de</strong> datos pue<strong>de</strong> estar acotada, evitando que elusuario introduzca valores erróneos.<strong>Java</strong> proporciona dos gran<strong>de</strong>s APIs para programar interfaces gráficas <strong>de</strong>153


154CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIOusuario: Abstract Windows Toolkit (AWT) y Swing. En este capítulo veremoscuales son las características <strong>de</strong> ambas y justificaremos por qué elegir una sobreotra.La programación <strong>de</strong> interfaces gráficos <strong>de</strong> usuario está basada en la i<strong>de</strong>a <strong>de</strong>que los componentes gráficos, tales como botones y cajas <strong>de</strong> edición <strong>de</strong> texto,son capaces <strong>de</strong> lazar eventos cuando el usuario interacciona sobre ellos. Porejemplo, cada vez que el usuario hace click sobre un botón, este lanza un eventocomo respuesta, y será tarea <strong>de</strong>l programador escribir el código necesario paraescuchar el tipo <strong>de</strong> eventos que le interese y actuar en <strong>con</strong>secuencia cuando seproduzca uno.Los interfaces gráficos <strong>de</strong> usuario son anteriores a la aparición <strong>de</strong>l lenguaje<strong>de</strong> programación <strong>Java</strong>, y en su programación se han <strong>de</strong>scubierto interesantespatrones <strong>de</strong> diseño, uno <strong>de</strong> ellos, ampliamente utilizado en otros lenguajes <strong>de</strong>programación como por ejemplo Smalltalk, es el patrón <strong>de</strong> diseño Mo<strong>de</strong>lo/Vista/Controlador.Este patrón <strong>de</strong> diseño agrupa las clases <strong>de</strong> una aplicación segúnsu responsabilidad, <strong>de</strong> modo que una clase sólo pue<strong>de</strong> formar parte bien <strong>de</strong>l Mo<strong>de</strong>lo,bien <strong>de</strong> la Vista o bien <strong>de</strong>l Controlador. Veremos <strong>con</strong> <strong>de</strong>talle este patrón<strong>de</strong> diseño y alguna técnica útil para su implementación.11.1. APIs para la programación <strong>de</strong> interfacesgráficos <strong>de</strong> usuario en <strong>Java</strong>: AWT y SwingEn <strong>Java</strong>, existen dos APIs para la programación <strong>de</strong> interfaces gráficos <strong>de</strong> usuarioAWT (Abstract Window Toolkit) y Swing. AWT fue la primera API disponibleen <strong>Java</strong> y sus principales características son:La creación <strong>de</strong> componentes gráficos se <strong>de</strong>lega al Sistema Operativo.El Sistema Operativo se encarga <strong>de</strong> dibujar los componentes gráficos y <strong>de</strong>la <strong>de</strong>tección <strong>de</strong> eventos.El aspecto <strong>de</strong> la aplicación es el nativo <strong>de</strong>l Sistema Operativo.La principal <strong>de</strong>sventaja <strong>de</strong> AWT es que <strong>de</strong>scansa directamente sobre el SistemaOperativo quien interviene tanto en la creación <strong>de</strong> componentes gráficoscomo en la <strong>de</strong>tección <strong>de</strong> eventos, <strong>de</strong> modo que la aplicación se pue<strong>de</strong> ralentizarsi la interfaz <strong>con</strong>tiene muchos elementos gráficos, y por otro lado no se pue<strong>de</strong>nintroducir cambios en el aspecto <strong>de</strong> los componentes.El API Swing viene a liberar la creación <strong>de</strong> interfaces gráficos <strong>de</strong> la cargaque supone la <strong>de</strong>pen<strong>de</strong>ncia <strong>con</strong> respecto al Sistema Operativo. Las principalescaracterísticas <strong>de</strong> este API son:Swing se encarga <strong>de</strong> dibujar los componentes y <strong>de</strong> <strong>de</strong>tectar la interacciónsobre ellos.El <strong>con</strong>junto <strong>de</strong> componentes es más gran<strong>de</strong> que el que proporciona elSistema Operativo.Se tiene <strong>con</strong>trol absoluto sobre el aspecto <strong>de</strong> los componentes.Por todo ellos, Swing ha ido <strong>de</strong>splazando a AWT en la creación <strong>de</strong> interfacesgráficos <strong>de</strong> usuario en <strong>Java</strong>.


11.2. CONTENEDORES Y COMPONENTES 155Figura 11.1: Colocación <strong>de</strong> Componentes <strong>con</strong> FlowLayout.11.2. Contenedores y ComponentesDentro <strong>de</strong> Swing tenemos dos gran<strong>de</strong>s grupos <strong>de</strong> elementos: los Contenedoresy los Componentes. La diferencia entre ellos es que los Contenedores pue<strong>de</strong>nalbergar otros Contenedores o Componentes <strong>de</strong>ntro <strong>de</strong> ellos, y los Componentesson los elementos gráficos <strong>con</strong> los que el usuario pue<strong>de</strong> interaccionar, como porejemplo botones, listas, etcétera.Los tres Contenedores que disponibles en Swing son JFrame que representauna ventana <strong>con</strong> marco, JWindow que representa una ventana sin marco, yJPanel que no tiene un aspecto visual, su cometido es albergar otros Contenedoreso Componentes <strong>de</strong>ntro <strong>de</strong> él.La i<strong>de</strong>a básica es que vamos a utilizar los JPanel como muñecas rusas, <strong>de</strong>modo que colocaremos Componentes <strong>de</strong>ntro <strong>de</strong> JPanel y esos JPanel <strong>de</strong>ntro <strong>de</strong>otros JPanel <strong>con</strong> más Componentes para ir creando el aspecto <strong>de</strong>seado para elinterfaz gráfico <strong>de</strong> nuestra aplicación.11.3. Gestores <strong>de</strong> Aspecto (Layout Managers)Cuando se programan interfaces gráficos <strong>de</strong> usuario, un aspecto importante esla colocación <strong>de</strong> los Componentes <strong>de</strong>ntro <strong>de</strong> la ventana <strong>de</strong> nuestra aplicación.<strong>Java</strong> nos facilita esta tarea mediante el uso <strong>de</strong> Gestores <strong>de</strong> Aspecto (LayoutManagers en inglés). Estos Gestores <strong>de</strong> Aspecto son los encargados <strong>de</strong> colocarlos Componentes que vamos añadiendo en los Contenedores. Cada uno <strong>de</strong> losGestores <strong>de</strong> Aspecto sigue una política <strong>de</strong> colocación <strong>de</strong> los componentes, así,por ejemplo, BoxLayout coloca cada nuevo componente al la izquierda <strong>de</strong>l últimocomponente añadido, como en el sentido <strong>de</strong> la escritura <strong>de</strong>l español, <strong>de</strong> tal modoque si no hay espacio suficiente para insertar un nuevo Componente en la líneaactual, porque se ha llegado al bor<strong>de</strong> <strong>de</strong> la ventan, el Componente se añadirá alprincipio <strong>de</strong> una nueva línea por <strong>de</strong>bajo <strong>de</strong> la actual. La Figura 11.1 muestraun ejemplo <strong>de</strong> este comportamiento.Este Gestor <strong>de</strong> Aspecto es el que por <strong>de</strong>fecto utiliza JPanel cada


156CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIOFigura 11.2: Colocación <strong>de</strong> Componentes <strong>con</strong> Bor<strong>de</strong>rLayout.vez que añadimos sobre él un Componente. JFrame posee otro Gestor <strong>de</strong>Aspecto por <strong>de</strong>fecto Bor<strong>de</strong>rLayout. Este Gestor <strong>de</strong> Aspecto <strong>de</strong>fine 5 zonasBor<strong>de</strong>rLayout.CENTER, Bor<strong>de</strong>rLayout.NORTH, Bor<strong>de</strong>rLayout.SOUTH,Bor<strong>de</strong>rLayout.EAST, Bor<strong>de</strong>rLayout.WEST, <strong>de</strong>ntro <strong>de</strong> las cuales sólo po<strong>de</strong>mosañadir un Componente o Contenedor <strong>con</strong> el método add(Componentcomponente, int zona), si no indicamos la zona (add(Componentcomponente), el nuevo componente será añadido a la región central. LaFigura 11.2 muestra una ventana en la que se observa la disposición <strong>de</strong> lascinco zonas <strong>de</strong> Bor<strong>de</strong>rLayout. El código fuente <strong>de</strong> este sencilla ejemplo apareceen el Listado 11.1.1 package g u i ;23 import j a v a . awt . Bor<strong>de</strong>rLayout ;4 import j a v a . awt . Container ;56 import javax . swing . JButton ;7 import javax . swing . JFrame ;8 import javax . swing . S w i n g U t i l i t i e s ;910 public f i n a l c l a s s EjemploBor<strong>de</strong>rLayout {11 private EjemploBor<strong>de</strong>rLayout ( ) {12 super ( ) ;13 }1415 private void creaGUI ( ) {16 JFrame ventana = new JFrame ( " B o r d e r L a y o u t M a n a g e r " ) ;17 Container c o n tenedor = ventana . getContentPane ( ) ;18 c o n t e nedor . add (new JButton ( " C e n t r o " ) ) ;19 c o n t e nedor . add (new JButton ( " N o r t e " ) , Bor<strong>de</strong>rLayout .NORTH) ;20 c o n t e nedor . add (new JButton ( " Sur " ) , Bor<strong>de</strong>rLayout .SOUTH) ;21 c o n t e nedor . add (new JButton ( " Este " ) , Bor<strong>de</strong>rLayout .EAST) ;22 c o n t e nedor . add (new JButton ( " O e s t e " ) , Bor<strong>de</strong>rLayout .WEST) ;23 ventana . s e t S i z e ( 4 0 0 , 400) ;24 ventana . s e t V i s i b l e ( true ) ;25 }2627 public s t a t i c void main ( S t r i n g [ ] a r g s ) {28 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {29 @Overri<strong>de</strong>30 public void run ( ) {


11.4.DETECCIÓN DE EVENTOS: ESCUCHADORES 15731 new EjemploBor<strong>de</strong>rLayout ( ) . creaGUI ( ) ;32 }33 }) ;34 }35 }Listado 11.1: Ejemplo <strong>de</strong> uso <strong>de</strong> Bor<strong>de</strong>rLayoutEl Listado 11.1 <strong>con</strong>tiene algunos <strong>de</strong>talles importante. En la línea 16, estamosalmacenando una referencia al <strong>con</strong>tenedor <strong>de</strong> la ventana principal, que esdon<strong>de</strong> añadimos los botones. Las líneas 17-21 aña<strong>de</strong>n un botón, instancia <strong>de</strong>JButton a cada uno <strong>de</strong> las cinco regiones que <strong>de</strong>fine un Bor<strong>de</strong>rLayout. En lalínea 22 estamos indicando el tamaño <strong>de</strong> la ventana, y finalmente, en la línea23 hacemos visible la ventana. En las línea 28-33 estamos utilizando un técnicaque quedará <strong>de</strong>finitivamente clara cuando veamos cómo programar hilos en <strong>Java</strong>en el Capítulo 14, en este momento basta <strong>de</strong>cir que en esas líneas <strong>de</strong> código seestá creando un hilo para aten<strong>de</strong>r al interfaz gráfico <strong>de</strong> usuario <strong>de</strong> modo que nointerfiere <strong>con</strong> el hilo <strong>de</strong> la aplicación principal.Los Gestores <strong>de</strong> Aspecto por <strong>de</strong>fecto se pue<strong>de</strong>n cambiar <strong>con</strong> el métodosetLayout(java.awt.LayoutManager) al que se le pasa una instancia <strong>de</strong>l nuevoGestor <strong>de</strong> Aspecto que queremos utilizar.Otros Gestores <strong>de</strong> Aspecto son GridLayout, que permite <strong>de</strong>finir una rejilla<strong>con</strong> filas y columnas, y po<strong>de</strong>mos añadir un componente <strong>de</strong>ntro <strong>de</strong> cada una<strong>de</strong> las posiciones <strong>de</strong>ntro <strong>de</strong> la rejilla; y BoxLayout nos permite disponer loscomponentes verticalmente, uno encima <strong>de</strong> otro, u horizontalmente, uno a laizquierda <strong>de</strong> otro.11.4. Detección <strong>de</strong> eventos: EscuchadoresSi has probado a ejecutar el código <strong>de</strong>l Listado 11.1 quizás te hayas dado cuenta<strong>de</strong>l siguiente <strong>de</strong>talle, al pulsar sobre el botón <strong>de</strong> cerrar la ventana que apareceen el marco <strong>de</strong> esta, la ventana se cierra pero la aplicación sigue ejecutándose.No existe ningún error en la aplicación, simplemente, al cerrar la ventana, lo queen realidad está ocurriendo es que se hace invisible, pero <strong>con</strong> ello no su fuerzaque acabe la ejecución <strong>de</strong> la aplicación.Para enten<strong>de</strong>r lo que está realmente ocurriendo tenemos que <strong>con</strong>ocer <strong>de</strong>qué modo actúan los Contenedores y Componentes en <strong>Java</strong> 1 .La i<strong>de</strong>a básica es que cuando el usuario interacciona <strong>con</strong> los Contenedoreso los Componentes estos lanzan eventos como respuesta, el programador <strong>de</strong>beescuchar estos eventos, y cuando los recibe actuar en <strong>con</strong>secuencia. Dicho <strong>de</strong>otro modo, el programador escribe código que observa cuando un evento ocurre,y le indica al componente que quiere ser informado cuando el evento ocurra. Porsu lado, el componente informa al observador <strong>de</strong> que el evento ha ocurrido cadavez que este se produce. De nuevo nos en<strong>con</strong>tramos <strong>con</strong> un patrón <strong>de</strong> diseñollamado Observador. La sección 16.9 presenta este patrón <strong>de</strong> diseño <strong>con</strong> <strong>de</strong>talle.La Figura 11.3 muestra gráficamente la dinámica en la <strong>de</strong>tección <strong>de</strong> eventos.De modo resumido, los pasos que <strong>de</strong>bemos dar para respon<strong>de</strong>r cuando unevento ocurra son:1 Este comportamiento es igualmente seguido en otros muchos lenguajes <strong>de</strong> programación


158CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIOFigura 11.3: Dinámica <strong>de</strong> la <strong>de</strong>tección <strong>de</strong> eventos en Swing.Figura 11.4: Dinámica <strong>de</strong> la <strong>de</strong>tección <strong>de</strong> eventos para el caso particular <strong>de</strong>WindowEvent.1. Conocer cual es el tipo <strong>de</strong> eventos genera el Contenedor o Componenteque nos interesa.2. Conocer cual es el interface capaz <strong>de</strong> escuchar a ese tipo <strong>de</strong> eventosy escribir una clase que lo implemente. A esta clase la llamaremos claseescuchadora.3. Añadir una instancia <strong>de</strong> la clase escuchadora a la lista <strong>de</strong> observadores <strong>de</strong>lContenedor o el Componente.Para ver <strong>de</strong> modo <strong>de</strong>tallado como los pasos anteriores se <strong>con</strong>cretan encódigo, supongamos que <strong>de</strong>seamos cerrar la aplicación cuando el usuario hagaclick sobre el botón <strong>de</strong> cierre <strong>de</strong> la ventana. El primer paso es <strong>con</strong>ocerqué tipo <strong>de</strong> eventos lanza JFrame, que es nuestra ventana, cuando elusuario hace click sobre el botón correspondiente; para ello po<strong>de</strong>mos <strong>con</strong>sultarla página http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/uiswing/events/eventsandcomponents.html, don<strong>de</strong> en<strong>con</strong>tramosque JFrame lanza eventos <strong>de</strong> tipo WindowEvent.El siguiente paso es <strong>con</strong>ocer cual es el interface capaz <strong>de</strong> escuchar este tipo<strong>de</strong> eventos. En la anterior dirección web en<strong>con</strong>tramos que es WindowListener 2 .Este interface <strong>de</strong>clara métodos cuyo significado aparece en la Tabla 11.1.La Figura 11.4 muestra la dinámica en la <strong>de</strong>tección <strong>de</strong> eventos generados porJFrame.La clase que implemente este interface <strong>de</strong>be <strong>de</strong>finir cada uno <strong>de</strong> los métodos<strong>de</strong> la Tabla 11.1. Si lo único que queremos hacer es cerrar la aplicacióncuando el usuario cierra la ventana, el único método al que añadiremos códigoes public void windowClosing(WindowEvent e).Finalmente añadiremos una instancia <strong>de</strong> la clase que implementa elinterface WindowListener como escuchador a la ventana, <strong>con</strong> el métodoaddWindowListener 3 .2 Observa la nomenclatura usada en el nombrado <strong>de</strong> eventos y sus correspondientes escuchadores:si en<strong>con</strong>tramos un evento <strong>de</strong> tipo xxxEvent, el interface capaz <strong>de</strong> escucharlo sellamará xxxListener3 Fíjate <strong>de</strong> nueva en la nomenclatura utilizada para nombrar el método que aña<strong>de</strong> el escuchadoral Contenedor, si el escuchador es xxxListener, el método que lo aña<strong>de</strong> al Contenedoro Componente es addxxxListener(interface xxxListener)


11.4.DETECCIÓN DE EVENTOS: ESCUCHADORES 159windowOpened(WindowEvent e)windowClosing(WindowEvent e)windowClosed(WindowEvent e)windowI<strong>con</strong>ified(WindowEvent e)windowDei<strong>con</strong>ified(WindowEvent e)windowActivated(WindowEvent e)windowDeactivated(WindowEvent e)Se invoca cuando la ventana seabre.Se invoca cuando se intenta cerrarla ventana.Se invoca cuando la ventana seha cerrado <strong>de</strong>finitivamente.Se invoca cuando la ventana seminimiza.Se invoca cuando la ventana pasa<strong>de</strong> estar minimizada a tener suestado normal.Se invoca cuando la ventana pasaa ser la ventana activa.Se invoca cuando la ventana <strong>de</strong>ja<strong>de</strong> ser la ventana activa.Tabla 11.1: Métodos <strong>de</strong>clarados en la interface WindowListener todos ellosson public void.El Listado 11.2 muestra un ejemplo completo <strong>de</strong> un escuchador para loseventos <strong>de</strong> la ventana que cierra la aplicación cuando el usuario pulsa el botón<strong>de</strong> cerrar ventana.1 package g u i ;23 import j a v a . awt . event . WindowEvent ;4 import j a v a . awt . event . WindowListener ;56 import javax . swing . JFrame ;7 import javax . swing . S w i n g U t i l i t i e s ;8 // Esta c l a s e implementa WindowListener l u e g o e s e s c u c h a d o r <strong>de</strong>WindowEvent9 public c l a s s EjemploWindowListener implements WindowListener {10 private EjemploWindowListener ( ) {11 super ( ) ;12 }1314 private void creaGUI ( ) {15 // Creamos l a ventana16 JFrame ventana = new JFrame ( " A p l i c a c i ó n que se c i e r r a <strong>con</strong> la v e n t a n . " );17 // Añadimos como escuchador l a i n s t a n c i a <strong>de</strong> e s t a c l a s e18 ventana . addWindowListener ( t h i s ) ;19 ventana . s e t S i z e ( 4 0 0 , 400) ;20 ventana . s e t V i s i b l e ( true ) ;21 }2223 public s t a t i c void main ( S t r i n g [ ] a r g s ) {24 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {25 @Overri<strong>de</strong>26 public void run ( ) {27 new EjemploWindowListener ( ) . creaGUI ( ) ;28 }29 }) ;30 }3132 // Los métodos que s i g u e n e s t á n d e c l a r a d o s en l a i n t e r f a c eWindowListener33 @Overri<strong>de</strong>34 public void windowOpened ( WindowEvent e ) {35 }36


160CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIO37 // Este e s e l ú n ico método <strong>con</strong> c ó d i g o38 @Overri<strong>de</strong>39 public void windowClosing ( WindowEvent e ) {40 System . out . p r i n t l n ( " C e r r a n d o la v e n t a n a ... " ) ;41 System . e x i t ( 0 ) ;42 }4344 @Overri<strong>de</strong>45 public void windowClosed ( WindowEvent e ) {46 }4748 @Overri<strong>de</strong>49 public void w i n d o w I c o n i f i e d ( WindowEvent e ) {50 }5152 @Overri<strong>de</strong>53 public void w i n d o w D e i c o n i f i e d ( WindowEvent e ) {54 }5556 @Overri<strong>de</strong>57 public void windowActivated ( WindowEvent e ) {58 }5960 @Overri<strong>de</strong>61 public void windowDeactivated ( WindowEvent e ) {62 }63 }Listado 11.2: Aplicación que finaliza cuando se cierra la ventana. La claseprincipal implementa el interface WindowListener, por lo que se pue<strong>de</strong> añadircomo escuchador <strong>de</strong> eventos WindowEvent.Si el Listado 11.2 te ha parecido tedioso, ya que hemos tenidoque <strong>de</strong>finir todos los métodos <strong>de</strong>jándolos vacíos excepto el métodowindowClosing(WindowEvent e), tu sensación es acertada. En los casos en losque una interface tiene <strong>de</strong>clarados muchos métodos, <strong>de</strong> los que usualmentesólo se escribe código para algunos <strong>de</strong> ellos, <strong>Java</strong> nos proporciona un clase <strong>de</strong><strong>con</strong>veniencia, llamada adaptadora, que implementa la interface <strong>de</strong>finiendo todoslos métodos vacíos. ¿Cual es la ventaja <strong>de</strong> utilizar estas clases adaptadoras?,pues que nuestros escuchadores en vez <strong>de</strong> implementar el interface extien<strong>de</strong>nla clase adaptadora, y sólo sobrescriben los métodos necesarios, la implementación<strong>de</strong>l resto <strong>de</strong> métodos será la que nos proporcione la clase adaptadora, es<strong>de</strong>cir, serán todos vacíos. El Listado 11.3 muestra un ejemplo cuyo comportamientoes el mismo que el ejemplo <strong>de</strong>l Listado 11.2, pero utilizando una claseinterna anónima que extien<strong>de</strong> la clase adaptadora WindowAdapter.1 package g u i ;23 import j a v a . awt . event . WindowAdapter ;4 import j a v a . awt . event . WindowEvent ;56 import javax . swing . JFrame ;7 import javax . swing . S w i n g U t i l i t i e s ;89 public f i n a l c l a s s EjemploClaseAdaptadora {10 private EjemploClaseAdaptadora ( ) {11 super ( ) ;12 }1314 private void creaGUI ( ) {15 // Creamos l a ventana16 JFrame ventana = new JFrame ( " E s c u c h a d o r <strong>con</strong> c l a s e a d a p t a d o r a . " ) ;17 // Añadimos como escuchador una i n s t a n c i a <strong>de</strong> una c l a s e i n t e r n a anónima18 // que e x t i e n d e a WindowAdapter y s ó l o s o b r e s c r i b e e l métodowindowClosing .


11.4.DETECCIÓN DE EVENTOS: ESCUCHADORES 16119 ventana . addWindowListener (new WindowAdapter ( ) {20 @Overri<strong>de</strong>21 public void windowClosing ( WindowEvent e ) {22 System . e x i t ( 0 ) ;23 }24 }) ;25 ventana . s e t S i z e ( 4 0 0 , 400) ;26 ventana . s e t V i s i b l e ( true ) ;27 }2829 public s t a t i c void main ( S t r i n g [ ] a r g s ) {30 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {31 @Overri<strong>de</strong>32 public void run ( ) {33 new EjemploClaseAdaptadora ( ) . creaGUI ( ) ;34 }35 }) ;36 }37 }Listado 11.3: Uso <strong>de</strong> la clase adaptadora WindowAdapter para cerrar laaplicación al cerrar la ventana.Por otro lado, la propia clases JFrame nos ofrece un método para <strong>de</strong>finir elcomportamiento <strong>de</strong> la aplicación cuando se cierra la ventana, este método essetDefaultCloseOperation(int modo). El Listado 11.4 muestra un ejemplo<strong>de</strong> uso <strong>de</strong> este método.1 package g u i ;23 import javax . swing . JFrame ;4 import javax . swing . S w i n g U t i l i t i e s ;56 public c l a s s E j e m p l o S e t D e f a u l t C l o s e O p e r a t i o n {7 private E j e m p l o S e t D e f a u l t C l o s e O p e r a t i o n ( ) {8 super ( ) ;9 }1011 private void creaGUI ( ) {12 // Creamos l a ventana13 JFrame ventana = new JFrame ( " E s c u c h a d o r <strong>con</strong> c l a s e a d a p t a d o r a . " ) ;14 // Usamos e l método <strong>de</strong> 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 n15 ventana . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;16 ventana . s e t S i z e ( 4 0 0 , 400) ;17 ventana . s e t V i s i b l e ( true ) ;18 }1920 public s t a t i c void main ( S t r i n g [ ] a r g s ) {21 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {22 @Overri<strong>de</strong>23 public void run ( ) {24 new E j e m p l o S e t D e f a u l t C l o s e O p e r a t i o n ( ) . creaGUI ( ) ;25 }26 }) ;27 }28 }Listado 11.4: Uso <strong>de</strong>l método setDefaultCloseOperation(int) para acabar laaplicación cuando se cierra la ventana.Esta técnica <strong>de</strong> escucha <strong>de</strong> eventos es transversal a todos los Contenedoresy Componentes que forman parte <strong>de</strong> Swing. Los Contenedores y Componenteslanzan eventos que seremos capaces <strong>de</strong> escuchar implementando el interfacea<strong>de</strong>cuado en una clase, y registrando esa clase como escuchador <strong>de</strong>l Contenedoro Componente.En la sección siguiente vamos a ver algunos <strong>de</strong> los Componentes Swing más


162CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIOcomunes y se mostrarán ejemplos <strong>de</strong> cómo escuchar los eventos que se producencuando el usuario interacciona sobre ellos.11.5. Algunos componentes SwingEsta sección no preten<strong>de</strong> ser una presentación exhaustiva <strong>de</strong> Componentes Swing,si no una muestra <strong>de</strong> cómo utilizar la técnica que se ha mostrado en la secciónanterior para escuchar los eventos que producen.11.5.1. JLabel, muestra texto o i<strong>con</strong>osEl primer y más sencillo Componente Swing es JLabel que se muestra comouna ca<strong>de</strong>na <strong>de</strong> texto o un i<strong>con</strong>o que el usuario no pue<strong>de</strong> modificar, pero sí elprogramador. Este componente no lanza ningún tipo <strong>de</strong> evento, ya que el usuario,como hemos dicho, no pue<strong>de</strong> interaccionar sobre él, nos sirve para mostrartexto o i<strong>con</strong>os.11.5.2. JButton, botones que el usuario pue<strong>de</strong> pulsarEl siguiente componente en la lista es JButton <strong>con</strong> el que po<strong>de</strong>mos crear botonesque el usuario pue<strong>de</strong> pulsar. Cada vez que el usuario pulsa un JButton, este lanzaun evento <strong>de</strong> tipo ActionEvent, si queremos escuchar este tipo <strong>de</strong> evento, necesitamosimplementar la interface ActionListener que únicamente <strong>de</strong>clara unmétodo public void actionPerformed(ActionEvent e) que será invocadocada vez que el usuario pulse el botón. Finalmente, registraremos el escuchadoral botón <strong>con</strong> el método addActionListener(ActionListener escuchador) <strong>de</strong>la clase JButton.El Listado 11.5 muestra un ejemplo <strong>de</strong> <strong>de</strong>tección <strong>de</strong> los eventos ActionEvent,don<strong>de</strong> también se ha incluido un JLabel. En la línea 21 creamos el botón comouna instancia <strong>de</strong> JButton, en las líneas 22-27 añadimos al botón un escuchadorcomo una clase interna anónima que implementa el interface ActionListenery <strong>de</strong>finimos el método que <strong>de</strong>clara esta interfaz public void actionPerformed(ActionEvent e). Otro <strong>de</strong>talle interesante es que hemos utilizado un JPanelpara añadir sobre él los componentes JLabel y JButton y aprovechar que elGestor <strong>de</strong> Aspecto <strong>de</strong> un JPanel es FlowLayout y coloca los Componentes enel sentido <strong>de</strong> la escritura. Otro <strong>de</strong>talle nuevo es el uso <strong>de</strong>l método public voidpack(); este método calcula el tamaño óptimo <strong>de</strong> la ventana para <strong>con</strong>tenertodos los componentes que se le han añadido, <strong>de</strong> este modo estamos <strong>de</strong>legandoen Swing el cálculo <strong>de</strong>l tamaño <strong>de</strong> la ventana.1 package g u i ;23 import j a v a . awt . event . ActionEvent ;4 import j a v a . awt . event . A c t i o n L i s t e n e r ;56 import javax . swing . JButton ;7 import javax . swing . JFrame ;8 import javax . swing . JLabel ;9 import javax . swing . JPanel ;10 import javax . swing . S w i n g U t i l i t i e s ;1112 public c l a s s EjemploJButton {13 private EjemploJButton ( ) {14 super ( ) ;


11.5. ALGUNOS COMPONENTES SWING 16315 }1617 private void creaGUI ( ) {18 JFrame ventana = new JFrame ( " Un J L a b e l y un J B u t t o n " ) ;19 JPanel c o n tenedor = new JPanel ( ) ;20 c o n t e n edor . add (new JLabel ( " P u l s a el b o t ó n : " ) ) ;21 JButton jbBoton = new JButton ( " P ú l s a m e " ) ;22 jbBoton . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) {23 @Overri<strong>de</strong>24 public void a c t i o n P e r f o r m e d ( ActionEvent e ) {25 System . out . p r i n t l n ( " B o t ó n p u l s a d o " ) ;26 }27 }) ;28 c o n t e n edor . add ( jbBoton ) ;29 ventana . getContentPane ( ) . add ( c o n t e n e d o r ) ;30 ventana . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;31 ventana . pack ( ) ;32 ventana . s e t V i s i b l e ( true ) ;33 }3435 public s t a t i c void main ( S t r i n g [ ] a r g s ) {36 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {37 @Overri<strong>de</strong>38 public void run ( ) {39 new EjemploJButton ( ) . creaGUI ( ) ;40 }41 }) ;42 }4344 }Listado 11.5: Ejemplo <strong>de</strong> uso <strong>de</strong> JButton11.5.3. JTextField, campos <strong>de</strong> introducción <strong>de</strong> textoLa clase JTextField dibuja una caja <strong>de</strong> edición <strong>de</strong> texto <strong>de</strong> una única líneadon<strong>de</strong> el usuario pue<strong>de</strong> introducir texto. En el momento <strong>de</strong> la instanciación <strong>de</strong>JTextField po<strong>de</strong>mos indicar el número <strong>de</strong> columnas.Un JTextField lanza eventos <strong>de</strong> tipo ActionEvent cada vez que el usuariopulsa el botón Enter y este componente tiene el foco. Este es el mismo eventoque lanza un botón cuando el usuario lo pulsa, luego el procedimiento paraescuchar los eventos es el mismo que en el caso <strong>de</strong> JButton. El Listado 11.6muestra un ejemplo <strong>de</strong> uso <strong>de</strong> JTextField. Fíjate que esta vez en la línea 22hemos optado por cambiar el Gestor <strong>de</strong> Aspecto <strong>de</strong>l JFrame es vez <strong>de</strong> utilizar unJPanel intermedio. En la línea 29 hemos utilizado el método String getText()para obtener el texto introducido por el usuario en el JTextField.1 package g u i ;23 import j a v a . awt . Container ;4 import j a v a . awt . FlowLayout ;5 import j a v a . awt . event . ActionEvent ;6 import j a v a . awt . event . A c t i o n L i s t e n e r ;78 import javax . swing . JFrame ;9 import javax . swing . JLabel ;10 import javax . swing . JTextField ;11 import javax . swing . S w i n g U t i l i t i e s ;1213 public c l a s s EjemploJTextField {14 private JTextField j t f T e x t o ;1516 private EjemploJTextField ( ) {17 super ( ) ;18 }


164CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIO1920 public void creaGUI ( ) {21 JFrame ventana = new JFrame ( ) ;22 ventana . setLayout (new FlowLayout ( ) ) ;23 Container c o n tenedor = ventana . getContentPane ( ) ;24 c o n t e nedor . add (new JLabel ( " I n t r o d u c e un t e x t o : " ) ) ;25 j t f T e x t o = new JTextField ( 5 0 ) ;26 j t f T e x t o . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) {27 @Overri<strong>de</strong>28 public void a c t i o n P e r f o r m e d ( ActionEvent e ) {29 System . out . p r i n t l n ( " El t e x t o e s c r i t o es : " + j t f T e x t o . getText ( ) ) ;30 }31 }) ;32 ventana . add ( j t f T e x t o ) ;33 ventana . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;34 ventana . pack ( ) ;35 ventana . s e t V i s i b l e ( true ) ;36 }3738 public s t a t i c void main ( S t r i n g [ ] a r g s ) {39 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {40 @Overri<strong>de</strong>41 public void run ( ) {42 new EjemploJTextField ( ) . creaGUI ( ) ;43 }44 }) ;45 }46 }Listado 11.6: Ejemplo <strong>de</strong> uso <strong>de</strong> JTextField11.5.4. JRadioButton, botones <strong>de</strong> opcionesEl siguiente Componente en complejidad es el botón <strong>de</strong> radio JRadioButton quedibuja un botón asociado a una opción, como por ejemplo para elegir la forma<strong>de</strong> pago Tarjeta/Transferencia/Cheque. Muchas veces las opciones presentadasal usuario son excluyentes, como en el caso anterior, <strong>de</strong> modo que seleccionaruna <strong>de</strong> ellas implica que se <strong>de</strong>-selecciona la anterior si hubiese alguna.JRadioButton pue<strong>de</strong> lanzar dos tipos <strong>de</strong> eventos interesante, nuestro <strong>con</strong>ocidoActionEvent y el nuevo ItemEvent. El evento ItemEvent nos da másinformación sobre lo que ha ocurrido que ActionEvent, ya que nos dice si loque ha ocurrido es una selección o una <strong>de</strong>s-selección <strong>de</strong>l botón.Ya sabemos que los eventos <strong>de</strong> tipo ActionEvent los po<strong>de</strong>mos escuchar<strong>con</strong> una clase que implemente el interface ActionListenerañadida al Componente que queremos escuchar <strong>con</strong> el métodoaddActionListener(ActionListener escuchador), y que este interfacesólo <strong>de</strong>clara un método public void actionPerformed(ActionEvent e) quese invoca cada vez que el botón se pulsa.En el caso <strong>de</strong> un evento <strong>de</strong> tipo ItemEvent lo po<strong>de</strong>mos escuchar<strong>con</strong> una clase que implemente el interface ItemListener siempreque añadamos la instancia al Componente que queremos escuchar <strong>con</strong>addItemListener(ItemListener escuchador). Este interface sólo <strong>de</strong>claraun método public void itemStateChanged(ItemEvent e) que se invocacada vez que el usuario selecciona o <strong>de</strong>s-selecciona un JRadioButton. Para<strong>con</strong>ocer si lo que ha ocurrido es una selección o una <strong>de</strong>-selección, po<strong>de</strong>mosutilizar el método getStateChange() <strong>de</strong> la clase ItemEvent que nos<strong>de</strong>volverá ItemEvent.SELECTED si lo que ha ocurrido es una selección, oItemEvent.DESELECTED si lo que ha ocurrido es una <strong>de</strong>-selección.


11.5. ALGUNOS COMPONENTES SWING 1651 package g u i ;23 import j a v a . awt . Bor<strong>de</strong>rLayout ;4 import j a v a . awt . event . ActionEvent ;5 import j a v a . awt . event . A c t i o n L i s t e n e r ;6 import j a v a . awt . event . ItemEvent ;7 import j a v a . awt . event . I t e m L i s t e n e r ;89 import javax . swing . BoxLayout ;10 import javax . swing . ButtonGroup ;11 import javax . swing . JFrame ;12 import javax . swing . JPanel ;13 import javax . swing . JRadioButton ;14 import javax . swing . S w i n g U t i l i t i e s ;1516 public f i n a l c l a s s EjemploJRadioButton {17 private EjemploJRadioButton ( ) {18 super ( ) ;19 }2021 private JPanel creaContenedor ( S t r i n g p o s i c i o n ) {22 JPanel c o n tenedor = new JPanel ( ) ;23 c o n t e n edor . setLayout (new BoxLayout ( <strong>con</strong>tenedor , BoxLayout . Y AXIS ) ) ;24 Escuchador escuchador = new Escuchador ( ) ;25 JRadioButton jrbMucho = new JRadioButton ( " M u c h o " ) ;26 jrbMucho . a d d A c t i o n L i s t e n e r ( escuchador ) ;27 jrbMucho . a d d I t e m L i s t e n e r ( e scuchador ) ;28 c o n t e n edor . add ( jrbMucho ) ;29 JRadioButton jrbNormal = new JRadioButton ( " N o r m a l " ) ;30 jrbNormal . a d d A c t i o n L i s t e n e r ( e scuchador ) ;31 jrbNormal . a d d I t e m L i s t e n e r ( escuchador ) ;32 c o n t e n edor . add ( jrbNormal ) ;33 JRadioButton jrbPoco = new JRadioButton ( " Poco " ) ;34 jrbPoco . a d d A c t i o n L i s t e n e r ( escuchador ) ;35 jrbPoco . a d d I t e m L i s t e n e r ( e scuchador ) ;36 c o n t e n edor . add ( jrbPoco ) ;37 i f ( p o s i c i o n == Bor<strong>de</strong>rLayout .EAST) {38 ButtonGroup grupo = new ButtonGroup ( ) ;39 grupo . add ( jrbMucho ) ;40 grupo . add ( jrbNormal ) ;41 grupo . add ( jrbPoco ) ;42 }43 return c o n t e nedor ;44 }4546 private void creaGUI ( ) {47 JFrame ventana = new JFrame ( " E j e m p l o J R a d i o B u t t o n " ) ;48 ventana . add ( creaContenedor ( Bor<strong>de</strong>rLayout .WEST) , Bor<strong>de</strong>rLayout .WEST) ;49 ventana . add ( creaContenedor ( Bor<strong>de</strong>rLayout .EAST) , Bor<strong>de</strong>rLayout .EAST) ;50 ventana . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;51 ventana . pack ( ) ;52 ventana . s e t V i s i b l e ( true ) ;53 }5455 private c l a s s Escuchador implements A c t i o n L i s t e n e r , I t e m L i s t e n e r {56 public Escuchador ( ) {57 super ( ) ;58 }5960 @Overri<strong>de</strong>61 public void a c t i o n P e r f o r m e d ( ActionEvent e ) {62 System . out . p r i n t l n ( " B o t ó n p u l s a d o " ) ;63 }6465 @Overri<strong>de</strong>66 public void itemStateChanged ( ItemEvent e ) {67 S t r i n g t e x t o = ( ( JRadioButton ) e . g e t S o u r c e ( ) ) . getText ( ) ;68 i f ( e . getStateChange ( ) == ItemEvent .DESELECTED)69 System . out . format ( " B o t ó n %s d e s e l e c c i o n a d o .\ n " , t e x t o ) ;70 e l s e i f ( e . getStateChange ( ) == ItemEvent .SELECTED)71 System . out . format ( " B o t ó n %s s e l e c c i o n a d o .\ n " , t e x t o ) ;72 }73 }


166CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIO7475 public s t a t i c void main ( S t r i n g [ ] a r g s ) {76 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {77 @Overri<strong>de</strong>78 public void run ( ) {79 new EjemploJRadioButton ( ) . creaGUI ( ) ;80 }81 }) ;82 }8384 }Listado 11.7: Ejemplo <strong>de</strong> uso <strong>de</strong> JRadioButtonEl Listado 11.7 muestra un ejemplo <strong>con</strong> este nuevo Componente. A<strong>de</strong>más<strong>de</strong> lo ya comentado, fíjate que en las líneas 38-41 estamos utilizando una nuevaclase ButtonGroup, esta clase agrupa los botones <strong>de</strong> manera excluyente, <strong>de</strong>modo que cuando alguno <strong>de</strong> los botones se selecciona, si hay algún otro botónpreviamente seleccionado este último se <strong>de</strong>s-seleccionará. La clase ButtonGroupcrea un grupo lógico, y no tiene ninguna representación gráfica. En la línea 67pue<strong>de</strong>s ver como hemos recuperado el texto escrito a la <strong>de</strong>recha <strong>de</strong> cada uno <strong>de</strong>los JRadioButton. Y finalmente, en la línea 23 hemos utilizado un nuevo Gestor<strong>de</strong> Aspecto, BoxLayout que nos permite disponer los Componentes verticalmente<strong>de</strong>ntro <strong>de</strong> la ventana.11.5.5. JCheckBox, botones <strong>de</strong> selección múltipleUsualmente los botones <strong>de</strong> tipo JRadioButton se utilizan cuando las opcionespresentadas al usuario son mutuamente excluyentes entre sí, y se han añadido aun ButtonGroup para comportarse <strong>de</strong> este modo. Si lo que queremos presentar alusuario son opciones no excluyentes solemos utilizar botones <strong>de</strong> tipo JCheckBox.Estos botones se dibujan como una pequeña caja que al seleccionarlo aparecemarcada <strong>con</strong> un tick.Los JCheckBox lanzan los mismos tipos <strong>de</strong> eventos que los JRadioButton,es <strong>de</strong>cir eventos ActionEvent y eventos ItemEvent para indicar, estos últimos,si lo que ha ocurrido es una selección o una <strong>de</strong>-selección. Por lo tanto todolo comentado en la sección 11.5.4 sobre los JRadioButton es válido para losJCheckBox.11.5.6. JList, listas <strong>de</strong> selecciónLa clase JList presentan al usuario varias opciones en forma <strong>de</strong> lista. El usuariopue<strong>de</strong> seleccionar una o más opciones <strong>de</strong>pendiendo <strong>de</strong>l modo <strong>de</strong> selección <strong>de</strong> lalista.Los eventos que un JList pue<strong>de</strong> lazar cada vez que el usuario seleccionauna opción <strong>de</strong> la lista son nuestro <strong>con</strong>ocido ActionEvent y el nuevoListSelectionEvent. Este evento nos indica si la selección se está efectuando(por ejemplo, el usuario pulsa sobre un elemento <strong>de</strong> la lista y, sin soltar elbotón <strong>de</strong>l ratón, se <strong>de</strong>splaza sobre los elementos <strong>de</strong> la lista), o es la acción final,cuando el usuario suelta el botón <strong>de</strong>l ratón.Para escuchar los eventos <strong>de</strong> tipo ItemSelectionEvent <strong>de</strong>bemos implementarla interface ItemSelectionListener que <strong>de</strong>clara un único métodopublic void valueChanged(ListSelectionEvent e). Para <strong>con</strong>sultar si


11.5. ALGUNOS COMPONENTES SWING 167la selección está en marcha o es <strong>de</strong>finitiva po<strong>de</strong>mos usar el métodogetValueIsAdjusting() <strong>de</strong> la clase ItemSelectionEvent.El Listado 11.8 muestra un ejemplo <strong>de</strong> uso <strong>de</strong> este componente. Otros <strong>de</strong>tallesinteresantes <strong>de</strong> este ejemplo son el uso <strong>de</strong>l método setVisibleRowCount(int)<strong>de</strong> la línea 25 para indicar cuantos elementos son visibles en la lista.En la línea 23 activamos el modo <strong>de</strong> selección <strong>de</strong> los elementos <strong>de</strong>la lista a ListSelectionMo<strong>de</strong>l.SINGLE SELECTION, <strong>de</strong> modo que sólo sepodrá seleccionar un elemento único <strong>de</strong> la lista (otros modos posible sonSINGLE INTERVAL SELECTION y MULTIPLE INTERVAL SELECTION).1 package g u i ;23 import j a v a . awt . Container ;45 import javax . swing . JFrame ;6 import javax . swing . J L i s t ;7 import javax . swing . J S c r o l l P a n e ;8 import javax . swing . L i s t S e l e c t i o n M o d e l ;9 import javax . swing . S w i n g U t i l i t i e s ;10 import javax . swing . event . L i s t S e l e c t i o n E v e n t ;11 import javax . swing . event . L i s t S e l e c t i o n L i s t e n e r ;1213 public f i n a l c l a s s EjemploJList {14 private EjemploJList ( ) {15 super ( ) ;16 }1718 private void creaGUI ( ) {19 JFrame ventana = new JFrame ( " E j e m p l o J L i s t " ) ;20 Container c o ntenedor = ventana . getContentPane ( ) ;21 J L i s t o p c i o n e s = new J L i s t (new S t r i n g [ ] { " L u n e s " , " M a r t e s " , " M i é r c o l e s ", " J u e v e s " , " V i e r n e s " , " S á b a d o " , " D o m i n g o " }) ;22 o p c i o n e s . setVisibleRowCount ( 5 ) ;23 o p c i o n e s . s e t S e l e c t i o n M o d e ( L i s t S e l e c t i o n M o d e l . SINGLE SELECTION) ;24 o p c i o n e s . a d d L i s t S e l e c t i o n L i s t e n e r (new Escuchador ( ) ) ;25 J S c r o l l P a n e s c r o l l = new J S c r o l l P a n e ( o p c i o n e s ) ;26 c o n t e n edor . add ( s c r o l l ) ;27 ventana . pack ( ) ;28 ventana . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;29 ventana . s e t V i s i b l e ( true ) ;30 }3132 private c l a s s Escuchador implements L i s t S e l e c t i o n L i s t e n e r {33 @Overri<strong>de</strong>34 public void valueChanged ( L i s t S e l e c t i o n E v e n t e ) {35 i f ( e . g e t V a l u e I s A d j u s t i n g ( ) == true )36 System . out . p r i n t l n ( " Item en c u r s o : " + ( ( J L i s t ) e . g e t S o u r c e ( ) ) .g e t S e l e c t e d V a l u e ( ) ) ;37 e l s e i f ( e . g e t V a l u e I s A d j u s t i n g ( ) == f a l s e )38 System . out . p r i n t l n ( " Item 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 ( ) ) .g e t S e l e c t e d V a l u e ( ) ) ;39 }40 }4142 public s t a t i c void main ( S t r i n g [ ] a r g s ) {43 S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) {44 @Overri<strong>de</strong>45 public void run ( ) {46 new EjemploJList ( ) . creaGUI ( ) ;47 }48 }) ;49 }5051 }Listado 11.8: Ejemplo <strong>de</strong> uso <strong>de</strong> JList


168CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIOPor <strong>de</strong>fecto, el Componente JList no tiene una barra <strong>de</strong> <strong>de</strong>splazamientopara po<strong>de</strong>r visualizar los elementos <strong>de</strong> la lista, si queremos que la lista poseauno, lo tenemos que añadir tal y como se muestra en las líneas 25 y 26. Fíjateque indicamos el Componente al que asociaremos la barra <strong>de</strong> <strong>de</strong>splazamiento enel momento <strong>de</strong> crear esta. Finalmente añadimos la barra <strong>de</strong> <strong>de</strong>splazamiento ala ventana y no la lista original.Otros métodos interesantes <strong>de</strong>l Componente JList son ObjectgetSelectedValue() el elementos actualmente seleccionado, si la lista es<strong>de</strong> selección única; y Object [] getSelectedValues() si se lista es <strong>de</strong>selección múltiple.Con el componente JList acabamos la breve muestra <strong>de</strong> las posibilida<strong>de</strong>s<strong>de</strong> creación <strong>de</strong> interfaces gráficos <strong>de</strong> usuario Swing. Esta sección no preten<strong>de</strong> serun exposición exhaustiva <strong>de</strong> todas las posibilida<strong>de</strong>s que nos proporciona Swing,lo que preten<strong>de</strong> mostrar es la técnica <strong>de</strong> cómo programar interfaces gráficos <strong>de</strong>usuario <strong>con</strong> el patrón <strong>de</strong> diseño Observable.11.6. El patrón <strong>de</strong> diseño Mo<strong>de</strong>lo/Vista/ControladorQuizás el patrón <strong>de</strong> diseño Mo<strong>de</strong>lo/Vista/Controlador sea uno <strong>de</strong> los más utilizadosen el <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> informáticos, tanto es así que incluso existeuna adaptación al mundo <strong>de</strong> aplicaciones web <strong>de</strong> este patrón <strong>de</strong> diseño.Este patrón <strong>de</strong> diseño <strong>de</strong>fine tres actores <strong>con</strong> las siguientes responsabilida<strong>de</strong>s:Mo<strong>de</strong>lo es el responsable <strong>de</strong> mantener y gestionar los datos <strong>de</strong> la aplicación.Vista es la responsable <strong>de</strong>l interfaz gráfico <strong>de</strong> usuario y la <strong>de</strong>tección <strong>de</strong> eventossobre los componentes.Controlador es quien hace correspon<strong>de</strong>r la interacción <strong>de</strong>l usuario <strong>con</strong> posiblecambios en el Mo<strong>de</strong>lo.Veamos , <strong>con</strong> un ejemplo, el papel <strong>de</strong> cada uno <strong>de</strong> estos actores. En la Figura11.5 se muestra el interfaz gráfico <strong>de</strong> una aplicación que calcula la cuota mensual<strong>de</strong> una hipoteca. El usuario pue<strong>de</strong> introducir los tres datos que se necesita parael cálculo en tres cajas <strong>de</strong> edición <strong>de</strong> texto, y cuando pulsa el botón Calculaaparece la nueva cuota en la parte inferior <strong>de</strong> la ventana.En este caso, el Mo<strong>de</strong>lo <strong>con</strong>tiene los datos <strong>de</strong> la hipoteca: cantidad hipotecada,duración <strong>de</strong> la hipoteca, interés <strong>de</strong>l préstamo y la cuota mensual. La Vistaes la encargada <strong>de</strong> crear el interfaz gráfico y la <strong>de</strong>tección <strong>de</strong> los eventos sobreel interfaz. El Controlador sabe que cuando el usuario pulsa el botón Calcula,<strong>de</strong>be leer los datos <strong>de</strong> la hipoteca y enviárselos al Mo<strong>de</strong>lo para que este haga elcálculo.En la Figura 11.6 se muestra la dinámica <strong>de</strong> este patrón <strong>de</strong> diseño, que se<strong>de</strong>talla en los siguiente pasos:1. El usuario interacciona sobre la Vista.2. La Vista informa al Controlador <strong>de</strong> lo ocurrido.


11.6. EL PATRÓN DE DISEÑO MODELO/VISTA/CONTROLADOR 169Figura 11.5:hipoteca.Un interfaz gráfico para el cálculo <strong>de</strong> la cuota mensual <strong>de</strong> unaFigura 11.6: Dinámica <strong>de</strong>l mo<strong>de</strong>lo MVC.3. El Controlador <strong>de</strong>ci<strong>de</strong> que datos necesita <strong>de</strong> la Vista para llevar a cabo latarea como respuesta a la interacción <strong>de</strong>l usuario.4. El Controlador actualiza el Mo<strong>de</strong>lo.5. El Mo<strong>de</strong>lo informa a la Vista <strong>de</strong> que se ha actualizado.6. La Vista pi<strong>de</strong> los datos <strong>de</strong> su interés para visualizarlos.En el ejemplo <strong>de</strong>l cálculo <strong>de</strong> la cuota mensual <strong>de</strong> una hipoteca esta dinámicase <strong>con</strong>cretaría <strong>de</strong>l siguiente modo:1. El usuario introduce la cantidad, el tiempo y el interés <strong>de</strong> la hipoteca ypulsa el botón Calcula.2. La Vista informa al Controlador <strong>de</strong> que el usuario ha pulsado el botónCalcula.3. La lógica <strong>de</strong>l negocio programada en el Controlador indica que si el usuariopulsa el botón Calcula se <strong>de</strong>be recuperar la cantidad, el tiempo y el interés<strong>de</strong> la hipoteca que están en la Vista.4. El Controlador envía estos datos al Mo<strong>de</strong>lo para que calcule la nuevacuota.5. El Mo<strong>de</strong>lo calcula la nueva cuota e informa <strong>de</strong> ello a la Vista6. La Vista pi<strong>de</strong> la nueva cuota y la visualiza.


170CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIOFigura 11.7: Diagrama UML para el patrón <strong>de</strong> diseño MVC.Aunque en un primer momento, este patrón pue<strong>de</strong> resultar farragoso, o parecernosque hay muchas idas y venidas entre el código <strong>de</strong> los actores, es todo lo<strong>con</strong>trario, gracias a esta división <strong>de</strong> responsabilida<strong>de</strong>s los posibles cambios en laimplementación <strong>de</strong> uno <strong>de</strong> los actores es completamente transparente al resto.Imagina por ejemplo que hay un cambio en la Vista, si <strong>de</strong>spués <strong>de</strong> cambiar, laVista sigue proporcionándonos los datos sobre los que efectuar el cálculo y lepo<strong>de</strong>mos seguir informando <strong>de</strong> los cambios en el Mo<strong>de</strong>lo para que se actualice,este cambio será totalmente transparente tanto para el Controlador como parael Mo<strong>de</strong>lo.Fíjate que para po<strong>de</strong>r implementar este patrón <strong>de</strong> diseño, la Vista <strong>de</strong>be<strong>con</strong>ocer tanto al Controlador como al Mo<strong>de</strong>lo, y por su parte el Controlador<strong>de</strong>be <strong>con</strong>ocer tanto a la Vista como al Mo<strong>de</strong>lo. Por su lado, el único actor quenecesita <strong>con</strong>ocer el Mo<strong>de</strong>lo es a la Vista, <strong>de</strong> hecho, en la Figura 11.6 no hayninguna flecha que salga <strong>de</strong>s<strong>de</strong> el Mo<strong>de</strong>lo hacia el Controlador. Que un actortenga <strong>con</strong>ocimiento <strong>de</strong> los otros implica que tendrá una referencia hacia el actor<strong>con</strong> el que necesita intercambiar mensajes. Para <strong>de</strong>jar aún más clara la potencia<strong>de</strong> este patrón, vamos a implementar las referencias a una interface y no a unaclase <strong>con</strong>creta, es <strong>de</strong>cir, la funcionalidad que un actor ofrece la vamos a recogeren un interface y a<strong>de</strong>más tendremos la clase <strong>con</strong>creta que implementará elinterface, la Figura 11.7 muestra el esquema <strong>de</strong> clases citado.En el Apéndice B se encuentra el código fuente <strong>de</strong> la aplicación <strong>de</strong>l cálculo<strong>de</strong> la cuota mensual <strong>de</strong> una hipoteca.Cuestiones.1. ¿Cuales son las ventajas y <strong>de</strong>sventajas <strong>de</strong> utilizar el método <strong>de</strong> <strong>con</strong>venienciasetDefaultCoseOperation(JFrame.EXIT ON CLOSE) para cerrarla aplicación cuando el usuario cierra la ventana?.


11.6. EL PATRÓN DE DISEÑO MODELO/VISTA/CONTROLADOR 1712. El código <strong>de</strong>l cálculo <strong>de</strong> la cuota mensual <strong>de</strong> una hipoteca mostrado enel Apéndice B sigue la estrategia <strong>de</strong> no enviar datos <strong>de</strong>s<strong>de</strong> la Vista alControlador cuando se produce el evento <strong>de</strong> pulsar el botón Calcula, es<strong>de</strong>cir, la Vista no envía la cantidad hipoteca, el tiempo ni el interés, esel Controlador quien pi<strong>de</strong> estos datos una vez que la Vista le informa <strong>de</strong>que el usuario pulsó el botón Calcula. Lo mismo ocurre cuando el Mo<strong>de</strong>locambia su estado, al informar a la Vista no le envía el nuevo valor <strong>de</strong>la cuota mensual <strong>de</strong> la hipoteca, simplemente le informa <strong>de</strong> que hay unnuevo valor disponible y es finalmente la Vista quien pi<strong>de</strong> el nuevo valoral Mo<strong>de</strong>lo.¿Cuales son las ventajas <strong>de</strong> esta aproximación?. ¿Cuales son las <strong>de</strong>sventajas?.Ejercicios.1. Recupera el ejercicio <strong>de</strong> la agenda telefónica y crea un interfaz gráfico <strong>de</strong>usuario para ella.Lecturas recomendadas.El capítulo 1 <strong>de</strong> la referencia [8] presenta el patrón <strong>de</strong> diseño MVC haciendoreferencia al resto <strong>de</strong> patrones que utiliza.La referencia [3] también presenta <strong>de</strong> modo riguroso y muy ameno elpatrón <strong>de</strong> diseño MVC.


172CAPÍTULO 11. INTERFACES GRÁFICAS DE USUARIO


Capítulo 12AppletsContenidos12.1. ¿Qué son los Applets? . . . . . . . . . . . . . . . . . 17312.2. Ciclo <strong>de</strong> vida <strong>de</strong> un Applet . . . . . . . . . . . . . 17412.3. Código HTML para <strong>con</strong>tener un Applet . . . . . . 17512.4. Lectura <strong>de</strong> parámetros <strong>de</strong> la página HTML . . . . 17612.5. Convertir una aplicación Swing en un Applet . . 17612.6. Comunicación entre Applets . . . . . . . . . . . . . 177IntroducciónPara muchos <strong>de</strong> nosotros, el primer <strong>con</strong>tacto <strong>con</strong> <strong>Java</strong> fue a través <strong>de</strong> losApplets, esos pequeños programas que se ejecutan <strong>de</strong>ntro <strong>de</strong> una página web y<strong>con</strong> los que se pue<strong>de</strong> interaccionar. Algunos ejemplos se pue<strong>de</strong>n en<strong>con</strong>trar en lassiguientes direcciones: http://www.falstad.com/mathphysics.html, http://openastexviewer.net/web/thinlet.html.En este capítulo se va a presentar la programación <strong>de</strong> Apples, sus características,particularida<strong>de</strong>s, y cómo, <strong>con</strong> poco trabajo extra po<strong>de</strong>mos <strong>con</strong>vertirnuestras aplicaciones Swing en Applets siempre que se cumpla una serie <strong>de</strong>restricciones.12.1. ¿Qué son los Applets?Los Applets son aplicaciones <strong>Java</strong> que se ejecutan en el <strong>con</strong>texto <strong>de</strong> un navegadorweb. A través <strong>de</strong> código HTML reservamos una zona <strong>de</strong> la página web paravisualizar el Applet, y es el navegador web el encargado <strong>de</strong> <strong>de</strong>scargar las clases<strong>de</strong>l Applet <strong>de</strong>s<strong>de</strong> la url especificada, iniciar una instancia <strong>de</strong> la máquina virtual<strong>de</strong> <strong>Java</strong> y ejecutar el Applet.La seguridad, en el caso <strong>de</strong> los Applets, es un importante factor a tener encuenta. Para ejecutar un Applet es necesario <strong>de</strong>scargar <strong>de</strong>s<strong>de</strong> la web las clasesque se ejecutarán en nuestra máquina. Fíjate el riesgo que en principio se corresi no hubiese restricciones <strong>de</strong> seguridad, <strong>de</strong>scargas un programa que no sabes173


174CAPÍTULO 12. APPLETSquien ha escrito ni <strong>con</strong> qué propósito y lo ejecutas en tu máquina. Si no hubiesenrestricciones <strong>de</strong> seguridad un programa malicioso podría acce<strong>de</strong>r a tu disco duropara leer información personal, o podría borrar o modificar ficheros, o escribiren tu disco duro.Por todo lo anterior, un Applet tiene las siguientes restricciones <strong>de</strong> seguridad:Un Applet no pue<strong>de</strong> leer <strong>de</strong>l disco duro <strong>de</strong>l cliente.Un Applet no pue<strong>de</strong> escribir al disco duro <strong>de</strong>l cliente.Un Applet no pue<strong>de</strong> abrir <strong>con</strong>exiones <strong>de</strong> red a ningún otro servidor salvoaquel <strong>de</strong>s<strong>de</strong> el que se <strong>de</strong>scargó.Un Applet no pue<strong>de</strong> ejecutar aplicaciones en el cliente.Un Applet no pue<strong>de</strong> acce<strong>de</strong>r a la información privada <strong>de</strong>l usuario.Estas restricciones <strong>de</strong> seguridad y el hecho <strong>de</strong> que finalmente el Applet seejecutará en un Máquina Virtual <strong>Java</strong> los hacen muy seguros.12.2. Ciclo <strong>de</strong> vida <strong>de</strong> un AppletUn Applet se ejecuta en el <strong>con</strong>texto <strong>de</strong> un navegador web y tiene fuertes restricciones<strong>de</strong> seguridad, tal y como hemos visto. El hecho <strong>de</strong> que los Applets seejecuten en el <strong>con</strong>texto <strong>de</strong> un navegador web implica que su ciclo <strong>de</strong> vida no es el<strong>de</strong> una aplicación <strong>de</strong> escritorio, como las que ya hemos aprendido a programar.El ciclo <strong>de</strong> vida <strong>de</strong> un Applet está directamente relacionado <strong>con</strong> las llamadasque el navegador web hace a métodos <strong>de</strong>l Applet.Para que una <strong>de</strong> nuestras clases sea un Applet <strong>de</strong>be exten<strong>de</strong>r a la claseJApplet que <strong>de</strong>fine los siguientes métodos relacionados <strong>con</strong> su ciclo <strong>de</strong> vida:public void init(), el navegador web llama a este método cuando elApplet ha sido efectivamente cargado. Este método es el primero que seinvoca en un Applet.public void start(), el navegador web llama a este método para indicarleque <strong>de</strong>be empezar su ejecución.public void paint(Graphics g), el navegador web llama a este métodocada vez que se <strong>de</strong>be dibujar el <strong>con</strong>tenido <strong>de</strong>l Applet, y nos permiteel acceso al <strong>con</strong>texto gráfico <strong>de</strong> bajo nivel Graphics. Un <strong>de</strong>talle muy importantees que <strong>de</strong>s<strong>de</strong> nuestro código nunca llamaremos directamente aeste método, para forzar su llamada utilizaremos el método public voidrepaint().public void stop(), el navegador web llama a este método para indicarque el Applet <strong>de</strong>be <strong>de</strong>tener su ejecución, por ejemplo, cuando se abandonala página web que <strong>con</strong>tiene el Applet.public void <strong>de</strong>stroy(), el navegador web llama a este método antes <strong>de</strong>eliminar el Applet <strong>de</strong> memoria, en cuyo caso se llamará previamente almétodo stop().


12.3.CÓDIGO HTML PARA CONTENER UN APPLET 175Figura 12.1: Llamada a los métodos <strong>de</strong> un Applet durante su ciclo <strong>de</strong> vida.La Figura 12.1 muestra gráficamente el or<strong>de</strong>n <strong>de</strong> las llamadas entre los métodosque <strong>con</strong>stituyen el ciclo <strong>de</strong> vida <strong>de</strong> un Applet.Para programar <strong>de</strong> modo eficiente un Applet <strong>de</strong>bemos seguir estas reglas: losrecursos que el Applet necesita para su ejecución <strong>de</strong>ben ser creados en su métodoinit(), y esos mismos recursos <strong>de</strong>ben ser liberados en su método <strong>de</strong>stroy().Nunca llamaremos, <strong>de</strong>s<strong>de</strong> nuestro código, al método paint(Graphics g) paraforzar el dibujado <strong>de</strong>l Applet, para ellos utilizaremos el método repaint().12.3. Código HTML para <strong>con</strong>tener un AppletPara po<strong>de</strong>r visualizar un Applet al cargar una página HTML <strong>de</strong>bemos utilizar laetiqueta como se muestra en el Listado 12.1. El atributo achive nos sirvepara indicar el fichero empaquetado <strong>de</strong> nuestra aplicación; <strong>con</strong> la etiqueta co<strong>de</strong>especificamos la clase que implementa el Applet, como veremos en la siguientesección; y mediante las etiquetas width y height especificamos el ancho y alto<strong>de</strong>ntro <strong>de</strong> la página web reservado para la visualización <strong>de</strong>l Applet.< t i t l e>El primer a p p l e tS i pue<strong>de</strong>s ver e s t o tu navegador no s o p o r t a <strong>Java</strong> .Listado 12.1: Código HTML que muestra la aplicación <strong>de</strong> la hipoteca <strong>de</strong>ntro <strong>de</strong>una página web.Otros atributos <strong>de</strong> la etiqueta que nos pue<strong>de</strong>n ser <strong>de</strong> utilidad son:alt, muestra un texto alternativo si el navegador no pue<strong>de</strong> visualizar elApplet.align, el alineado <strong>de</strong>l Applet <strong>de</strong>ntro <strong>de</strong> la página web.hspace, el margen a la izquierda y <strong>de</strong>recha <strong>de</strong>l Applet en unida<strong>de</strong>s <strong>de</strong>píxeles.


176CAPÍTULO 12. APPLETSvspace, el margen superior e inferior <strong>de</strong>l Applet en unida<strong>de</strong>s <strong>de</strong> píxeles.name, el nombre <strong>de</strong>l Applet. Etiqueta importante en la comunicación entreApplets que resi<strong>de</strong>n en la misma página web, como veremos en la Sección12.6.12.4. Lectura <strong>de</strong> parámetros <strong>de</strong> la página HTMLLa etiqueta pue<strong>de</strong> <strong>con</strong>tener otras etiquetas <strong>de</strong> interés a<strong>de</strong>más <strong>de</strong> lasque ya hemos visto. Con la etiqueta especificamos un parámetro <strong>con</strong>su valor, por ejemplo


12.6.COMUNICACIÓN ENTRE APPLETS 1773 import javax . swing . JApplet ;45 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 ntrolador ;6 import g u i . h i p o t e c a . c o n t r o l a d o r . ControladorImpl ;7 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>lo ;8 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>loImpl ;9 import g u i . h i p o t e c a . v i s t a . V i s t a ;10 import g u i . h i p o t e c a . v i s t a . VistaImpl ;1112 public c l a s s HipotecaApplet extends JApplet {13 private s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 1L ;1415 @Overri<strong>de</strong>16 public void i n i t ( ) {17 V i s t a v i s t a = new VistaImpl ( ) ;18 Mo<strong>de</strong>lo mo<strong>de</strong>lo = new Mo<strong>de</strong>loImpl ( ) ;19 Controlador c o n t r o l a d o r = new ControladorImpl ( ) ;20 mo<strong>de</strong>lo . s e t V i s t a ( v i s t a ) ;21 v i s t a . 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 ) ;22 v i s t a . setMo<strong>de</strong>lo ( mo<strong>de</strong>lo ) ;23 c o n t r o l a d o r . setMo<strong>de</strong>lo ( mo<strong>de</strong>lo ) ;24 c o n t r o l a d o r . s e t V i s t a ( v i s t a ) ;2526 setContentPane ( v i s t a . getContenedor ( ) ) ;27 }28 }Listado 12.2: Applet <strong>con</strong> la aplicación <strong>de</strong>l cálculo <strong>de</strong> la cuota mensual <strong>de</strong> unahipotecaComo tuvimos cuidado <strong>de</strong> aislar todo lo relativo al actor Vista siguiendoel patrón <strong>de</strong> diseño MVC, la transformación <strong>de</strong> aplicación Swing a Appletha sido muy sencilla, <strong>de</strong> hecho, hemos podido aprovechar todas lasclases <strong>de</strong>ntro los paquetes gui.hipoteca.mo<strong>de</strong>lo, gui.hipoteca.vista ygui.hipoteca.<strong>con</strong>trolador.12.6. Comunicación entre AppletsApplets que resi<strong>de</strong>n <strong>de</strong>ntro <strong>de</strong> la misma página web pue<strong>de</strong>n obtener referenciasa los otros Applets <strong>de</strong>ntro <strong>de</strong> la misma página y a través <strong>de</strong> estas referenciasun Applet pue<strong>de</strong> llamar a los métodos <strong>de</strong> otro Applet <strong>de</strong>ntro <strong>de</strong> la misma páginaweb. Para ello utilizaremos el método Applet getApplet(String nombre)que recibe como argumento el nombre <strong>de</strong>l Applet <strong>de</strong>l que queremos obteneruna referencia. Recuerda que el nombre <strong>de</strong> un Applet lo po<strong>de</strong>mos <strong>de</strong>finir <strong>con</strong>el atributo name <strong>de</strong> la etiqueta . Este método pertenece al <strong>con</strong>textodon<strong>de</strong> se está ejecutando el Applet, que no es otra cosa que la propia páginaweb. Para obtener este <strong>con</strong>texto <strong>de</strong>s<strong>de</strong> un Applet utilizamos el método publicAppletContext getAppletContext().El Listado 12.3 muestra un sencillo ejemplo <strong>de</strong> comunicación entre Appletsresi<strong>de</strong>ntes en la misma página web mostrado en el Listado 12.4. La Figura 12.2muestra cómo se visualiza la página web en un navegador.1 package a p p l e t s . comunicacion ;23 import j a v a . awt . Bor<strong>de</strong>rLayout ;4 import j a v a . awt . event . ActionEvent ;5 import j a v a . awt . event . A c t i o n L i s t e n e r ;67 import javax . swing . JApplet ;8 import javax . swing . JButton ;9 import javax . swing . JLabel ;


178CAPÍTULO 12. APPLETS10 import javax . swing . JPanel ;11 import javax . swing . JTextField ;1213 public c l a s s ComunicacionApplets extends JApplet {14 private s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 1L ;15 private JTextField j t f T u D i c e s ;16 private JLabel j l E l D i c e ;17 private ComunicacionApplets e l O t r o = null ;18 JPanel c o n tenedor = new JPanel (new Bor<strong>de</strong>rLayout ( ) ) ;1920 @Overri<strong>de</strong>21 public void i n i t ( ) {22 JPanel miEntrada = new JPanel ( ) ;23 miEntrada . add (new JLabel ( " Tú d i c e s : " ) ) ;24 j t f T u D i c e s = new JTextField ( 5 0 ) ;25 miEntrada . add ( j t f T u D i c e s ) ;26 JButton j b E n v i a r = new JButton ( " E n v i a r " ) ;27 j b E n v i a r . a d d A c t i o n L i s t e n e r (new Escuchador ( ) ) ;28 miEntrada . add ( j b E n v i a r ) ;29 JPanel suEntrada = new JPanel ( ) ;30 j l E l D i c e = new JLabel ( " E s c u c h a n d o .... " ) ;31 suEntrada . add ( j l E l D i c e ) ;32 c o n t e nedor . add ( suEntrada , Bor<strong>de</strong>rLayout .SOUTH) ;33 c o n t e nedor . add ( miEntrada , Bor<strong>de</strong>rLayout .NORTH) ;34 setContentPane ( c o ntenedor ) ;35 }3637 public void r e c i b e M e n s a j e ( S t r i n g mensaje ) {38 j l E l D i c e . s etText ( " M e n s a j e : " + mensaje ) ;39 r e p a i n t ( ) ;40 }4142 private c l a s s Escuchador implements A c t i o n L i s t e n e r {43 @Overri<strong>de</strong>44 public void a c t i o n P e r f o r m e d ( ActionEvent e ) {45 i f ( e l O t r o == null )46 e l O t r o = ( ComunicacionApplets ) getAppletContext ( ) . g etApplet (getParameter ( " E l O t r o " ) ) ;47 e l O t r o . r e c i b e M e n s a j e ( j t f T u D i c e s . getText ( ) ) ;48 }49 }5051 }Listado 12.3: Código HTML <strong>de</strong> la página visualizada en la Figura 12.21 2 3 < t i t l e >Conversación e n t r e Applets 4 56 7 13 16 S i pue<strong>de</strong>s ver e s t o tu navegador no s o p o r t a <strong>Java</strong> .17 18 19 25


12.6.COMUNICACIÓN ENTRE APPLETS 179Figura 12.2: Llamada a los métodos <strong>de</strong> un Applet durante su ciclo <strong>de</strong> vida.27 v a l u e=" S u p e r i o r "/>28 S i pue<strong>de</strong>s ver e s t o tu navegador no s o p o r t a <strong>Java</strong> .29 30 31 Listado 12.4: Applet <strong>con</strong> la aplicación <strong>de</strong>l cálculo <strong>de</strong> la cuota mensual <strong>de</strong> unahipotecaEjercicios.1. Convierte la aplicación <strong>de</strong> la Agenda en un Applet para que puedas interaccionar<strong>con</strong> ella a través <strong>de</strong> un navegador web.Lecturas recomendadas.En esta dirección web http://download.oracle.com/javase/tutorial/<strong>de</strong>ployment/applet/in<strong>de</strong>x.html podrás en<strong>con</strong>trar toda la informaciónnecesaria para programar Applets.


180CAPÍTULO 12. APPLETS


Capítulo 13Control <strong>de</strong> errores <strong>con</strong>MyLyn y BugzillaContenidos13.1. Sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> tareas MyLyn . . . . . . . . 18213.1.1. Cual es el objetivo <strong>de</strong> MyLyn . . . . . . . . . . . . . 18213.1.2. Trabajar <strong>con</strong> MyLyn . . . . . . . . . . . . . . . . . . 18213.1.2.1. Trabajar <strong>con</strong> tareas . . . . . . . . . . . . . 18213.1.2.2. Trabajar <strong>con</strong> categorías . . . . . . . . . . . 18613.1.2.3. Trabajar <strong>con</strong> Working Sets . . . . . . . . . 18713.2. Sistema <strong>de</strong> gestión <strong>de</strong> errores Bugzilla . . . . . . . 18813.2.1. Cual es el objetivo <strong>de</strong> Bugzilla . . . . . . . . . . . . 18813.2.2. Instalación <strong>de</strong> Bugzilla . . . . . . . . . . . . . . . . . 18813.2.3. Trabajar <strong>con</strong> Bugzilla . . . . . . . . . . . . . . . . . 19513.3. Acceso a Bugzilla <strong>de</strong>s<strong>de</strong> MyLyn y Eclipse . . . . . 19913.3.1. Beneficios <strong>de</strong> la combinación <strong>de</strong> Bugzilla y MyLyn<strong>de</strong>s<strong>de</strong> Eclipse . . . . . . . . . . . . . . . . . . . . . . 20113.3.2. Trabajo <strong>con</strong> MyLyn y Bugzilla <strong>de</strong>s<strong>de</strong> Eclipse . . . . 20113.3.2.1. Añadir errores a Bugzilla <strong>de</strong>s<strong>de</strong> Eclipse . . 20113.3.2.2. Recuperar errores <strong>de</strong>s<strong>de</strong> Bugzilla como tareasen MyLyn . . . . . . . . . . . . . . . . 201IntroducciónLos Entornos <strong>de</strong> <strong>Desarrollo</strong> Integrado son una excelente herramienta para lacreación y mantenimiento <strong>de</strong> código. Sin embargo, cuando se trabaja en <strong>proyectos</strong>cuyo código está organizado en un gran número <strong>de</strong> clases <strong>de</strong>ntro <strong>de</strong> suscorrespondiente paquetes, navegar por le código para en<strong>con</strong>trar un método ouna clase pue<strong>de</strong> llegar a <strong>con</strong>sumir mucho tiempo. MyLyn es una herramienta,integrada en Eclipse a través <strong>de</strong> un plug-in, que oculta el código que no es relevantea la tarea que estamos llevando a cabo. Aunque MyLyn es mucho másque eso.181


182CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAPor otro lado, una tarea importante en todo proyecto informática es el seguimientoy gestión <strong>de</strong> errores en el código. Bugzilla es una excelente herramientaque cumple esta función.Quizás, lo mejor <strong>de</strong> ambas herramientas es la posibilidad <strong>de</strong> combinarlas<strong>de</strong>ntro <strong>de</strong> Eclipse <strong>de</strong> tal manera que po<strong>de</strong>mos inicial el seguimiento <strong>de</strong> un erroren Bugzilla <strong>de</strong>s<strong>de</strong> el propio Eclipse y darlo <strong>de</strong> alta como una tarea en MyLynpara po<strong>de</strong>r centrar nuestra atención sólo en el código relativo a ese error.13.1. Sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> tareas MyLynMyLyn es una potente herramienta que se encuentra en el paquete básico <strong>de</strong>Eclipse. Al <strong>de</strong>scargarnos Eclipse nos estamos <strong>de</strong>scargando también esta herramienta.13.1.1. Cual es el objetivo <strong>de</strong> MyLynCuando trabajamos en equipo y en gran<strong>de</strong>s <strong>proyectos</strong> es usual que el códigofuente <strong>de</strong>l proyecto esté organizado en un gran número <strong>de</strong> paquetes, <strong>de</strong>ntro <strong>de</strong>los cuales en<strong>con</strong>tramos un gran número <strong>de</strong> clases. El número total <strong>de</strong> ficheros<strong>con</strong> código fuente pue<strong>de</strong> ser muy elevado. Generalmente, cuando <strong>de</strong>sarrollamosuna tarea <strong>de</strong>ntro <strong>de</strong> un proyecto <strong>de</strong> gran envergadura, sólo son relevantes a estatarea un número reducido <strong>de</strong> ficheros frente al total. Si el entorno <strong>de</strong> <strong>de</strong>sarrolloque estamos utilizando nos presenta todos los ficheros <strong>de</strong>l proyecto, po<strong>de</strong>mosper<strong>de</strong>r bastante tiempo navegando por ellos y cribando los que sí son relevantesa nuestra tarea.En esencia, el objetivo <strong>de</strong> MyLyn es permitir <strong>con</strong>centrarnos sólo en el código<strong>de</strong> un proyecto sobre el que estamos trabajando, ocultando el resto <strong>de</strong>l código <strong>de</strong>lproyecto que no es relevante a la tarea que estamos llevando a cabo. Pero MyLynno es sólo eso, si no un sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> trabajo que po<strong>de</strong>mos utilizar bien <strong>de</strong>forma local o bien en <strong>con</strong>exión <strong>con</strong> un sistema <strong>de</strong> <strong>con</strong>trol <strong>de</strong> errores. Y es en esteúltimo caso cuando Bugzilla se <strong>con</strong>vierte en una herramienta imprescindible.En MyLyn la unidad básica <strong>de</strong> trabajo es la tarea. Las tareas se pue<strong>de</strong>norganizar en categorías y estas a su vez se pue<strong>de</strong>n agrupar en grupos <strong>de</strong> trabajo.Veamos cómo trabajar <strong>con</strong> cada uno <strong>de</strong> estos nuevos <strong>con</strong>ceptos.13.1.2. Trabajar <strong>con</strong> MyLynMyLyn dispone <strong>de</strong> una vista propia en Eclipse. El nombre <strong>de</strong> esta vista es TaskList. Si no aparece esta vista por <strong>de</strong>fecto, pue<strong>de</strong>s hacerla visible seleccionandola opción que se muestra en la Figura 13.1.La nueva vista que se abrirá en Eclipse se muestra en la Figura 13.2.13.1.2.1. Trabajar <strong>con</strong> tareasLo primero que vamos a hacer es crear una nueva tarea, para ello, <strong>de</strong>spliega eli<strong>con</strong>o que se muestra en la Figura 13.3 y selecciona la opción New Task....A <strong>con</strong>tinuación se nos abrirá la ventana que se muestra en la Figura 13.4don<strong>de</strong> <strong>de</strong>bemos seleccionar la opción por <strong>de</strong>fecto Local. Lo que estamos haciendoen este punto es indicarle a MyLyn que la tarea que estamos creando es local a


13.1. SISTEMA DE CONTROL DE TAREAS MYLYN 183Figura 13.1: Opción <strong>de</strong> menú para abrir la vista <strong>de</strong> MyLyn.Figura 13.2: Aspecto <strong>de</strong> la vista Task List <strong>de</strong> MyLyn.


184CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.3: Creación <strong>de</strong> una nueva tarea en MyLyn.nuestra máquina <strong>de</strong> <strong>de</strong>sarrollo, no estamos utilizando ningún repositorio externo.La creación <strong>de</strong> tareas en repositorios externos la cubriremos en la sección 13.3.Tras seleccionar el repositorio local, se nos abrirá la ventana mostrada en laFigura 13.5 don<strong>de</strong> po<strong>de</strong>mos introducir las propieda<strong>de</strong>s <strong>de</strong> la nueva tarea.En esta ventana po<strong>de</strong>mos <strong>de</strong>finir las siguiente propieda<strong>de</strong>s para la nuevatarea:Nombre <strong>de</strong> la tarea.Status: el estado <strong>de</strong> la tarea, que pue<strong>de</strong> ser Completo o Incompleto.Scheduled: cuando está planificado que trabajaremos en la tarea.Due: en que fecha <strong>de</strong>be estar completada la tarea.Estimate: número <strong>de</strong> horas estimadas <strong>de</strong> <strong>de</strong>dicación a la tarea.Campo <strong>de</strong> comentarios.Una vez <strong>de</strong>finidas estas propieda<strong>de</strong>s <strong>de</strong> la nueva tarea, po<strong>de</strong>mos guardarla,y al hacerlo, veremos que se actualiza la vista Task List <strong>con</strong> la nueva tarea,que aparecerá en la carpeta Uncategorized. En esta carpeta se aña<strong>de</strong>n, <strong>de</strong> modoautomático, todas las tareas que no se han asignado a ninguna categoría.Antes <strong>de</strong> ver cómo crear nuevas categorías veamos cual es el trabajo básico<strong>con</strong> las tareas. MyLyn nos permite <strong>con</strong>centrarnos sólo en el código que necesitamospara <strong>de</strong>sarrollar una <strong>de</strong>terminada tarea. Para activar una tarea pulsa eli<strong>con</strong>o <strong>con</strong> aspecto <strong>de</strong> círculo, <strong>con</strong> relleno blanco la primera vez, que se encuentraa la izquierda <strong>de</strong>l nombre <strong>de</strong> la tarea. Verás que ocurren varios cambios en Eclipse,por un lado, el aspecto <strong>de</strong>l i<strong>con</strong>o a la izquierda <strong>de</strong> la tarea cambia <strong>de</strong> aspectopara mostrar una esfera sombreada en gris. Y lo que es más importante, la vistaPackage Explorer ha ocultado toda la información relativa a la organización <strong>de</strong>los paquetes y las clases en ella <strong>con</strong>tenida. Observa también que el botón <strong>de</strong>tarea activa <strong>de</strong> MyLyn mostrado en la Figura 13.6 está pulsado.¿Qué ha ocurrido al activar la tarea? ¿Por qué han <strong>de</strong>saparecido todas lasclases <strong>de</strong> la vista Package Explorer? Como hemos comentado, MyLyn nos permite<strong>con</strong>centrarnos sólo en el código relacionado <strong>con</strong> la tarea que activa en cadamomento. ¿Cómo lo <strong>con</strong>sigue? Ocultando el código que no hemos modificado o<strong>con</strong>sultado en el <strong>de</strong>sarrollo <strong>de</strong> la tarea. Como es la primera vez que activamos latarea, y no hemos modificado o <strong>con</strong>sultado ningún código, MyLyn oculta todala jerarquía <strong>de</strong> paquetes. Para <strong>de</strong>sactivar el filtro, pulsa sobre el i<strong>con</strong>o mostradoen la Figura 13.6, se mostrará <strong>de</strong> nuevo la jerarquía completa <strong>de</strong> clases. Abre


13.1. SISTEMA DE CONTROL DE TAREAS MYLYN 185Figura 13.4: Selección <strong>de</strong>l repositorio don<strong>de</strong> se añadirá la nueva tarea.Figura 13.5: Definición <strong>de</strong> las propieda<strong>de</strong>s <strong>de</strong> una nueva tarea en MyLyn.


186CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.6: Tarea activa en MyLyn MyLyn.el código <strong>de</strong> una clase y activa <strong>de</strong> nuevo el filtro. Verás que todas las clases seocultan excepto aquella que aparece en el editor. Realiza algún cambio en elcódigo <strong>de</strong> la clase, como añadir algún comentario, verás que en la vista PackageExplorer aparecen sólo los métodos en los que has añadido comentarios, el resto<strong>de</strong> métodos <strong>de</strong> la clase permanece oculto. Esta es la base <strong>de</strong> la potencia <strong>de</strong>MyLyn presentar sólo el código <strong>de</strong> las clases que hemos modificado o <strong>con</strong>sultadoal <strong>de</strong>sarrollar una <strong>de</strong>terminada tarea.Observa que en el editor también aparece el i<strong>con</strong>o <strong>de</strong> filtro <strong>de</strong> MyLyn, si loactivas, los métodos sobre los que no has trabajado al <strong>de</strong>sarrollar la tarea secolapsarán, sólo se mostrará el código <strong>de</strong> aquellos métodos que has modificadoo <strong>con</strong>sultado.Finalmente, si <strong>de</strong>sactivas la tarea, pulsando en el i<strong>con</strong>o a la izquierda <strong>de</strong> latarea en la vista Task List, Eclipse te mostrará todos los <strong>proyectos</strong>, paquetesy clases en la vista Package Explorer. Si activas <strong>de</strong> nuevo la tarea, Eclipse temostrará sólo el código relacionado <strong>con</strong> ella. MyLyn recuerda el estado en quequedó la información <strong>de</strong> trabajo relacionada <strong>con</strong> la tarea en el momento <strong>de</strong> su<strong>de</strong>sactivación.13.1.2.2. Trabajar <strong>con</strong> categoríasLas categorías son agrupaciones <strong>de</strong> tareas. Una tarea sólo pue<strong>de</strong> pertenecera una categoría. Las categorías nos sirven para estructurar las tareas. Paraun proyecto <strong>con</strong>creto po<strong>de</strong>mos, por ejemplo, crear una categoría relativa a lastareas relacionadas <strong>con</strong> la creación <strong>de</strong>l interface gráfico <strong>de</strong> usuario, otra categoríarelativa a las tareas relacionadas <strong>con</strong> el mo<strong>de</strong>lo <strong>de</strong> datos <strong>de</strong> nuestra aplicacióny así sucesivamente.Para crear una nueva categoría <strong>de</strong>spliega la opción <strong>de</strong> creación <strong>de</strong> nuevoselementos en la vista Task List y selecciona la opción New Cathegory tal ycomo muestra la Figura 13.7.Como nombre <strong>de</strong> la categoría introduzcamos, por ejemplo, Mo<strong>de</strong>lo <strong>de</strong> datos.Al pulsar el botón Ok observarás que se ha creado una carpeta <strong>con</strong> ese nombre enla vista Task List. Para añadir, o cambiar <strong>de</strong> ubicación, una tarea ya existente,simplemente pincha sobre la tarea y arrástrala hasta la categoría <strong>de</strong>seada. Verásque la <strong>de</strong>scripción <strong>de</strong> la tarea se actualiza para dar cuenta <strong>de</strong> la categoría a laque pertenece.Para crear una tarea <strong>de</strong>ntro <strong>de</strong> una <strong>de</strong>terminada categoría, simplementeselecciona la categoría antes <strong>de</strong> crear la tarea. Por <strong>de</strong>fecto, la tarea se creará enla categoría actualmente seleccionada.


13.1. SISTEMA DE CONTROL DE TAREAS MYLYN 187Figura 13.7: Creación <strong>de</strong> una nueva categoría en MyLyn.Figura 13.8: Creación <strong>de</strong> un Workin Set en MyLyn.13.1.2.3. Trabajar <strong>con</strong> Working SetsPor encima <strong>de</strong> las categorías existe un nuevo escalón que nos permite organizarlasen nuevos grupos. Fíjate que las tareas sólo pue<strong>de</strong>n pertenecer a una únicacategoría, una tarea no pue<strong>de</strong> estar en más <strong>de</strong> una categoría. Los Working Setsnos permiten agrupar categorías, y una categoría pue<strong>de</strong> pertenecer a más <strong>de</strong> unWorking Set. La pertenencia <strong>de</strong> una categoría a un Working Set no es exclusiva.Supongamos que tenemos una jerarquía <strong>de</strong> tareas y categorías como la mostradaen la Figura 13.8.Pulsa el triángulo negro a la izquierda <strong>de</strong>l enlace All para <strong>de</strong>splegar lasopciones y selecciona Edit. Se abrirá la ventana <strong>de</strong> la Figura 13.9.Pulsa el botón New para crear un nuevo Working Set, se abrirá la ventanamostrada en la Figura 13.10. Introduce un nombre para este nuevo Working Sety selecciona las categorías Vista y Mo<strong>de</strong>lo <strong>de</strong> datos. Para acabar pulsa el botónFinish. Volverás a la ventana <strong>de</strong> la Figura 13.8 pero esta vez podrá ver el nuevoWorking Set recién creado, pulsa el botón Ok. Ahora, si <strong>de</strong>spliegas <strong>de</strong> nuevo lalista <strong>de</strong> Working Sets (triángulo a la izquierda <strong>de</strong>l enlace All <strong>de</strong> la Figura 13.8verás el nombre <strong>de</strong>l nuevo Working Set, si lo seleccionas sólo te aparecerán lascategorías Mo<strong>de</strong>lo <strong>de</strong> datos y Vista que son las que has incluido en este Working


188CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.9: Selección y edición <strong>de</strong> un Workin Set en MyLyn.Set, el resto <strong>de</strong> categorías no se visualiza.Pue<strong>de</strong>s probar a crear un nuevo Working Set, llámalo Relación entre Mo<strong>de</strong>loy Controlador y aña<strong>de</strong> a él sólo las categorías Mo<strong>de</strong>lo y Controlador. Como ves,la categoría Mo<strong>de</strong>lo está <strong>de</strong>ntro <strong>de</strong> dos Working Sets.13.2. Sistema <strong>de</strong> gestión <strong>de</strong> errores BugzillaBugzilla es una herramienta libre y <strong>de</strong> código abierto utilizada por gran<strong>de</strong>scompañías, como Mozilla, Apache o Eclipse. Se pue<strong>de</strong> trabajar <strong>con</strong> Bugzilla<strong>de</strong>s<strong>de</strong> un navegador web o bien <strong>de</strong>s<strong>de</strong> un cliente como MyLyn si se dispone <strong>de</strong>l<strong>con</strong>ector a<strong>de</strong>cuado. Afortunadamente, y no es <strong>de</strong> extrañar, Eclipse proporcionapor <strong>de</strong>fecto este <strong>con</strong>ector.13.2.1. Cual es el objetivo <strong>de</strong> BugzillaBugzilla es un sistema <strong>de</strong> seguimiento <strong>de</strong> errores. Durante el ciclo <strong>de</strong> vida <strong>de</strong>lsoftware, una <strong>de</strong> las etapas básicas es la <strong>de</strong>tección y solución <strong>de</strong> errores. Bugzillanos permite gestionar y automatizar el seguimiento <strong>de</strong> errores hasta su resoluciónfinal.13.2.2. Instalación <strong>de</strong> BugzillaEn el momento <strong>de</strong> escribir estas página, la última versión estable <strong>de</strong> Bugzilla esla 4.0, que se pue<strong>de</strong> <strong>de</strong>scargar <strong>de</strong>s<strong>de</strong> http://www.bugzilla.org.En esta sección se muestra cómo instalar Bugzilla en Ubuntu 10.10. Paraotros sistemas operativos o versiones <strong>de</strong> Linux <strong>con</strong>sultar los <strong>de</strong>talles en la páginaweb <strong>de</strong> Bugzilla.


13.2. SISTEMA DE GESTIÓN DE ERRORES BUGZILLA 189Figura 13.10: Propieda<strong>de</strong>s <strong>de</strong> un Workin Set en MyLyn.Antes <strong>de</strong> instalar Bugzilla necesitamos instalar algunos paquetes <strong>de</strong> los que<strong>de</strong>pen<strong>de</strong> Bugzilla. El primero <strong>de</strong> ellos es perl, para comprobar si tenemos perlinstalado abrimos un terminal y escribimos:$perl -vThis is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multiCopyright 1987-2009, Larry WallPerl may be copied only un<strong>de</strong>r the terms of either the ArtisticLicense or the GNU General Public License, which may be foundin the Perl 5 source kit.Complete documentation for Perl, including FAQ lists, should befound on this system using "man perl" or "perldoc perl". If youhave access to the Internet, point your browser athttp://www.perl.org/, the Perl Home Page.Si po<strong>de</strong>mos ver lo anterior es que tenemos la versión 5.10.1 <strong>de</strong> perl instalada,Bugzilla necesita al menos la versión 5.8. Si perl no está instalado lo po<strong>de</strong>mosinstalar tecleando en el terminal:$sudo apt-get install perlFíjate que para instalar nuevos paquetes en linux necesitamos permiso <strong>de</strong>superusuario, <strong>de</strong> ahí que utilicemos sudo. Antes <strong>de</strong> proce<strong>de</strong>r a la instalación senos preguntará por la clave <strong>de</strong> superusuario.


190CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLALo siguiente que <strong>de</strong>bemos instalar, si no lo tenemos ya instalado, es un gestor<strong>de</strong> bases <strong>de</strong> datos. Vamos a utilizar MySQL aunque también es posible utilizarPostgreSQL. Para comprobar si MySQL está instalado escribimos <strong>de</strong> nuevo enun terminal:$mysql --versionmysql Ver 14.14 Distrib 5.1.41, for <strong>de</strong>bian-linux-gnu (i486)using readline 6.1Lo anterior nos indica que tenemos instalada la versión 5.1.41 <strong>de</strong> este gestor<strong>de</strong> bases <strong>de</strong> datos. Si no lo tenemos, lo po<strong>de</strong>mos instalar tecleando:apt-get install mysql-admin mysql-client mysql-serverLo anterior nos instalará tanto el gesto <strong>de</strong> bases <strong>de</strong> datos como el clientey las herramientas <strong>de</strong> administración. Durante la instalación <strong>de</strong> MySQL se nospedirá que <strong>de</strong>finamos la clave <strong>de</strong> acceso <strong>de</strong>l administrador. Esta clave <strong>de</strong> accesola utilizaremos más tar<strong>de</strong> en la <strong>con</strong>figuración <strong>de</strong> Bugzilla.El siguiente paso es comprobar si tenemos instalado el servidor web Apache.Para ello, <strong>de</strong> nuevo, escribe en un terminal:$apache2 -vServer version: Apache/2.2.16 (Ubuntu)Server built: Nov 18 2010 21:17:43En este caso, el mensaje nos indica que tenemos instalado el servidor webApache en nuestro sistema en su versión 2.2.16. Si no fuese el caso lo pue<strong>de</strong>sinstalar tecleando en el terminal$sudo apt-get install apache2En este punto, ya tenemos instalados todos los paquetes necesarios parapo<strong>de</strong>r trabajar <strong>con</strong> Bugzilla. Lo siguiente es <strong>de</strong>scargar y <strong>con</strong>figurar Bugzilla.El directorio por <strong>de</strong>fecto don<strong>de</strong> el servidor web Apache busca los ficherossolicitados es, en el caso <strong>de</strong> la distribución <strong>de</strong> Linux Ubuntu 10.10, /var/www 1 .Sitúate en ese directorio y <strong>de</strong>scarga Bugzilla escribiendo en un terminal lo siguiente:$sudo wget http://ftp.mozilla.org/pub/mozilla.org/webtools/bugzilla-4.0.tar.gzAl acabar la <strong>de</strong>scarga verás que tienes un fichero comprimido <strong>con</strong> nombrebugzilla-4.0.tar.gz. Para <strong>de</strong>scomprimir este fichero escribe en el terminal:$ tar -xvf bugzilla-4.0.tar.gzObservarás que se ha creado el directorio bugzilla-4.0. El siguiente paso paratener Bugzilla a punto es instalar los módulos perl que necesita Bugzilla, paraello sitúate en el directorio /var/www/bugzilla-4.0 y escribe en el terminal:$sudo ./checksetup.pl ?check-modules1 Para otras distribuciones <strong>de</strong> Linux y sistemas operativos <strong>con</strong>sultar la documentación.


13.2. SISTEMA DE GESTIÓN DE ERRORES BUGZILLA 191Te aparecerá una lista <strong>con</strong> los módulos necesarios y los opcionales que necesitaBugzilla. Tal y como se indica al final <strong>de</strong> la lista <strong>de</strong> módulos, pue<strong>de</strong>s instalartodos los necesarios escribiendo en el terminal:$sudo /usr/bin/perl install-module.pl ?allPara comprobar que tenemos todos los módulos necesarios instalados escribe<strong>de</strong> nuevo en el terminal:$sudo ./checksetup.pl ?check-modulesSi alguno <strong>de</strong> los módulos necesarios no se ha instalado escribe <strong>de</strong> nuevo enun terminal:$sudo /usr/bin/perl install-module.pl ?allEn este punto necesitamos instalar los módulos perl necesarios para Apache.Los instalamos escribiendo en el terminal:$sudo apt-get install libapache2-mod-perl2libapache2-mod-perl2-<strong>de</strong>vlibapache2-mod-perl2-doc$ sudo /usr/bin/perl install-module.pl Apache2::SizeLimitCon todo esto ya po<strong>de</strong>mos <strong>con</strong>tinuar <strong>con</strong> la instalación <strong>de</strong> Bugzilla, escribeen el terminal:$sudo ./checksetup.plCon ello, entre otras cosas, se habrá creado el fichero local<strong>con</strong>fig en eldirectorio /var/www/bugzilla-4.0. Edita este fichero y modifica las siguientevariables <strong>de</strong> Bugzilla:$webservergroup = ?www-data?;$db_pass = ?clave <strong>de</strong> administrado <strong>de</strong> MySQL?;El siguiente paso es crear la base <strong>de</strong> datos que manejará Bugzilla. Para crearesta base <strong>de</strong> datos utilizaremos el cliente <strong>de</strong> MySQL que ya hemos instalado.Escribe en un terminal:$ sudo mysql -u root -pSe te solicitará la clave <strong>de</strong> administrador que has introducido en la instalación<strong>de</strong> MySQL. Una vez introducida <strong>con</strong> éxito nos en<strong>con</strong>traremos en el ambiente <strong>de</strong>lcliente <strong>de</strong> MySQL. Para crear la base <strong>de</strong> datos que manejará Bugzilla escribe:mysql> create database bugs;Query OK, 1 row affected (0.00 sec)Para ver todas las bases <strong>de</strong> datos que gestiona MySQL escribe:


192CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAmysql> show databases;+-----------+| Database |+-----------+| information_schema || bugs || mysql |+-----------+3 rows in set (0.00 sec)Verás que efectivamente tenemos la base <strong>de</strong> datos llamada bugs creada. Losiguiente es asignar todos los privilegios para trabajar <strong>con</strong> esta base <strong>de</strong> datos aladministrador root, para ello escribe:mysql> GRANT ALL PRIVILEGES ON bugs.* TO root@localhostIDENTIFIED BY ?clave <strong>de</strong> administrador?;mysql> FLUSH PRIVILEGES;Para salir <strong>de</strong>l cliente <strong>de</strong> MySQL escribe:mysql> quit;De regreso al terminal vamos a <strong>con</strong>tinuar <strong>con</strong> la instalación <strong>de</strong> Bugzilla,escribe lo siguiente en el terminal:$ ./checksetup.plEste script <strong>de</strong> perl creará, entre otras cosas, todas las tablas necesarias enla base <strong>de</strong> datos bugs y <strong>con</strong>figurará Bugzilla. También se nos pedirá que introduzcamosel correo electrónico y clave <strong>de</strong> acceso <strong>de</strong>l Administrador <strong>de</strong> Bugzilla.El nombre <strong>de</strong> usuario en Bugzilla <strong>de</strong>be ser un correo electrónico.El siguiente paso es <strong>con</strong>figurar Apache para que re<strong>con</strong>ozca el directorio don<strong>de</strong>tenemos instalado Bugzilla, para ello sitúate en el directorio /etc/apache2 2 yedita el fichero httpd.<strong>con</strong>f y escribe en él lo siguiente:Alias /bugzilla "/var/www/bugzilla-4.0"AddHandler cgi-script .cgiOptions +In<strong>de</strong>xes +ExecCGIDirectoryIn<strong>de</strong>x in<strong>de</strong>x.cgiAllowOverri<strong>de</strong> LimitEn este punto sólo nos queda reiniciar el servidor web Apache escribiendo losiguiente en el terminal:$sudo /etc/init.d/apache2 restartY si no tenemos ningún problema ya po<strong>de</strong>mos abrir un navegador web para<strong>con</strong>ectarnos a Bugzilla escribiendo como url la siguiente http://localhost/


13.2. SISTEMA DE GESTIÓN DE ERRORES BUGZILLA 193Figura 13.11: Página <strong>de</strong> inicio <strong>de</strong> Bugzilla.Figura 13.12: Página <strong>de</strong> introducción <strong>de</strong> usuario y clave <strong>de</strong> Bugzilla.bugzilla. Deberemos ver la página <strong>de</strong> inicio <strong>de</strong> Bugzilla tal y como se muestraen la siguiente Figura 13.11:Una vez ingresado en Bugzilla utilizando el correo electrónico <strong>de</strong>l administradorcomo usuario y la clave que hemos <strong>de</strong>finido en la instalación <strong>de</strong> <strong>de</strong> Bugzilla,se presentará una pantalla como la mostrada en la Figura 13.12, seleccionamosel enlace Administration para acabar <strong>de</strong> <strong>con</strong>figurar Bugzilla.En la nueva pantalla mostrada en la Figura 13.13 seleccionamos el enlaceParameters lo que nos dará paso a la pantalla mostrada en la Figura 13.14En esta pantalla, los únicos parámetros que es necesario <strong>con</strong>figurar sonurlbase y cookiepath. Como valor <strong>de</strong> urlbas escribiremos la url <strong>de</strong> nuestroservidor, en el ejemplo se muestra http://localhost/bugzilla/ lo queindica que estamos utilizando Bugzilla en el servidor local, no tendremos accesoa este servidor <strong>de</strong>s<strong>de</strong> otra máquina. Si Bugzilla estuviese instalado en2 El directorio don<strong>de</strong> se encuentra el fichero httpd.<strong>con</strong>f pue<strong>de</strong> ser distinto <strong>de</strong>pendiendo <strong>de</strong>la distribución <strong>de</strong> Linux o <strong>de</strong>l sistema operativo.


194CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.13: Página <strong>de</strong> administración <strong>de</strong> Bugzilla.Figura 13.14: Página <strong>de</strong> <strong>de</strong>finición <strong>de</strong> parámetros <strong>de</strong> Bugzilla.


13.2. SISTEMA DE GESTIÓN DE ERRORES BUGZILLA 195Figura 13.15: Lista <strong>de</strong> usuarios en Bugzilla.un servidor <strong>con</strong> nombre mi.servidor.com el valor <strong>de</strong>l parámetro urlbase seríahttp://mi.servidor.com/bugzilla/. Como valor <strong>de</strong> cookiepath escribiremos/bugzilla/. Finalmente pulsamos el botón Sava changes.13.2.3. Trabajar <strong>con</strong> BugzillaComo ya se hemos comentado, Bugzilla es un sistema <strong>de</strong> gestión <strong>de</strong> errores <strong>con</strong>interfaz web. El núcleo <strong>de</strong> trabajo <strong>con</strong> Bugzilla es el proyecto. En Bugzilla sepue<strong>de</strong>n crear tantos <strong>proyectos</strong> como sea necesario. Una vez creado un proyecto,todos los usuarios que estén autorizados a dar <strong>de</strong> alta los errores en<strong>con</strong>tradosen el proyecto pue<strong>de</strong>n empezara a hacerlo.Por lo tanto, lo primero que vamos a hacer es crear un nuevo proyecto.Después crearemos un usuario y le asignaremos privilegios para dar <strong>de</strong> alta loserrores en<strong>con</strong>trados en el proyecto recién creado.Todo este proceso lo vamos a hacer <strong>con</strong> la cuenta <strong>de</strong> Administrador <strong>de</strong> Bugzilla.Ingresa en Bugzilla pulsando sobre el enlace Log In que se muestra en laFigura 13.11, se te solicitará un nombre <strong>de</strong> usuario y su clave <strong>de</strong> acceso. Comoya hemos comentado, los nombre <strong>de</strong> usuario en Bugzilla <strong>de</strong>ben ser direcciones<strong>de</strong> correo electrónico, <strong>de</strong>bemos introducir la dirección <strong>de</strong> correo electrónico <strong>de</strong>Administrador que introdujimos en la <strong>con</strong>figuración <strong>de</strong> Bugzilla y su clave.Como en el caso <strong>de</strong> la <strong>con</strong>figuración <strong>de</strong> los parámetros <strong>de</strong> Bugzilla, seleccionamosen enlace Administration y esta vez, <strong>de</strong>ntro <strong>de</strong> esta pantalla, seleccionamosel enlace Users, lo que nos dará paso a la pantalla mostrada en la Figura 13.15,don<strong>de</strong> seleccionamos el enlace add a new user, se nos mostrará la pantalla <strong>de</strong>la Figura 13.16 don<strong>de</strong> introduciremos los datos <strong>de</strong>l nuevo usuario. Fíjate quecomo nombre <strong>de</strong> usuario se <strong>de</strong>be introducir una dirección <strong>de</strong> correo electrónico.Finalmente pulsamos el botón Add.Lo siguiente que vamos a hacer es crear un nuevo producto. Como ya se hacomentado, los errores se dan <strong>de</strong> alta en un <strong>de</strong>terminado producto, pue<strong>de</strong>s pensarque los productos son tus <strong>proyectos</strong>. Para crear un nuevo producto, vuelve a lapágina Administration y selecciona el enlace Products, verás una página comola <strong>de</strong> la Figura 13.17, por <strong>de</strong>fecto hay un producto <strong>de</strong> prueba creado, llamadoTestProduct. Vamos a añadir un nuevo producto selecciona el enlace Add loque te llevará a la nueva página mostrada en la Figura 13.18, introduce losparámetros <strong>de</strong>l nuevo producto y pulsa el botón Add, esto te llevará a la nuevapágina mostrada en la Figura 13.19, en esta página se nos indica que <strong>de</strong>bemoscrear al menos un componente <strong>de</strong>ntro <strong>de</strong>l nuevo producto, pue<strong>de</strong>s pensar quelos componentes <strong>de</strong> los productos son como las tareas <strong>de</strong> tu proyecto. Selecciona


196CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.16: Página <strong>de</strong> creación <strong>de</strong> un nuevo usuario en Bugzilla.Figura 13.17: Lista <strong>de</strong> productos <strong>de</strong> Bugzilla.el enlace Edit components, lo que te llevará a la página mostrada en la Figura13.20. Selecciona el enlace Add <strong>con</strong> lo que te llevará a la página <strong>de</strong> <strong>de</strong>finición<strong>de</strong>l nuevo componente mostrada en la Figura 13.21, introduce en ella los datos<strong>de</strong>l nuevo componente y pulsa el botón Add lo que te dará entrada <strong>de</strong> nuevo ala página don<strong>de</strong> se muestra los componentes <strong>de</strong> un producto. Ahora verás queel componente ComponentePrueba se ha añadido a la lista.Resumiendo lo que he hemos hecho hasta este momento:1. Hemos <strong>con</strong>figurado Bugzilla.2. Hemos creado un nuevo usuario.Figura 13.18: Propieda<strong>de</strong>s <strong>de</strong> un producto en Bugzilla.


13.2. SISTEMA DE GESTIÓN DE ERRORES BUGZILLA 197Figura 13.19: Producto recién creado en Bugzilla. Debemos crear al menos uncomponente en este producto.Figura 13.20: Lista <strong>de</strong> los componentes <strong>de</strong> un producto en Bugzilla.Figura 13.21: Propieda<strong>de</strong>s <strong>de</strong> un nuevo componente en Bugzilla.


198CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.22: Lista <strong>de</strong> los productos en Bugzilla en los que po<strong>de</strong>mos dar <strong>de</strong> altanuevos errores.Figura 13.23: Definición <strong>de</strong> los parámetros <strong>de</strong>l error que se está dando <strong>de</strong> altaen Bugzilla.3. Hemos creado un nuevo producto (proyecto).4. Hemos añadido un nuevo componente (tarea) al producto (proyecto).Ahora ya po<strong>de</strong>mos ingresar a Bugzilla <strong>con</strong> nuestro nuevo usuario y dar <strong>de</strong>alta un error en el componente recién creado. Para ello sal <strong>de</strong> Bugzilla ya quehemos ingresado como administradores, e ingresa <strong>de</strong> nuevo pero esta vez <strong>con</strong> elusuario y clave recién creados. En la página <strong>de</strong> inicio <strong>de</strong> Bugzilla selecciona eli<strong>con</strong>o <strong>con</strong> leyenda File a Bug, lo que te llevará a la nueva página mostrada en laFigura 13.22 don<strong>de</strong> seleccionarás el producto don<strong>de</strong> quieres dar <strong>de</strong> alta el error.En nuestro caso, seleccionamos el enlace NuevoProducto, lo que nos llevará ala página mostrada en la Figura 13.23 y pulsa el botón Submit Bug, lo que tedará paso a la página mostrada en la Figura 13.24 don<strong>de</strong> se muestran todos los<strong>de</strong>talles <strong>de</strong>l error recién creado. Esta última <strong>con</strong>tiene todos los <strong>de</strong>talles <strong>de</strong>l error,y a medida que trabajemos sobre este error y añadamos nueva información, todaella aparecerá en esta pantalla.Para completar esta breve introducción al uso <strong>de</strong> Bugzilla realicemos unabúsqueda sobre la base <strong>de</strong> datos <strong>de</strong> errores. Para ello vuelve a la página inicial<strong>de</strong> Bugzilla y selecciona el i<strong>con</strong>o que tiene como leyenda Search, y en la nuevapágina seleccionemos el enlace Advanced Search, en este momento se nos mostrarála página <strong>de</strong> la Figura 13.25. En esta página seleccionemos el producto


13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE 199Figura 13.24: Información sobre el error recién creado en Bugzilla.NuevoProducto y <strong>de</strong>jemos el resto <strong>de</strong> campos tal y como están, esta <strong>con</strong>sulta seinterpreta como ✭✭Busca todos los errores dados <strong>de</strong> alta en el producto Nuevo-Producto✮✮, pulsa el botón Search y verá la página mostrada en la Figura 13.26<strong>con</strong> los resultados <strong>de</strong> la búsqueda. Sólo aparece un único error y al pulsar sobreel i<strong>de</strong>ntificador <strong>de</strong> este error volveremos a la página <strong>de</strong> información <strong>de</strong>talladasobre él.Hasta aquí hemos visto el trabajo básico <strong>con</strong> Bugzilla como una potentísimaherramienta <strong>de</strong> <strong>con</strong>trol y gestión <strong>de</strong> errores, pero sin duda, la potencia <strong>de</strong> estaherramienta se magnifica en combinación <strong>con</strong> MyLyn, tal y como se muestra enla siguiente sección.13.3. Acceso a Bugzilla <strong>de</strong>s<strong>de</strong> MyLyn y EclipseTanto MyLyn como Bugzilla son herramientas muy útiles en el <strong>de</strong>sarrollo <strong>de</strong>software utilizadas <strong>de</strong> modo in<strong>de</strong>pendiente. Pero sin duda, la posibilidad <strong>de</strong>combinar ambas soluciones en Eclipse nos facilita enormemente el <strong>de</strong>sarrollo <strong>de</strong>software y el seguimiento <strong>de</strong> errores.Hemos visto cómo MyLyn nos permite <strong>de</strong>finir tareas y <strong>con</strong>centrar nuestraatención únicamente en el código relacionado <strong>con</strong> ellas. Por otro lado, Bugzilla esun potente sistema <strong>de</strong> seguimiento <strong>de</strong> errores. Como <strong>de</strong>sarrolladores <strong>de</strong> software,algunas <strong>de</strong> nuestras tareas <strong>con</strong>sisten en solucionar errores en las aplicacionesque <strong>de</strong>sarrollamos, po<strong>de</strong>mos ver la resolución <strong>de</strong> cada error como una tareaespecífica. Sería muy interesante po<strong>de</strong>r combinar la potencia <strong>de</strong> MyLyn <strong>con</strong> la<strong>de</strong> Bugzilla para trabajar en la resolución <strong>de</strong> errores como si se tratase <strong>de</strong> otra


200CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.25: Búsqueda <strong>de</strong> errores en Bugzilla.Figura 13.26: Resultado <strong>de</strong> la búsqueda <strong>de</strong> errores en Bugzilla.


13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE 201tarea cualquiera.13.3.1. Beneficios <strong>de</strong> la combinación <strong>de</strong> Bugzilla y MyLyn<strong>de</strong>s<strong>de</strong> EclipseLa principal ventaja <strong>de</strong> po<strong>de</strong>r trabajar en Eclipse <strong>con</strong> Bugzilla a través <strong>de</strong>MyLyn es que po<strong>de</strong>mos realizar el seguimiento <strong>de</strong> los errores como si <strong>de</strong> otratarea cualquiera se tratase.En MyLyn po<strong>de</strong>mos <strong>de</strong>finir una <strong>con</strong>sulta ha Bugzilla <strong>de</strong> tal modo que al dar<strong>de</strong> alta un nuevo error este nos aparezca como una nueva tarea en MyLyn.Otra ventaja importante es que po<strong>de</strong>mos trabajar <strong>con</strong> Bugzilla directamente<strong>de</strong>s<strong>de</strong> Eclipse sin necesidad <strong>de</strong> abandonar el entorno <strong>de</strong> <strong>de</strong>sarrollo para realizarel seguimiento <strong>de</strong> los errores.13.3.2. Trabajo <strong>con</strong> MyLyn y Bugzilla <strong>de</strong>s<strong>de</strong> EclipseEn esta sección vamos a presentar los fundamentos <strong>de</strong>l trabajo <strong>con</strong>junto <strong>de</strong>s<strong>de</strong>Eclipse <strong>con</strong> MyLyn y Bugzilla.13.3.2.1. Añadir errores a Bugzilla <strong>de</strong>s<strong>de</strong> EclipsePara crear un nuevo error en Bugzilla <strong>de</strong>s<strong>de</strong> Eclipse crea una nueva tarea enla vista Task List tal y como se muestra en la Figura 13.3. Cuando se abrala ventana mostrada en la Figura 13.4 pulsa esta vez sobre el botón Add TaskRepository. Lo que vamos a hacer esta vez, es crear un repositorio hacia Bugzilla.Al pulsar sobre el botón Add Task Repository se abrirá la ventana mostrada enla Figura 13.27, selecciona Bugzilla y pulsa el botón Next 3 .En la nueva ventana que se abrirá, introduce los datos <strong>de</strong> <strong>con</strong>exión al repositorio<strong>de</strong> Bugzilla como se muestra en la Figura 13.28.Una vez rellenados los datos <strong>de</strong> <strong>con</strong>exión es útil pulsar el botón ValidateSettings para comprobar que se pue<strong>de</strong> establecer la <strong>con</strong>exión <strong>con</strong> el repositorio.Si la <strong>con</strong>exión se establece po<strong>de</strong>mos pulsar el botón Finish, <strong>de</strong> lo <strong>con</strong>trario,corrige los datos <strong>de</strong> <strong>con</strong>exión y vuelve a pulsar el botón Finish, verás la ventana<strong>de</strong> la Figura 13.29 don<strong>de</strong> aparecerá el repositorio recién creado Errores.Pulsa <strong>de</strong> nuevo el botón Finish <strong>con</strong> lo que se abrirá una solapa en Eclipsepara <strong>de</strong>finir las propieda<strong>de</strong>s <strong>de</strong>l nuevo error. Rellénala <strong>con</strong> los datos <strong>de</strong> un errortal y como muestra la Figura 13.30.Finalmente pulsa el botón Submit <strong>con</strong> lo que se enviará el nuevo error alrepositorio Bugzilla. Como ejercicio pue<strong>de</strong>s probar a buscar el nuevo error <strong>de</strong>s<strong>de</strong>un navegador web en Bugzilla.13.3.2.2. Recuperar errores <strong>de</strong>s<strong>de</strong> Bugzilla como tareas en MyLynPor último, veamos cómo po<strong>de</strong>mos crear una <strong>con</strong>sulta <strong>de</strong>s<strong>de</strong> MyLyn para que nosaparezcan como tareas en la vista Task List los errores presentes en el repositorio<strong>de</strong> Bugzilla. Para ello seleccionemos el botón New Query que se muestra en laFigura 13.3 y en la ventana que se abrirá seleccionemos el repositorio creado enla sección anterior Errores, y pulsemos el botón Next.3 Es posible que tengas que actualizar MyLyn para trabajar <strong>con</strong> la versión 4.0 <strong>de</strong> Bugzilla,<strong>con</strong>sulta la dirección http://www.eclipse.org/mylyn/new/ para <strong>con</strong>ocer los <strong>de</strong>talles.


202CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.27: Creación <strong>de</strong> un nuevo repositorio en MyLyn.Figura 13.28: Datos <strong>de</strong>l repositorio Bugzilla.


13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE 203Figura 13.29: Repositorio Errores recién creado.En la nueva ventana que se abrirá tenemos dos opciones:Create query using form.Create query from existing URL.La opción que nos interesa es la primera, que aparece seleccionada por <strong>de</strong>fecto,y pulsemos el botón Next.En la nueva ventana que se abrirá introduce los parámetros <strong>de</strong> la <strong>con</strong>sultatal y como muestra la Figura 13.31, y finalmente pulsa el botón Finish, en lavista Task List podrás ver los errores importados a MyLyn <strong>de</strong>s<strong>de</strong> Bugzilla, taly como se muestra en la Figura 13.32.Ahora podrás trabajar <strong>con</strong> ellos como <strong>con</strong> cualquier otra tarea local, a<strong>de</strong>más<strong>de</strong> disponer <strong>de</strong> todos los campos que ofrece el <strong>con</strong>ector a Bugzilla.Lecturas recomendadas.El capítulo 25 <strong>de</strong> la referencia [13] está <strong>de</strong>dicado enteramente a MyLyn.En la dirección http://wiki.eclipse.org/in<strong>de</strong>x.php/Mylyn/User_Gui<strong>de</strong>se pue<strong>de</strong> en<strong>con</strong>trar un manual en línea completo para el trabajo <strong>con</strong>MyLyn. En la dirección http://www.eclipse.org/mylyn/start/ se pue<strong>de</strong>nen<strong>con</strong>trar otros enlaces <strong>de</strong> gran interés como ví<strong>de</strong>o tutoriales sobreMyLyn, muy a<strong>con</strong>sejables <strong>de</strong> ver.


204CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLAFigura 13.30: Datos <strong>de</strong>l repositorio Bugzilla.El capítulo 27 <strong>de</strong> la referencia [13] está <strong>de</strong>dicado enteramente a BugzillaLa dirección web <strong>de</strong> Bugzilla es http://www.bugzilla.org/, allí podrásen<strong>con</strong>trar gran cantidad <strong>de</strong> información sobre esta herramienta.


13.3. ACCESO A BUGZILLA DESDE MYLYN Y ECLIPSE 205Figura 13.31: Datos <strong>de</strong>l repositorio Bugzilla.Figura 13.32: Datos <strong>de</strong>l repositorio Bugzilla.


206CAPÍTULO 13. CONTROL DE ERRORES CON MYLYN Y BUGZILLA


Capítulo 14Programación <strong>con</strong>currente<strong>con</strong> HilosContenidos14.1. ¿Qué es un hilo? Utilida<strong>de</strong>s. Consi<strong>de</strong>raciones sobreel uso <strong>de</strong> hilos . . . . . . . . . . . . . . . . . . . 20814.2. Creación <strong>de</strong> hilos en <strong>Java</strong> . . . . . . . . . . . . . . 20914.2.1. Creación <strong>de</strong> un Hilo extendiendo a la clase Thread . 20914.2.2. Creación <strong>de</strong> un Hilo mediante una clase interna . . . 21014.2.3. Creación <strong>de</strong> un Hilo mediante una clase internaanónima . . . . . . . . . . . . . . . . . . . . . . . . . 21114.3. Ciclo <strong>de</strong> vida <strong>de</strong> un hilo . . . . . . . . . . . . . . . 21214.4. Control <strong>de</strong> hilos . . . . . . . . . . . . . . . . . . . . 21314.5. Sincronización . . . . . . . . . . . . . . . . . . . . . 21514.5.1. Sincronizacón utilizando los cerrojos intrínsecos . . . 21514.5.2. Sincronización utilizando el interface Lock . . . . 218IntroducciónLa programación <strong>con</strong>currente es nativa en <strong>Java</strong>, esto quiere <strong>de</strong>cir que no necesitamosninguna biblioteca adicional para po<strong>de</strong>r trabajar <strong>con</strong> Hilos, los Hilos sonnativos en <strong>Java</strong>.Un buen entendimiento <strong>de</strong> cómo trabajar <strong>con</strong> Hilos es fundamental cuandoen nuestras aplicaciones se ejecutan más <strong>de</strong> una tarea <strong>de</strong> manera <strong>con</strong>currente.Y precisamente las aplicaciones <strong>con</strong> interfaces gráficos son un excelente ejemplo<strong>de</strong> las ventajas <strong>de</strong> trabajar <strong>con</strong> Hilos. Imagina que has escrito una aplicación,<strong>con</strong> un interfaz gráfico en la que al pulsar un botón se lleva a cabo un complejocálculo que <strong>con</strong>sume mucho tiempo <strong>de</strong> CPU. Si tu aplicación tiene un único Hilo<strong>de</strong> ejecución, el interfaz gráfico se quedará <strong>con</strong>gelado cuando el usuario inicieel cálculo, ya que hasta que este no se acabe, no volverá el <strong>con</strong>trol al interfazgráfico. Obviamente, ninguna aplicación se comporta así, o al menos no <strong>de</strong>bería.Todos los Hilos <strong>de</strong> una aplicación en <strong>Java</strong> comparten el mismo espacio <strong>de</strong>207


208CAPÍTULO 14. PROGRAMACIÓN CONCURRENTE CON HILOSmemoria, lo que implica que varios Hilos pue<strong>de</strong>n estar accediendo a la mismazona <strong>de</strong> memoria (por ejemplo una variable) para modificar su valor. En estecaso se pue<strong>de</strong>n producir colisiones e in<strong>con</strong>sistencias por el uso compartido <strong>de</strong>memoria. De algún modo hay que establecer un mecanismo <strong>de</strong> sincronizaciónpara que el acceso a zonas <strong>de</strong> memoria compartida no <strong>de</strong>n lugar a este tipo <strong>de</strong>comportamiento erróneo.En este capítulo vas a <strong>con</strong>ocer cómo trabajar <strong>con</strong> Hilos en <strong>Java</strong>, vas a <strong>con</strong>ocercómo se crean Hilos en <strong>Java</strong> y como sincronizar su ejecución cuando acce<strong>de</strong>n arecursos compartidos para que no haya in<strong>con</strong>sistencias en tus aplicaciones.14.1. ¿Qué es un hilo? Utilida<strong>de</strong>s. Consi<strong>de</strong>racionessobre el uso <strong>de</strong> hilosDe un modo muy resumido po<strong>de</strong>mos <strong>de</strong>cir que un Hilo es un flujo <strong>de</strong> <strong>con</strong>trolin<strong>de</strong>pendiente <strong>de</strong>ntro <strong>de</strong> un programa. Como tal tiene su propia pila <strong>de</strong> llamadas,pero todos los Hilos creados en el mismo programa comparten el mismo espacio<strong>de</strong> direcciones, lo que implica que comparten todas las instancias <strong>de</strong>l programaexcepto las locales. Nuestros programas pue<strong>de</strong>n tener un gran número <strong>de</strong> Hilosejecutándose al mismo tiempo y todos ellos comparten el uso <strong>de</strong> la CPU.Esto proporciona gran<strong>de</strong>s ventajas, ya que si disponemos, como empiezaa ser la habitual, <strong>de</strong> CPUs <strong>con</strong> más <strong>de</strong> un núcleo, varios hilos pue<strong>de</strong>n estarejecutándose <strong>de</strong> modo paralelo en cada uno <strong>de</strong> los núcleos <strong>de</strong> la CPU.También po<strong>de</strong>mos pensar que cada uno <strong>de</strong> los Hilos es un programa secuencialen sí mismo, <strong>con</strong> lo que la programación <strong>de</strong> soluciones complejas, cuandose piensan como un todo, se simplifican enormemente al ser divididas en partesque se ejecutan <strong>de</strong> modo in<strong>de</strong>pendiente pero coordinado.A cambio tenemos que pagar un precio, ya que si varios Hilos intentan acce<strong>de</strong>ral mismo recurso compartido y al mismo tiempo, el resultado, si el accesono está sincronizado, pue<strong>de</strong> no ser el <strong>de</strong>seado. Para ver lo que queremos <strong>de</strong>cir<strong>con</strong> claridad, supongamos dos hilos que se están ejecutando sobre una únicaCPU, el código que ejecuta el primero <strong>de</strong> ellos aparece en el Listado 14.1 y elque ejecuta el segundo aparece en el Listado 14.2. Supongamos que el primerHilo tiene el disfrute <strong>de</strong> la única CPU, y que el valor <strong>de</strong> la variable cantida<strong>de</strong>n ese momento es 9, la <strong>con</strong>dición <strong>de</strong> la línea 1 se cumple, y antes <strong>de</strong> que seejecute la segunda línea, se ce<strong>de</strong> el disfrute <strong>de</strong> la CPU al segundo hilo, que compruebaque la <strong>con</strong>dición <strong>de</strong> su línea 1 se cumple, por lo que incrementa en dosunida<strong>de</strong>s la variable cantidad que pasa a valer 11, y <strong>de</strong>spués <strong>de</strong> modificar elvalor <strong>de</strong> cantidad, <strong>de</strong> nuevo, se ce<strong>de</strong> el disfrute <strong>de</strong> la CPU al primer hilo. Eneste momento, la <strong>con</strong>dición que comprobó el primer Hilo ya no es válida y aúnasí, se incrementará el valor <strong>de</strong> la variable suma que <strong>de</strong> 11 pasará a valer 12.Evi<strong>de</strong>ntemente se ha producido un error ya que en este caso el acceso al recursocompartido suma no está sincronizado.1 i f ( c a n t i d a d < 10)2 c a n t i d a d++;3 System . out . 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ódigo que ejecuta el primer Hilo.


14.2.CREACIÓN DE HILOS EN JAVA 209Figura 14.1: La i<strong>de</strong>a <strong>de</strong> cómo crear un Hilo en <strong>Java</strong> gráficamente. Necesitamosdos ingredientes, la tarea cuyo código se va a ejecutar <strong>con</strong>currentemente, y el<strong>con</strong>trolador <strong>de</strong>l hilo (clase Thread).1 i f ( c a n t i d a d < 20)2 c a n t i d a d += 2 ;3 System . out . 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ódigo que ejecuta el segundo Hilo.14.2. Creación <strong>de</strong> hilos en <strong>Java</strong>Lo primero que hay que <strong>con</strong>ocer <strong>de</strong> la creación <strong>de</strong> Hilos en <strong>Java</strong> es que secomponen <strong>de</strong> dos partes, por un lado <strong>de</strong>finiremos la tarea que queremos quese ejecute <strong>de</strong> manera <strong>con</strong>currente <strong>con</strong> otras tareas, y por otro lado tenemos el<strong>con</strong>trolador <strong>de</strong> esa tarea, que nos permitirá sincronizarla <strong>con</strong> el resto <strong>de</strong> tareas.La clase que representa una tarea, y que <strong>con</strong>trolará un Hilo, ha <strong>de</strong> implementarel interface Runnable. Este interface <strong>de</strong>clara un único método publicvoid run(), el código <strong>de</strong>l cual se ejecutará <strong>de</strong> manera <strong>con</strong>currente <strong>con</strong> otrosHilos cuando se inicie. Por otro lado, el <strong>con</strong>trolador <strong>de</strong>l Hilo será una instancia<strong>de</strong> la clase Thread, a quien en el momento <strong>de</strong> su creación <strong>de</strong>bemos pasarle comoargumento una instancia <strong>de</strong> la clase que implementa el interface Runnable.La Figura 14.1 muestra gráficamente este <strong>con</strong>cepto.Po<strong>de</strong>mos utilizar tres técnicas distintas para crear un hilo en <strong>Java</strong>:1. Exten<strong>de</strong>r a la clase Thread.2. Definir una clase interna que implemente el interface Runnable.3. Definir una clase interna anónima que implemente el interface Runnable.Veamos cada una <strong>de</strong> estas técnicas por separado, y cuales son sus ventajasy <strong>de</strong>sventajas.14.2.1. Creación <strong>de</strong> un Hilo extendiendo a la clase ThreadLa clase Thread implementa la interface Runnable, luego simplemente extendiéndolay sobrescribiendo su método public void run() tendremos un nuevoHilo. El Listado 14.3 muestra el uso <strong>de</strong> esta primera técnica. Fíjate que al crear


210CAPÍTULO 14. PROGRAMACIÓN CONCURRENTE CON HILOSel Hilo, este no empieza su ejecución, para indicar que el Hilo <strong>de</strong>be empezar aejecutarse <strong>de</strong>bemos llamar a su método start. El Hilo permanece vivo siempreque se esté ejecutando su método public void run(). El resultado <strong>de</strong> laejecución <strong>de</strong> este Hilo es que se muestran 10 mensajes <strong>de</strong> texto por <strong>con</strong>sola.1 package h i l o s ;23 public f i n a l c l a s s ThreadExtendido extends Thread{4 public ThreadExtendido ( ) {5 super ( ) ;6 }78 @Overri<strong>de</strong>9 public void run ( ) {10 for ( int i = 0 ; i < 1 0 ; i ++)11 System . out . p r i n t l n ( " E s t a m o s en : " + i ) ;12 }1314 public s t a t i c void main ( S t r i n g [ ] a r g s ) {15 new ThreadExtendido ( ) . s t a r t ( ) ;16 }17 }Listado 14.3: Creación <strong>de</strong> un Hilo extendiendo a la clase ThreadFíjate que el método public void run() es un método público, luego, mientrasel Hilo se está ejecutando, cualquier otra clase podría llamar al métodorun() <strong>de</strong> esta clase, provocando <strong>con</strong> ello in<strong>con</strong>sistencias durante la ejecución <strong>de</strong>nuestra aplicación. Por ello, esta técnica <strong>de</strong> creación <strong>de</strong> Hilos está <strong>de</strong>sa<strong>con</strong>sejada.14.2.2. Creación <strong>de</strong> un Hilo mediante una clase internaEl Listado 14.4 muestra un ejemplo <strong>de</strong> esta segunda técnica. Hemos <strong>de</strong>finido laclase ClaseInterna que implementa el interface Runnable, <strong>de</strong>ntro <strong>de</strong> la claseHiloClaseInterna. Y en la línea 20 creamos una instancia <strong>de</strong> la clase Thread<strong>con</strong> un argumento que es una instancia <strong>de</strong> la clase ClaseInterna que <strong>de</strong>fine ensu método public void run() la tarea que se ejecuta <strong>con</strong>currentemente. Igualque en el ejemplo anterior, el Hilo permanecerá vivo siempre que se encuentreejecutando su método run().1 package h i l o s ;23 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 {4 private H i l o C l a s e I n t e r n a ( ) {5 super ( ) ;6 }78 private c l a s s C l a s e I n t e r n a implements Runnable {9 private C l a s e I n t e r n a ( ) {10 super ( ) ;11 }1213 public void run ( ) {14 for ( int i = 0 ; i < 1 0 ; i ++)15 System . out . p r i n t l n ( " E s t a m o s en : " + i ) ;16 }17 }1819 private void e j e c u t a ( ) {20 new Thread (new C l a s e I n t e r n a ( ) ) . s t a r t ( ) ;21 }2223 public s t a t i c void main ( S t r i n g [ ] a r g s ) {


14.2.CREACIÓN DE HILOS EN JAVA 21124 new H i l o C l a s e I n t e r n a ( ) . e j e c u t a ( ) ;25 }26 }Listado 14.4: Creación <strong>de</strong> un Hilo como una clase interna que implementa elinterface RunnableAl <strong>con</strong>trario que en la creación <strong>de</strong> hilos como extensiones <strong>de</strong> la clase Thread,en este caso la clase interna ClaseInterna es private, luego sólo se podrá acce<strong>de</strong>ra su método publi void run() <strong>de</strong>s<strong>de</strong> <strong>de</strong>ntro <strong>de</strong> la clase en la que está <strong>de</strong>finida,hemos evitado que otra clase llame <strong>de</strong> modo acci<strong>de</strong>ntal al método run()previniendo <strong>con</strong> ello in<strong>con</strong>sistencias en nuestra aplicación.Este método está recomendado cuando la tarea que se <strong>de</strong>be ejecutar <strong>de</strong>manera <strong>con</strong>currente es compleja, y la clase interna implementa el algoritmo <strong>de</strong>ejecución <strong>de</strong> la tarea. A<strong>de</strong>más, ya que la tarea <strong>con</strong>currente está implementada<strong>de</strong>ntro <strong>de</strong> una clase, podremos crear instancias <strong>de</strong> esta clase en cualquier otrolugar <strong>de</strong> la clase que la <strong>con</strong>tiene.14.2.3. Creación <strong>de</strong> un Hilo mediante una clase internaanónimaEl Listado 14.5 muestra un ejemplo <strong>de</strong>l uso <strong>de</strong> clases internas anónimas parala creación <strong>de</strong> Hilos. Este técnica es muy parecida a la técnica <strong>de</strong> la secciónanterior salvo que se ha utilizado una clase interna anónima para implementarla tarea que se ejecuta <strong>de</strong> manera <strong>con</strong>currente.1 package h i l o s ;23 public f i n a l c l a s s HiloClaseInternaAnonima {4 private HiloClaseInternaAnonima ( ) {5 super ( ) ;6 }78 private void e j e c u t a ( ) {9 new Thread (new Runnable ( ) {10 @Overri<strong>de</strong>11 public void run ( ) {12 for ( int i = 0 ; i < 1 0 ; i ++)13 System . out . p r i n t l n ( " E s t a m o s en : " + i ) ;14 }15 }) . s t a r t ( ) ;16 }1718 public s t a t i c void main ( S t r i n g [ ] a r g s ) {19 new HiloClaseInternaAnonima ( ) . e j e c u t a ( ) ;20 }21 }Listado 14.5: Creación <strong>de</strong> un Hilo como una clase interna anónima queimplementa el interface RunnableEsta técnica está recomendada cuando el código <strong>de</strong> la tarea <strong>con</strong>currente essólo <strong>de</strong> algunas líneas y no se va a reutilizar en ningún otro caso, ya que al seruna clase anónima no tienen nombre y no podríamos crear una instancia <strong>de</strong> unaclase sin nombre en ninguna otra parte <strong>de</strong> nuestro código.


212CAPÍTULO 14. PROGRAMACIÓN CONCURRENTE CON HILOSFigura 14.2: Estados en los que se pue<strong>de</strong> en<strong>con</strong>trar un Hilo y las posiblestransiciones entre ellos.14.3. Ciclo <strong>de</strong> vida <strong>de</strong> un hiloLos Hilos pue<strong>de</strong>n en<strong>con</strong>trarse en más estados que únicamente en el estado <strong>de</strong>ejecución cuando están vivos. La Figura 14.2 muestra los estados en los que sepue<strong>de</strong> en<strong>con</strong>trar un hilo y las transiciones entre los estados.Un hilo inicia su ejecución cuando se llama a su método public voidstart(), y en este momento se dice que está en Ejecución. Durante su ejecución,un Hilo pue<strong>de</strong> pasar al estado Pausado o al estado Planificado, en esteestado el Hilo no se ejecuta, pero siempre podrá pasar <strong>de</strong> nuevo al estado enEjecución. Un Hilo pue<strong>de</strong> pasar al estado Terminado <strong>de</strong>s<strong>de</strong> el estado en Ejecución,se dice que el Hilo se ha terminado, y un hilo Terminado nunca más pue<strong>de</strong>pasar <strong>de</strong> nuevo al estado en Ejecución, Pausado o Planificado.La diferencia entre los estados Pausado o Planificado es que <strong>de</strong> un estadoPlanificado <strong>con</strong>ocemos el momento en que <strong>de</strong> nuevo pasará a Ejecución. De unestado Pausado no sabemos cuando volverá al estado Ejecución. Dependiendo<strong>de</strong>l método <strong>de</strong> la clase Thread que utilicemos, podremos llevar un Hilo enEjecución a uno <strong>de</strong> estos estados.Un hilo pasa al estado Terminado cuando ocurre alguna <strong>de</strong> estas tres cosas:1. Sale <strong>de</strong> su método public void run().2. Se produce una excepción <strong>de</strong>ntro <strong>de</strong>l método run() no gestionada.3. Se llama al método public void stop() <strong>de</strong>l Hilo.Buena prácticaEl uso <strong>de</strong>l método stop() para <strong>de</strong>tener la ejecución <strong>de</strong> un Hilo está fuertemente<strong>de</strong>sa<strong>con</strong>sejado ya que pue<strong>de</strong> provocar in<strong>con</strong>sistencias y <strong>de</strong> hecho está marcadocomo obsoleto en el API <strong>Java</strong>. Para acabar un Hilo limpiamente se a<strong>con</strong>sejasalir <strong>de</strong> su método run().Esta buena práctica es la que se sigue en los ejemplo mostrados, cuando seha iterado un cierto número <strong>de</strong> veces, se sale <strong>de</strong>l método public void run().


14.4. CONTROL DE HILOS 213Otra técnica muy extendida es utilizar <strong>de</strong>ntro <strong>de</strong>l método public void run()un bucle while(<strong>con</strong>dicion) cuya <strong>con</strong>dición inicialmente es true y se cambia afalse cuando se quiere acabar el Hilo limpiamente.14.4. Control <strong>de</strong> hilosEl <strong>con</strong>trol <strong>de</strong> hilos se pue<strong>de</strong> llevar a cabo <strong>de</strong>s<strong>de</strong> una perspectiva gruesa tratandocada Hilo como un bloque, o <strong>de</strong>s<strong>de</strong> una perspectiva más fina mediante el uso <strong>de</strong>la sincronización. En esta sección veremos como coordinar la ejecución <strong>de</strong> Hilos<strong>de</strong>s<strong>de</strong> una perspectiva gruesa <strong>de</strong>jando para la siguiente sección la sincronización<strong>de</strong> Hilos <strong>de</strong>s<strong>de</strong> una perspectiva más fina.Po<strong>de</strong>mos suspen<strong>de</strong>r temporalmente la ejecución <strong>de</strong> un Hilo mediante eluso <strong>de</strong>l método public static void sleep(long milisegundos) 1 . Pasado eltiempo especificado, el Hilo volverá al estado en Ejecución. Es <strong>de</strong>cir, <strong>con</strong> elmétodo sleep(long milisegundos) planificamos que el Hilo vuelva a su estadoEjecución pasados los milisegundos especificados.El Listado 14.6 muestra un ejemplo que muestra un mensaje cada 1000milisegundos, es <strong>de</strong>cir, cada segundo. El método sleep(long) pue<strong>de</strong> lanzar unaexcepción <strong>de</strong> tipo InterruptedException que <strong>de</strong>bemos gestionar.1 package h i l o s ;23 public f i n a l c l a s s EjemploSleep {4 private EjemploSleep ( ) {5 super ( ) ;6 }78 private void e j e c u t a ( ) {9 new Thread (new Runnable ( ) {10 @Overri<strong>de</strong>11 public void run ( ) {12 try {13 for ( int i = 0 ; i < 1 0 ; i ++) {14 System . out . p r i n t l n ( " E s t a m o s en : " + i ) ;15 Thread . s l e e p ( 1 0 0 0 ) ;16 }17 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {18 e . p r i n t S t a c k T r a c e ( ) ;19 }20 }21 }) . s t a r t ( ) ;22 }2324 public s t a t i c void main ( S t r i n g [ ] a r g s ) {25 new EjemploSleep ( ) . e j e c u t a ( ) ;26 }2728 }Listado 14.6: Uso <strong>de</strong>l método sleep(long milisegundos) para pausar laejecución <strong>de</strong>l Hilo durante el intervalo <strong>de</strong> tiempo espedificado.Si un Hilo está en estado Pausado o Planificado siempre lo podremos sacar <strong>de</strong>este estado <strong>con</strong> el método void interrupt(). Este método lanza una excepción<strong>de</strong> tipo InterruptedException.Existen casos en los que un Hilo <strong>de</strong>be esperar a que otro acabe su ejecuciónpara que él pueda <strong>con</strong>tinuar. Piensa por ejemplo en un Hilo que cuenta el número1 También existe una versión <strong>de</strong> mayor precisión public static void sleep(longmilisegundos, int nanosegundos


214CAPÍTULO 14. PROGRAMACIÓN CONCURRENTE CON HILOS<strong>de</strong> caracteres que se leen <strong>de</strong>s<strong>de</strong> un fichero. Si la lectura <strong>de</strong>l fichero se hace en unHilo y la cuenta <strong>de</strong>l número <strong>de</strong> caracteres en otro, el Hilo que cuenta el número<strong>de</strong> caracteres <strong>de</strong>be esperar a que el Hilo que carga el fichero acabe su tarea antes<strong>de</strong> empezar <strong>con</strong> la cuenta.Para que un Hilo espere a que otro acabe su ejecución llamaremos al métodopublic final void join() 2 <strong>de</strong>l segundo Hilo en el código <strong>de</strong>l primero, tal ycomo muestra el ejemplo <strong>de</strong>l Listado 14.7.1 package h i l o s ;23 public f i n a l c l a s s EjemploJoin {4 private Thread h i l o ;56 private EjemploJoin ( ) {7 super ( ) ;8 }910 private void e j e c u t a ( ) {11 h i l o = new Thread (new Tarea ( ) ) ;12 h i l o . s t a r t ( ) ;13 new Thread (new TareaQueEspera ( ) ) . s t a r t ( ) ;14 }1516 private c l a s s Tarea implements Runnable {17 public void run ( ) {18 try {19 for ( int i = 0 ; i < 1 0 ; i ++) {20 System . out . p r i n t l n ( " C u e n t a : " + i ) ;21 Thread . s l e e p ( 5 0 0 ) ;22 }23 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {24 e . p r i n t S t a c k T r a c e ( ) ;25 }26 }27 }2829 private c l a s s TareaQueEspera implements Runnable {30 public void run ( ) {31 try {32 for ( int i = 0 ; i < 5 ; i ++) {33 System . out . p r i n t l n ( " C u e n t a y e s p e r a : " + i ) ;34 Thread . s l e e p ( 5 0 0 ) ;35 }3637 h i l o . j o i n ( ) ;3839 for ( int i = 5 ; i < 1 0 ; i ++) {40 System . out . p r i n t l n ( " S a l g o <strong>de</strong> la e s p e r a y c u e n t a : " + i ) ;41 Thread . s l e e p ( 5 0 0 ) ;42 }43 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {44 e . p r i n t S t a c k T r a c e ( ) ;45 }46 }47 }4849 public s t a t i c void main ( S t r i n g [ ] a r g s ) {50 new EjemploJoin ( ) . e j e c u t a ( ) ;51 }52 }Listado 14.7: Uso <strong>de</strong>l método join() para pausar la ejecución <strong>de</strong>l Hilo hastaque acabe la ejecución <strong>de</strong> otro.2 Existen dos versiones <strong>de</strong> este método public final void join(long milisegundo ypublic final void sleep(long milisegundos, int nanosegundos en los que se espera, comomáximo, el tiempo especificado


14.5.SINCRONIZACIÓN 21514.5. SincronizaciónEn los ejemplos anteriores el <strong>con</strong>trol sobre Hilos no implicaba acceso <strong>con</strong>currentea recursos. Cuando varios Hilos que se ejecutan <strong>de</strong> modo <strong>con</strong>currente intentanacce<strong>de</strong>r al mismo recurso <strong>de</strong>bemos sincronizar los accesos al recurso para queno se produzcan in<strong>con</strong>sistencias en nuestras aplicaciones.La sincronización en <strong>Java</strong> se base en el uso <strong>de</strong> cerrojos, varios Hilos queintentan acce<strong>de</strong>r a un recurso compartido sincronizan su acceso a través <strong>de</strong> uncerrojo. Antes <strong>de</strong> acce<strong>de</strong>r al recurso compartido un Hilo intentará adquirir uncerrojo, si el cerrojo no está en posesión <strong>de</strong> ningún otro Hilo lo adquirirá, <strong>de</strong>lo <strong>con</strong>trario tendrá que esperar a que el cerrojo se libere. Una vez en posesión<strong>de</strong>l cerrojo pue<strong>de</strong> trabajar <strong>con</strong> el recurso compartido <strong>con</strong> la seguridad <strong>de</strong> queningún otro Hilo acce<strong>de</strong>rá a este recurso hasta que él no libere el cerrojo queindica la propiedad <strong>de</strong> uso <strong>de</strong>l recurso.¿Y don<strong>de</strong> se encuentran esos cerrojos que nos sirven para sincronizar el accesoa recursos compartidos?. Todo objeto tiene un cerrojo intrínseco, recuerda quelos Hilos y su sincronización son nativos en <strong>Java</strong>.En las secciones siguientes vamos a ver cómo sincronizar el acceso a recursoscompartidos <strong>con</strong> dos técnicas: mediante el uso <strong>de</strong> cerrojos intrínsecos y mediantela interface Lock y la interface Condition introducidas ambas en la versión5.0 <strong>de</strong> <strong>Java</strong>.14.5.1. Sincronizacón utilizando los cerrojos intrínsecosEn <strong>Java</strong> todo objeto posee un cerrojo intrínseco que po<strong>de</strong>mos utilizar parasincronizar el acceso a los recursos que nos proporciona el objeto. Como yasabemos, un método es un recurso o servicio que proporciona un objeto, siqueremos sincronizar el acceso a un método <strong>de</strong> un objeto basta marcarlo <strong>con</strong> lapalabra reservada synchronized, como en el Listado 14.8.1 c l a s s A {2 synchronized void metodo1 ( ) {}3 synchronized void metodo2 ( ) {}4 . . .5 }Listado 14.8: Para indica que el acceso a un método está sincronizado utilizamosla palabra reservada synchronized.Definir un método como synchronized implica que antes <strong>de</strong> que un Hilopueda ejecutar el método, <strong>de</strong>be adquirir el cerrojo intrínseco <strong>de</strong>l objeto parapo<strong>de</strong>r ejecutarlo. Si el cerrojo intrínseco está en posesión <strong>de</strong> otro Hilo, el Hilo queintenta adquirir el cerrojo tendrá que esperar a que el otro lo libere. El cerrojointrínseco pertenece al objeto y sincroniza el acceso a todos los métodos <strong>de</strong>finidoscomo synchronized, si un Hilo adquiere el cerrojo intrínseco accediendo almetodo1() <strong>de</strong>l ejemplo anterior, y otro Hilo intenta acce<strong>de</strong>r al metodo2() nopodrá hacerlo ya que no podrá adquirir el cerrojo intrínseco <strong>de</strong>l objeto. UnHilo libera el cerrojo cuando acaba la ejecución <strong>de</strong>l método sincronizado, y enese momento cualquier otro Hilo que se encuentre a la espera podrá adquirir elcerrojo.Como el cerrojo intrínseco pertenece al objeto, la sincronización en el ejemplo<strong>de</strong>l Listado 14.8 es relativa a todo el objeto, si un Hilo está en posesión <strong>de</strong>l cerrojo


216CAPÍTULO 14. PROGRAMACIÓN CONCURRENTE CON HILOSintrínseco, cualquier otro Hilo no podrá acce<strong>de</strong>r a ningún otro método <strong>de</strong>finidocomo synchronized.Hay casos en los que no nos interesa sincronizar el acceso a los método <strong>de</strong>lobjeto, si no sincronizar sólo bloques <strong>de</strong> código. Incluso a veces nos interesa tenervarios bloques <strong>de</strong> código sincronizados pero que el acceso a distintos bloques <strong>de</strong>código no esté sincronizado, si no que la sincronización sea relativa al bloque yno al objeto completo.<strong>Java</strong> nos permite sincronizar el acceso a bloque <strong>de</strong> código también medianteel uso <strong>de</strong> la palabra reservada synchronized, tal y como se muestra en el Listado14.9. Un Hilo pue<strong>de</strong> estar ejecutando el bloque sincronizado por el cerrojo1mientras que otro Hilo pue<strong>de</strong> estar ejecutando el bloque sincronizado por elcerrojo2; pero ningún otro Hilo podrá acce<strong>de</strong>r a estos bloques sincronizadoshasta que los Hilos en posesión <strong>de</strong> los cerrojos no abandonen el bloque sincronizado.1 c l a s s A {2 Object c e r r o j o 1 = new Object ( ) ;3 Object c e r r o j o 2 = new Object ( ) ;45 void metodo1 ( ) {6 . . .7 synchronized ( c e r r o j o 1 ) {8 // El a c c e s o a e s t e bloque e s t á s i n c r o n i z a d o9 // por c e r r o j o 1 .10 }11 . . .12 }1314 void metodo2 ( ) {}15 . . .16 synchronized ( c e r r o j o 2 ) {17 // El a c c e s o a e s t e bloque e s t á s i n c r o n i z a d o18 // por c e r r o j o 2 .19 }20 }21 }Listado 14.9: Cada uno <strong>de</strong> los bloques <strong>de</strong> código está sincronizado por un cerrojointrínseco <strong>de</strong> un objeto distinto. El acceso sólo está sincronizado si el acceso esal mismo bloque <strong>de</strong> código.Fíjate que, por otra parte, esta técnica nos permite sincronizar <strong>con</strong> más<strong>de</strong>talle el código <strong>con</strong>flictivo, no sincronizamos todo un método si no sólo laslíneas <strong>de</strong> código que acce<strong>de</strong>n al recurso compartido. Incluso po<strong>de</strong>mos utilizaresta técnica para sincronizar bloques <strong>de</strong> código en distintos métodos <strong>con</strong> elmismo cerrojo.Ya hemos visto que cuando un Hilo no pue<strong>de</strong> adquirir el cerrojo para acce<strong>de</strong>ra un método o bloque sincronizado tiene que esperar a que el Hilo que tiene enposesión el cerrojo lo libere, y que el cerrojo se libera cuando el Hilo que lotiene en posesión acaba la ejecución <strong>de</strong>l método o bloque sincronizado. Tambiénpo<strong>de</strong>mos forzar que un Hilo entre en espera mediante el método public finalvoid wait() throws InterruptedException 3 . Cuando un Hilo entra en espera,libera el cerrojo intrínseco que tenga en posesión, y esta es la diferencia<strong>con</strong> respecto al método sleep(int milisegundos) que provoca una espera <strong>de</strong>l3 Existen otras dos versiones <strong>de</strong> este método public final void wait(long timeout)throws InterruptedException y public final void wait(long timeout, int nanos)throws InterruptedException


14.5.SINCRONIZACIÓN 217Hilo, pero el Hilo no libera el cerrojo intrínseco que tenga en posesión. El uso<strong>de</strong>l método wait() lleva un Hilo <strong>de</strong>s<strong>de</strong> el estado Ejecución al estado Pausadoya que no <strong>con</strong>ocemos cuando el Hilo volverá al estado Ejecución.Un Hilo que ha entrado en espera mediante una llamada a wait() saldrá <strong>de</strong>este estado si ocurre alguna <strong>de</strong> estas cuatro cosas:Algún otro Hilo llama al método public final void notify().Algún otro Hilo llama al método public final void notifyAll().Algún otro Hilo llama al método public void interrupt().Mientras un Hilo permanece en estado <strong>de</strong> espera nunca será elegido paracompetir por la adquisición <strong>de</strong>l cerrojo.Existe la posibilidad <strong>de</strong> que el Hilo en estado <strong>de</strong> espera sufra un <strong>de</strong>spertarespúreo, lo que implica que el Hilo se <strong>de</strong>spierte aunque no ocurra ningunacircunstancia <strong>de</strong> la indicada anteriormente, <strong>de</strong> modo que, si <strong>de</strong>tuvimos el Hiloporque no se comprobaba cierta <strong>con</strong>dición, si el Hilo se <strong>de</strong>spierta <strong>de</strong> modoespúreo pue<strong>de</strong> que la <strong>con</strong>dición siga sin satisfacerse, por lo que siempre <strong>de</strong>bemoscomprobar que la <strong>con</strong>dición se satisface cuando un Hilo sale <strong>de</strong>l estado <strong>de</strong>espera.El Listado 14.10 muestra una posible implementación <strong>de</strong> un Buffer <strong>de</strong> tamañofijo <strong>con</strong> sus operaciones getDato() y setDato(T dato) sincronizadas y<strong>de</strong> modo que si un Hilo intenta tomar un dato y el Buffer está vacío el Hilo <strong>de</strong>beesperar (wait()), y si el Hilo intenta poner un nuevo dato y el Buffer está llenotambién <strong>de</strong>be esperar.1 package b u f f e r ;23 public c l a s s BufferSinLock {4 private int cabeza = 0 ;5 private int capacidad ;6 private Object datos [ ] ;7 private int ocupacion = 0 ;89 public B u f f e r S i n L o c k ( int capacidad ) {10 super ( ) ;11 t h i s . capacidad = capacidad ;12 datos = new Object [ capacidad ] ;13 }1415 @SuppressWarnings ( " u n c h e c k e d " )16 public synchronized T getDato ( ) throws I n t e r r u p t e d E x c e p t i o n {17 T dato ;18 while ( ocupacion == 0)19 wait ( ) ;20 dato = (T) datos [ cabeza ] ;21 ocupacion −−;22 cabeza++;23 cabeza %= capacidad ;24 System . out . format ( " - %s [ % d ]\ n " , dato , ocupacion ) ;25 n o t i f y A l l ( ) ;26 return dato ;27 }2829 public synchronized void setDato (T dato ) throws I n t e r r u p t e d E x c e p t i o n {30 while ( ocupacion == capacidad )31 wait ( ) ;32 datos [ ( cabeza + ocupacion ) %capacidad ] = dato ;33 ocupacion++;34 System . out . format ( " + % s [ % d ]\ n " , dato , ocupacion ) ;35 n o t i f y A l l ( ) ;


218CAPÍTULO 14. PROGRAMACIÓN CONCURRENTE CON HILOS36 }37 }Listado 14.10: Implementación <strong>de</strong> un Buffer <strong>de</strong> tamaño fijo utilizando cerrojosintrínsecosEn el Apéndice C se muestra un ejemplo <strong>de</strong> uso <strong>de</strong> este Buffer.14.5.2. Sincronización utilizando el interface LockDes<strong>de</strong> la versión <strong>Java</strong> 5, se ha enriquecido el API <strong>de</strong> <strong>Java</strong> <strong>con</strong> nuevos paquetespara facilitar la programación <strong>con</strong> Hilos. Estos nuevos paquetes son:java.util.<strong>con</strong>current Que proporciona nuevas estructuras <strong>de</strong> datos y clases<strong>de</strong> utilidad <strong>de</strong> acceso <strong>con</strong>currente.java.util.<strong>con</strong>current.atomic Conjunto <strong>de</strong> clases para acceso atómico a tipos<strong>de</strong> datos primitivos y referencia.java.util.<strong>con</strong>current.locks Conjunto <strong>de</strong> clases para facilitar la sincronizacióny acceso <strong>con</strong>currente a recursos compartidos.El último paquete java.util.<strong>con</strong>current.locks <strong>de</strong> la lista anterior proporcionaclases que facilitan la sincronización <strong>con</strong>currente a recursos compartidos.En particular la interface Lock y las clases que lo implementan ReentrantLock,ReentrantReadWriteLock.ReadLock y ReentrantReadWriteLock.WriteLockson <strong>de</strong> gran ayuda. La i<strong>de</strong>a básica es que un Hilo <strong>de</strong>be obtener el cerrojo querepresenta alguna <strong>de</strong> estas clases antes <strong>de</strong> po<strong>de</strong>r ejecutar el código que nos interesa<strong>de</strong> manera <strong>con</strong>currente, y una vez que se ha terminado la ejecución <strong>de</strong>lcódigo crítico, <strong>de</strong>bemos liberar el cerrojo. La técnica recomendada para hacerlose muestra en el Listado 14.111 Lock l = . . . ; // creamos alguna i n s t a n c i a que implemente a Lock2 l . l o c k ( ) ;3 try {4 // a c c e s o a l o s r e c u r s o s compartidos5 } f i n a l l y {6 l . unlock ( ) ;7 }Listado 14.11: Modo a<strong>con</strong>sejado <strong>de</strong> trabajar <strong>con</strong> los objetos que implementanla interface LockOtra interface <strong>de</strong> gran ayuda es Condition <strong>con</strong> la que po<strong>de</strong>mos <strong>de</strong>finirvarias <strong>con</strong>diciones sobre un mismo Lock <strong>de</strong> modo que el hilo que adquiere elLock pue<strong>de</strong> pausarse, liberando el Lock, utilizando un Condition si la <strong>con</strong>dición<strong>de</strong> ocupación <strong>de</strong>l Hilo no es válida, y se le informará a través <strong>de</strong> esa mismaCondition <strong>de</strong> que la <strong>con</strong>dición <strong>de</strong> ocupación <strong>de</strong>l Hilo pue<strong>de</strong> ser válida <strong>de</strong> nuevo.Como ejemplo <strong>de</strong> funcionamiento <strong>de</strong> estas interfaces, el Listado 14.12 muestrauna implementación <strong>de</strong> un Buffer <strong>de</strong> tamaño fijo y acceso sincronizado,como el <strong>de</strong>l Listado 14.10 pero esta vez haciendo uso <strong>de</strong> estas interfaces. En elApéndice C se muestra un ejemplo <strong>de</strong> uso <strong>de</strong> este Buffer.1 package b u f f e r ;2


14.5.SINCRONIZACIÓN 2193 import j a v a . u t i l . c o n c u r r e n t . l o c k s . Condition ;4 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 ;5 import j a v a . u t i l . c o n c u r r e n t . l o c k s . ReentrantLock ;67 public c l a s s BufferConLock {8 private int cabeza = 0 ;9 private int capacidad ;10 private Object datos [ ] ;11 private int ocupacion = 0 ;12 private Lock c e r r o j o ;13 private Condition c o n d i c i o n ;1415 public BufferConLock ( int capacidad ) {16 super ( ) ;17 t h i s . capacidad = capacidad ;18 datos = new Object [ capacidad ] ;19 c e r r o j o = new ReentrantLock ( ) ;20 c o n d i c i o n = c e r r o j o . newCondition ( ) ;21 }2223 @SuppressWarnings ( " u n c h e c k e d " )24 public T getDato ( ) throws I n t e r r u p t e d E x c e p t i o n {25 T dato ;26 c e r r o j o . l o c k ( ) ;27 try {28 while ( ocupacion == 0)29 c o n d i c i o n . await ( ) ;30 dato = (T) datos [ cabeza ] ;31 ocupacion −−;32 cabeza++;33 cabeza %= capacidad ;34 System . out . format ( " - %s [ % d ]\ n " , dato , ocupacion ) ;35 } f i n a l l y {36 c o n d i c i o n . s i g n a l A l l ( ) ;37 c e r r o j o . unlock ( ) ;38 }39 return dato ;40 }4142 public void setDato (T dato ) throws I n t e r r u p t e d E x c e p t i o n {43 c e r r o j o . l o c k ( ) ;44 try {45 while ( ocupacion == capacidad )46 c o n d i c i o n . await ( ) ;47 datos [ ( cabeza + ocupacion ) %capacidad ] = dato ;48 ocupacion++;49 System . out . format ( " + % s [ % d ]\ n " , dato , ocupacion ) ;50 } f i n a l l y {51 c o n d i c i o n . s i g n a l A l l ( ) ;52 c e r r o j o . unlock ( ) ;53 }54 }55 }Listado 14.12: Implementación <strong>de</strong> un Buffer <strong>de</strong> tamaño fijo utilizando las clasesReentrantLock y Condition¿Cuando utilizar los cerrojos intrínsecos y cuando el interface Lock?. Lasección 13.2 <strong>de</strong> la referencia [1] clarifica mucho cuando utilizar una técnica uotra.Ejercicios.1. Escribe una aplicación en la que varios Hilos intenten acce<strong>de</strong>r a un recursocompartido que muestre un mensaje por <strong>con</strong>sola. El acceso a este recursocompartido <strong>de</strong>be hacerse <strong>de</strong> modo sincronizado.


220CAPÍTULO 14. PROGRAMACIÓN CONCURRENTE CON HILOSLecturas recomendadas.El Capítulo 10 <strong>de</strong> la referencia [2] es sin duda una muy buena exposición<strong>de</strong> las técnicas <strong>de</strong> <strong>con</strong>currencia en <strong>Java</strong> utilizando cerrojos intrínsecos.La referencia [1] es obligada <strong>de</strong> principio a fin si la <strong>con</strong>currencia es capitalen nuestras aplicaciones.El Capítulo 9 <strong>de</strong> la referencia [4] expone criterios muy interesante a seguiren el uso <strong>de</strong> Hilos para que no se vea reducida la potencia <strong>de</strong> nuestrasaplicaciones por un mal uso <strong>de</strong> los Hilos.


Capítulo 15Programación para la RedContenidos15.1. Trabajo <strong>con</strong> URLs . . . . . . . . . . . . . . . . . . . 22215.1.1. ¿Qué es una URL? . . . . . . . . . . . . . . . . . . . 22215.1.2. Leer <strong>de</strong>s<strong>de</strong> una URL . . . . . . . . . . . . . . . . . . 22315.1.3. Escribir a una URL . . . . . . . . . . . . . . . . . . 22315.2. Trabajo <strong>con</strong> Sockets . . . . . . . . . . . . . . . . . . 22515.2.1. ¿Qué es un Socket? . . . . . . . . . . . . . . . . . . . 22515.2.2. Sockets bajo el protocolo TCP . . . . . . . . . . . . 22515.2.2.1. Sockets TCP en el lado <strong>de</strong>l servidor . . . . 22515.2.2.2. Sockets TCP en el lado <strong>de</strong>l cliente . . . . . 22615.2.3. Sockets bajo el protocolo UDP . . . . . . . . . . . . 22715.2.3.1. Sockets UDP en el lado <strong>de</strong>l servidor . . . . 22815.2.3.2. Sockets UDP en el lado <strong>de</strong>l cliente . . . . . 228Introducción<strong>Java</strong> nació como un lenguaje <strong>de</strong> propósito general <strong>con</strong> gran<strong>de</strong>s capacida<strong>de</strong>s paratrabajar en red. En el Capítulo 12 vimos cómo programar Applets, que sonaplicaciones <strong>Java</strong> que se ejecutan en el <strong>con</strong>texto <strong>de</strong> un navegador web. <strong>Java</strong> nospermite trabajar en red <strong>de</strong> un modo muy sencillo gracias a la gran cantidad <strong>de</strong>clases que nos proporciona el paquete java.net.En este capítulo veremos como trabajar <strong>con</strong> URLs, pilar básico para referenciarrecursos en el protocolo HTTP, y como trabajar <strong>con</strong> Sockets tanto bajoel protocolo TCP como bajo el protocolo UDP.Como verás, tanto si trabajamos <strong>con</strong> URLs como si trabajamos <strong>con</strong> Sockets,la lectura y escritura sobre estos canales la haremos a través <strong>de</strong> los Flujos quevimos en el Capítulo 7. Recuerda que todo proceso <strong>de</strong> lectura/escritura es in<strong>de</strong>pendiente<strong>de</strong>l dispositivo <strong>de</strong> entrada/salida y se realiza a través <strong>de</strong> Flujos, <strong>de</strong>ahí la importancia <strong>de</strong> <strong>con</strong>ocerlos a fondo.221


222CAPÍTULO 15. PROGRAMACIÓN PARA LA RED15.1. Trabajo <strong>con</strong> URLsEn esta sección vamos a recordar qué es una URL. Veremos que en el paquetejava.net tenemos una clase para crear URLs. Esta clase nos permite tanto leerel <strong>con</strong>tenido <strong>de</strong> la URL como escribir hacia la URL.15.1.1. ¿Qué es una URL?Una URL representa un recurso en Internet. Este recurso viene especificadopor cuatro partes, por ejemplo en la siguiente URL http://www.google.es:80/in<strong>de</strong>x.html, se está especificando:1. El protocolo http.2. El host <strong>de</strong> <strong>de</strong>stino www.google.es.3. El puerto <strong>de</strong> <strong>con</strong>exión 80.4. El fichero solicitado in<strong>de</strong>x.html.Los servidores que atien<strong>de</strong>n peticiones HTTP generalmente utilizan el puerto<strong>de</strong> escucha 80, y si no se indica la <strong>con</strong>trario, el fichero al que por <strong>de</strong>fecto seacce<strong>de</strong> es in<strong>de</strong>x.html 1 ; por lo que la anterior URL la po<strong>de</strong>mos escribir <strong>de</strong>forma abreviada <strong>de</strong> modo http://www.google.es.En esta sección vamos a utilizar como ejemplo siempre el protocolo HTTP,aunque, como el lector sabrá, existe otra gran cantidad <strong>de</strong> protocolos sobre TCP.<strong>Java</strong> nos proporciona la clase URL para po<strong>de</strong>r especificar recursos en Internetcomo muestra el Listado 15.1. En el primer caso especificamos la URL comouna única ca<strong>de</strong>na. En el segundo caso lo especificamos como cuatro ca<strong>de</strong>nasindicando el protocolo, dirección <strong>de</strong> Internet, puerto y recurso respectivamente.Si el protocolo tiene un puerto bien <strong>con</strong>ocido no hace falta especificarlo, comoen el tercer caso. Todos estos <strong>con</strong>structores pue<strong>de</strong>n lanzar una excepción <strong>de</strong> tipoMalformedURLException para indicar que la URL que se intenta especificar noes válida.1 URL u r l = new URL( " http :// www . g o o g l e . es :80/ i n d e x . html " ) ;2 URL u r l = new URL( " http " , " www . uji . es " , 80 , " i n d e x . html " ) ;3 URL u r l = new URL( " http " , " www . uji . es " , " i n d e x . html " ) ;Listado 15.1: Algunos <strong>con</strong>structores <strong>de</strong> la clase URLLa clase URL nos proporciona métodos para recuperar la información <strong>con</strong>tenidaen una URL:public String getProtocol(), <strong>de</strong>vuelve el protocolo.public String getHost(), <strong>de</strong>vuelve el host.public int getPort(), <strong>de</strong>vuelve el puerto <strong>de</strong> <strong>con</strong>exión.public String getFile(), <strong>de</strong>vuelve el recurso que se solicita.Pasemos ahora a ver cómo leer <strong>de</strong>s<strong>de</strong> una URL.1 La página <strong>de</strong> inicio <strong>de</strong> un sitio web también pue<strong>de</strong> ser in<strong>de</strong>x.js, o in<strong>de</strong>x.cgi


15.1. TRABAJO CON URLS 22315.1.2. Leer <strong>de</strong>s<strong>de</strong> una URLUna vez que tenemos <strong>con</strong>struida una URL válida, po<strong>de</strong>mos recuperar a partir<strong>de</strong> ella un InputStream para leer el <strong>con</strong>tenido <strong>de</strong>l recurso al que apunta laURL, mediante el método public final InputStream openStream() throwsIOException, y como ya vimos en el Capítulo 7 a partir <strong>de</strong> una referencia <strong>de</strong>tipo InputStream po<strong>de</strong>mos recuperar un BufferedRea<strong>de</strong>r <strong>con</strong> el que leer líneaa línea <strong>de</strong>s<strong>de</strong> el recurso. El Listado 15.2 muestra un ejemplo completo <strong>de</strong> cómoleer el <strong>con</strong>tenido <strong>de</strong> un fichero <strong>de</strong> esto apuntado por una URL.1 package red ;23 import j a v a . i o . BufferedRea<strong>de</strong>r ;4 import j a v a . i o . IOException ;5 import j a v a . i o . InputStream ;6 import j a v a . i o . InputStreamRea<strong>de</strong>r ;7 import j a v a . net . MalformedURLException ;8 import j a v a . net .URL;910 public f i n a l c l a s s LeerDes<strong>de</strong>URL {11 private LeerDes<strong>de</strong>URL ( ) {12 super ( ) ;13 }1415 private void e j e c u t a ( S t r i n g d i r e c c i o n ) {16 try {17 URL u r l = new URL( d i r e c c i o n ) ;18 InputStream i s = u r l . openStream ( ) ;19 BufferedRea<strong>de</strong>r br = new BufferedRea<strong>de</strong>r (new InputStreamRea<strong>de</strong>r ( i s ) ) ;20 try {21 S t r i n g ca<strong>de</strong>na ;22 while ( ( ca<strong>de</strong>na = br . r e a d L i n e ( ) ) != null )23 System . out . p r i n t l n ( ca<strong>de</strong>na ) ;24 } f i n a l l y {25 br . c l o s e ( ) ;26 }27 } catch ( MalformedURLException e ) {28 e . p r i n t S t a c k T r a c e ( ) ;29 } catch ( IOException e ) {30 e . p r i n t S t a c k T r a c e ( ) ;31 }32 }3334 public s t a t i c void main ( S t r i n g [ ] a r g s ) {35 new LeerDes<strong>de</strong>URL ( ) . e j e c u t a ( a r g s [ 0 ] ) ;36 }37 }Listado 15.2: Lectura <strong>de</strong>s<strong>de</strong> una URL15.1.3. Escribir a una URLEscribir a una URL exige un poco más <strong>de</strong> trabajo, ya que no po<strong>de</strong>mosescribir directamente a una URL. Para escribir a una URL necesitamos unareferencia a la clase URLConnection. La referencia necesaria nos la <strong>de</strong>vuelveel método public URLConnection openConnection() throws IOException<strong>de</strong> la clase URL. Una vez obtenida esta referencia, <strong>de</strong>bemos indicar que queremosescribir sobre ella, para lo que utilizaremos el método public voidsetDoOutput(boolean dooutput) <strong>de</strong> la clase URLConnection <strong>con</strong> un argumentotrue. Ahora ya po<strong>de</strong>mos recuperar un OutputStream <strong>con</strong> el métodopublic OutputStream getOutputStream() throws IOException <strong>de</strong> la claseURConnection


224CAPÍTULO 15. PROGRAMACIÓN PARA LA REDEl Listado 15.3 muestra todos los pasos necesarios para escribir a una URLy obtener una respuesta <strong>de</strong> ella. En este caso hemos utilizado la direcciónhttp://rubi.dlsi.uji.es/~oscar/PHP/nombre.php que espera un parámetro<strong>de</strong> entrada llamado nombre y <strong>de</strong>vuelve un saludo 2 .1 package red ;23 import j a v a . i o . BufferedRea<strong>de</strong>r ;4 import j a v a . i o . IOException ;5 import j a v a . i o . InputStreamRea<strong>de</strong>r ;6 import j a v a . i o . P r i n t W r i t e r ;7 import j a v a . net . MalformedURLException ;8 import j a v a . net .URL;9 import j a v a . net . URLConnection ;1011 public f i n a l c l a s s EscribirAURL {12 private EscribirAURL ( ) {13 super ( ) ;14 }1516 private void e j e c u t a ( S t r i n g s U r l ) {17 try {18 URL u r l = new URL( s U r l ) ;19 URLConnection c onexion = u r l . openConnection ( ) ;20 <strong>con</strong>exion . setDoOutput ( true ) ;21 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 ) ;22 pw . p r i n t l n ( " n o m b r e = o s c a r " ) ;23 BufferedRea<strong>de</strong>r br = new BufferedRea<strong>de</strong>r (new InputStreamRea<strong>de</strong>r ( c o n e x i on. getInputStream ( ) ) ) ;24 try {25 S t r i n g r e s p u e s t a ;26 while ( ( r e s p u e s t a = br . r e a d L i n e ( ) ) != null )27 System . out . p r i n t l n ( r e s p u e s t a ) ;28 } f i n a l l y {29 pw . c l o s e ( ) ;30 br . c l o s e ( ) ;31 }32 } catch ( MalformedURLException e ) {33 e . p r i n t S t a c k T r a c e ( ) ;34 } catch ( IOException e ) {35 e . p r i n t S t a c k T r a c e ( ) ;36 }37 }3839 public s t a t i c void main ( S t r i n g [ ] a r g s ) {40 new EscribirAURL ( ) . e j e c u t a ( a r g s [ 0 ] ) ;41 }42 }Listado 15.3: Escritura a una URLEn el Listado 15.3, presta atención al segundo parámetro <strong>de</strong>l <strong>con</strong>structor<strong>de</strong> PrintWriter que es true para indicar que se haga auto-flush cada vez queescribimos en el stream.Si el protocolo que utilizamos en la <strong>con</strong>exión es HTTP, po<strong>de</strong>mos mo<strong>de</strong>larla referencia <strong>de</strong> respuesta <strong>de</strong>l método openConnection() al tipoHttpURLConnection. Esta nueva clase tiene métodos que nos permiten especificarel tipo <strong>de</strong> petición que se realiza public void setRequestMethod(Stringmethod) throws ProtocolException para indicar si la petición es GET,POST, HEAD, etc.; y otros métodos interesantes para saber el código <strong>de</strong> estado<strong>de</strong> la petición (public int getResponseCo<strong>de</strong>() throws IOException).2 Para ver el resultado en un navegador, teclea la dirección http://rubi.dlsi.uji.es/ oscar/PHP/nombre.php?nombre=oscary obtendrás un saludo como respuesta


15.2. TRABAJO CON SOCKETS 22515.2. Trabajo <strong>con</strong> SocketsLos Sockets son el ladrillo fundamental en comunicaciones a través <strong>de</strong>l protocoloTCP o UDP. Como en el caso <strong>de</strong>l trabajos <strong>con</strong> URLs veremos que una vezestablecida la <strong>con</strong>exión entre dos Sockets (cliente/servidor) todas las tareas <strong>de</strong>lectura y escritura entre los Sockets se realizarán sobre los Flujos que la claseSocket nos proporciona.15.2.1. ¿Qué es un Socket?Un Socket es cada uno <strong>de</strong> los extremos que se establece en una comunicación,en toda comunicación tendremos un Socket en el lado <strong>de</strong>l emisor y un Socketen el lado <strong>de</strong>l receptor.En el paquete java.net en<strong>con</strong>tramos clases para trabajar <strong>con</strong> Sockets tantobajo el protocolo TCP como bajo el protocolo UDP. Como recordatorio, elprotocolo TCP está orientado a la <strong>con</strong>exión, mientras que el protocolo UDPestá orientado al mensaje.15.2.2. Sockets bajo el protocolo TCPEn las <strong>con</strong>exiones bajo el protocolo TCP existe un canal <strong>de</strong> <strong>con</strong>exión entre elcliente y el servidor. Entre otras cosas el protocolo TCP garantiza la recepción<strong>de</strong> los datos en el mismo or<strong>de</strong>n en que se emiten y sin pérdidas. El símil que seutiliza para visualizar las <strong>con</strong>exiones bajo el protocolo TCP es el <strong>de</strong> una llamada<strong>de</strong> teléfono, en la que se reserva un canal por el que la información circula sinpérdidas y <strong>de</strong> manera or<strong>de</strong>nada.15.2.2.1. Sockets TCP en el lado <strong>de</strong>l servidorPara crear un Socket que acepte <strong>con</strong>exiones en un <strong>de</strong>terminado puerto, <strong>Java</strong>nos proporciona la clase java.net.ServerSocket, en el momento <strong>de</strong> crear unainstancia <strong>de</strong> esta clase indicamos el puerto <strong>de</strong> escucha. Para aceptar <strong>con</strong>exionesutilizamos el método public Socket accept() throws IOException, que esbloqueante, es <strong>de</strong>cir, la ejecución se <strong>de</strong>tiene en este método hasta que un clientese <strong>con</strong>ecta. Cada vez que un cliente se <strong>con</strong>ecta al Socket servidor, el métodoaccept() nos <strong>de</strong>volverá una referencia al Socket cliente (instancia <strong>de</strong> la claseSocket esta vez), a partir <strong>de</strong> la cual podremos obtener Flujos <strong>de</strong> lectura oescritura.El Listado 15.4 muestra un ejemplo <strong>de</strong> un sencillo Servidor que envía unsaludo a cada cliente que se le <strong>con</strong>ecta. En la línea 41 pue<strong>de</strong>s ver cómo la claseSocket nos <strong>de</strong>vuelve un Flujo <strong>de</strong> escritura para comunicarnos <strong>con</strong> el cliente.1 package red ;23 import j a v a . i o . IOException ;4 import j a v a . i o . P r i n t W r i t e r ;5 import j a v a . net . S e r v e r S o c k e t ;6 import j a v a . net . Socket ;78 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 {9 private S e r v i d o r S e n c i l l o ( ) {10 super ( ) ;11 }12


226CAPÍTULO 15. PROGRAMACIÓN PARA LA RED13 private void e j e c u t a ( int puerto ) {14 try {15 System . out . p r i n t l n ( " S e r v i d o r a la e s c u c h a " ) ;16 // Creamos un Socket s e r v i d o r a l a escucha en e l puerto i n d i c a d o17 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 ( puerto ) ;18 Socket c l i e n t e ;19 try {20 // Cada vez 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 o21 while ( ( c l i e n t e = s e r v i d o r . a c c e p t ( ) ) != null )22 new Thread (new Saludo ( c l i e n t e ) ) . s t a r t ( ) ;23 } f i n a l l y {24 s e r v i d o r . c l o s e ( ) ;25 }26 } catch ( IOException e ) {27 e . p r i n t S t a c k T r a c e ( ) ;28 }2930 }3132 private c l a s s Saludo implements Runnable {33 private Socket c l i e n t e ;3435 public Saludo ( Socket c l i e n t e ) {36 t h i s . c l i e n t e = c l i e n t e ;37 }3839 @Overri<strong>de</strong>40 public void run ( ) {41 System . out . p r i n t l n ( " C l i e n t e c o n e c t a d o " ) ;42 try {43 // Obtenemos un stream <strong>de</strong> e s c r i t u r a a p a r t i r d e l Socket d e l c l i e n t e44 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 ) ;45 pw . p r i n t l n ( " Hola d e s d e el s e r v i d o r " ) ;46 pw . c l o s e ( ) ;47 } catch ( IOException e ) {48 e . p r i n t S t a c k T r a c e ( ) ;49 }50 }51 }5253 public s t a t i c void main ( S t r i n g a r g s [ ] ) {54 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 ] ) ) ;55 }56 }Listado 15.4: Un Socket servidor que envía un saludo a cada cliente que se le<strong>con</strong>ectaSi, por ejemplo, inicias el servidor en el puerto 1234, pasando este entero porla línea <strong>de</strong> argumentos, podrás <strong>con</strong>ectarte al servidor <strong>de</strong>s<strong>de</strong> un navegador web enla dirección http://localhost:1234. Y obtendrás como resultado el mensaje<strong>de</strong> saludo. También pue<strong>de</strong>s <strong>con</strong>ectarte al servidor mediante telnet escribiendoen una <strong>con</strong>sola telnet localhost 1234.15.2.2.2. Sockets TCP en el lado <strong>de</strong>l clientePara <strong>con</strong>ectarnos a un Socket servidor <strong>de</strong>s<strong>de</strong> <strong>Java</strong> disponemos <strong>de</strong> la clasejava.net.Socket. Al crear una instancia <strong>de</strong> esta clase le pasamos la direccióna la que nos queremos <strong>con</strong>ectar y en que puerto, y una vez establecida la <strong>con</strong>exiónpodremos abrir Flujos <strong>de</strong> lectura y escritura.El Listado 15.5 muestra un ejemplo <strong>de</strong> un cliente que se <strong>con</strong>ecta al servidor<strong>de</strong>l Listado 15.4 para obtener un saludo como respuesta.1 package red ;2


15.2. TRABAJO CON SOCKETS 2273 import j a v a . i o . BufferedRea<strong>de</strong>r ;4 import j a v a . i o . IOException ;5 import j a v a . i o . InputStreamRea<strong>de</strong>r ;6 import j a v a . net . Socket ;7 import j a v a . net . UnknownHostException ;89 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 {10 private C l i e n t e S e n c i l l o ( ) {11 super ( ) ;12 }1314 private void e j e c u t a ( int puerto ) {15 try {16 // Me c o n e c t o a l s e r v i d o r l o c a l que escuha e s e s t e puerto17 Socket c l i e n t e = new Socket ( " l o c a l h o s t " , puerto ) ;18 try {19 // Recupero un stream <strong>de</strong> l e c t u r a20 BufferedRea<strong>de</strong>r br = new BufferedRea<strong>de</strong>r (new InputStreamRea<strong>de</strong>r ( c l i e n t e. getInputStream ( ) ) ) ;21 S t r i n g s a l u d o ;22 while ( ( s a l u d o = br . r e a d L i n e ( ) ) != null )23 System . out . p r i n t l n ( s a l u d o ) ;24 } f i n a l l y {25 i f ( c l i e n t e != null ) c l i e n t e . c l o s e ( ) ;26 }27 } catch ( UnknownHostException e ) {28 e . p r i n t S t a c k T r a c e ( ) ;29 } catch ( IOException e ) {30 e . p r i n t S t a c k T r a c e ( ) ;31 }32 }3334 public s t a t i c void main ( S t r i n g [ ] a r g s ) {35 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 ] ) ) ;36 }37 }Listado 15.5: Un Socket clinet que <strong>con</strong>ecta al anterior servidor para recibir unsaludo.15.2.3. Sockets bajo el protocolo UDPEn el caso <strong>de</strong>l Protocolo UDP no se garantiza que los mensajes recibidos lo haganen el mismo or<strong>de</strong>n que los enviados, y tampoco se garantiza que al receptorlleguen todos los mensajes <strong>de</strong>l emisor, existe la posibilidad <strong>de</strong> que alguno sepierda por el camino. En este caso el símil que se utiliza es el <strong>de</strong> comunicacionesa través <strong>de</strong> un servicio postal.En el caso <strong>de</strong>l protocolo UDP, al <strong>con</strong>trario que en el caso <strong>de</strong>l protocolo TCP,no disponemos <strong>de</strong> dos clases diferenciadas <strong>de</strong>pendiendo <strong>de</strong> si lo que queremoscrear es un Socket servidor o un Socket cliente.En el caso <strong>de</strong>l protocolo UDP disponemos <strong>de</strong> dos clases para realizar la comunicación.La clase DatagramPacket la utilizaremos para especificar los datosque queremos enviar o recibir. La clase DatagramSocket es la que se encarga <strong>de</strong>enviar o recibir cada uno <strong>de</strong> los DatagramPacket.Dependiendo <strong>de</strong>l <strong>con</strong>structor que utilicemos para instanciar la claseDatagramPacket estaremos creando un datagrama para enviar datos, o un datagramapara recibir datos.De modo análogo, <strong>de</strong>pendiendo <strong>de</strong>l <strong>con</strong>structor que utilicemos para instanciarla clase DatagramSocket estaremos creando un Socket UDP capaz <strong>de</strong> recibirdatos en un cierto puerto, o un Socket UDP capaz <strong>de</strong> enviar datos a un <strong>de</strong>terminadoservidor.


228CAPÍTULO 15. PROGRAMACIÓN PARA LA RED15.2.3.1. Sockets UDP en el lado <strong>de</strong>l servidorPara crear un Socket UDP en el lado <strong>de</strong>l servidor <strong>de</strong>bemos utilizar el <strong>con</strong>structor<strong>de</strong> la clase DatagramSocket que recibe como parámetro el puerto <strong>de</strong> <strong>con</strong>exiónpublic DatagramSocket(int puerto). Una vez creado el Socket UDP, la claseDatagramSocket nos permitirá tanto enviar como recibir paquetes en el puertoindicado.Para recibir paquetes necesitamos crear una instancia <strong>de</strong> la claseDatagramPacket indicando el buffer sobre el que se leerán los datos y su tamaño,por ejemplo new DatagramPacket(new byte[100], 100) nos permitirá leerhasta 100 bytes <strong>de</strong>l paquete recibido, si el paquete recibido <strong>con</strong>tiene más <strong>de</strong>100 bytes, el resto será ignorado. El Listado 15.6 muestra un ejemplo <strong>de</strong> lectura<strong>de</strong> un paquete UDP.1 byte [ ] b u f f e r = new byte [ 8 ∗ 1 0 2 4 ] ;2 DatagramPacket paquete = new DatagramPacket ( b u f f e r , b u f f e r . l e n g t h ) ;3 DatagramSocket s e r v i d o r = new DatagramSocket ( 1 2 3 4 5 ) ; // Puerto <strong>de</strong>escucha 123454 s e r v i d o r . r e c e i v e ( paquete ) ; // El método r e c e i v e e s b l o q u e a n t e5 I n e t S o c k e t A d d r e s s d i r e c c i o n C l i e n t e = paquete . g e t S o c k e t A d d r e s s ( ) ;6 System . out . p r i n t l n ( " Host 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 <strong>de</strong> un paquete mediante el protocolo UDP.Para enviar un paquete como <strong>con</strong>testación al cliente <strong>de</strong>l que se acaba <strong>de</strong>recibir un paquete crearíamos una nueva instancia <strong>de</strong> la clase DatagramPacketpero esta vez utilizando el <strong>con</strong>structor que incluye un tercer parámetro <strong>con</strong> la dirección<strong>de</strong> <strong>de</strong>stino <strong>de</strong>l paquete public DatagramPacket(byte[] buffer, inttamBuffer, SocketAddress direccion). El listado 15.7 es una <strong>con</strong>tinuación<strong>de</strong>l listado 15.6 don<strong>de</strong> el servidor envía un paquete <strong>de</strong> respuesta al cliente.7 b y t e s [ ] mensaje = " Hola " . g e t B y t e s ( ) ;8 DatagramPacket paqueteRespuesta = new DatagramPacket ( mensaje , mensaje .length , d i r e c c i o n C l i e n t e ) ;9 s e r v i d o r . send ( paqueteRespuesta ) ;Listado 15.7: Escritura <strong>de</strong> un paquete mediante el protocolo UDP.Como pue<strong>de</strong>s observar en el Listado 15.7, es posible utilizar la misma instancia<strong>de</strong> la clase DatagramSocket para enviar datos. La dirección a la cual se<strong>de</strong>be enviar el paquete la obtiene la clase DatagramSocket a partir <strong>de</strong> la claseDatagramPacket.15.2.3.2. Sockets UDP en el lado <strong>de</strong>l clientePara escribir un cliente utilizando el protocolo UDP utilizamos <strong>de</strong> nuevo las clasesDatagramPacket y DatagramSocket, pero esta vez, al crear una instancia <strong>de</strong>DatagramPacket, indicamos la dirección y el puerto <strong>de</strong>l servidor al que queremoshacer llegar el paquete, por ejemplo new DatagramPacket(new byte[1],1, inetAddress, 12345). Para crear la instancia <strong>de</strong> DatagramSocket utilizaremosel <strong>con</strong>structor por <strong>de</strong>fecto new DatagramSocket(). Finalmente, paraenviar el paquete utilizaremos <strong>de</strong> nuevo el método send(paquete) <strong>de</strong> la claseDatagramSocket. En el Listado 15.8 se muestra un ejemplo <strong>de</strong> envío <strong>de</strong> unpaquete hacia un servidor UDP.


15.2. TRABAJO CON SOCKETS 2291 DatagramPacket paquete = new DatagramPacket (new byte [ 1 ] , 1 , i n e t A d d r e s s ,12345) ;2 DatagramSocket s e r v i d o r = new DatagramSocket ( ) ;3 s e r v i d o r . send ( paquete ) ;Listado 15.8: Envío <strong>de</strong> un paquete mediante el protocolo UDP <strong>de</strong>s<strong>de</strong> un clientea un servidor.Igual que en el caso <strong>de</strong>l servidor, el cliente pue<strong>de</strong> quedar a la escucha <strong>de</strong> lospaquetes que reciba <strong>de</strong>s<strong>de</strong> el servidor, como se muestra en el Listado 15.9 quees <strong>con</strong>tinuación <strong>de</strong>l Listado 15.8.4 byte [ ] b u f f e r = new byte [ 8 ∗ 1 0 2 4 ] ;5 paquete = new DatagramPacket ( b u f f e r , b u f f e r . l e n g t h ) ;6 s e r v i d o r . r e c e i v e ( paquete ) ;Listado 15.9: Recepción <strong>de</strong> paquetes por parte <strong>de</strong> un cliente.Cuestiones.1. Tanto el método accept() <strong>de</strong> la clase ServerSocket como el métodoreceive() <strong>de</strong> la clase DatagramSocket son bloqueantes. ¿Qué <strong>de</strong>bes utilizarpara que tu aplicación no se que<strong>de</strong> ✭✭<strong>con</strong>gelada✮✮ esperando a que seretorne <strong>de</strong> estos dos métodos?.2. El protocolo UDP no comprueba que un paquete ha llegado efectivamentea su <strong>de</strong>stino. ¿Cómo podrías asegurarte <strong>de</strong> que sí que llega a su <strong>de</strong>stino?3. El protocolo UDP tampoco tiene en cuenta que el or<strong>de</strong>n <strong>de</strong> los paquetesrecibidos es el mismo que el <strong>de</strong> los paquetes enviados. ¿Cómo podríasreor<strong>de</strong>nar los paquetes que recibe tu aplicación para garantizar que siguenel mismo or<strong>de</strong>n <strong>con</strong> el que se enviaron?4. Al utilizar Sockets bajo el protocolo TCP, ¿Cómo pue<strong>de</strong>s darte cuenta <strong>de</strong>que la <strong>con</strong>exión entre el cliente y el servidor se ha cortado? ¿Y en el caso<strong>de</strong>l protocolo UDP?Ejercicios.1. Itenta crear una aplicación <strong>de</strong> chat. En la aplicación hay una parte <strong>de</strong>servidor, que atien<strong>de</strong> la <strong>con</strong>exión <strong>de</strong> nuevos clientes. Cada vez que uncliente se <strong>con</strong>ecta al servidor <strong>de</strong> chat queda registrado <strong>de</strong> modo que recibetodos los mensajes que envían el resto <strong>de</strong> clientes.Lecturas recomendadas.Una excelente referencia para casi cualquier tema relacionado <strong>con</strong> la programaciónpara la red es el libro <strong>de</strong> Elliotte Rusty Harold [12].


230CAPÍTULO 15. PROGRAMACIÓN PARA LA RED


Capítulo 16Patrones <strong>de</strong> diseñoContenidos16.1. Principios <strong>de</strong> POO . . . . . . . . . . . . . . . . . . . 23216.2. ¿Qué son los patrones <strong>de</strong> diseño? . . . . . . . . . . 23316.3. ¿Qué es el acoplamiento entre clases y por qué hayque evitarlo? . . . . . . . . . . . . . . . . . . . . . . 23316.4. Grupos <strong>de</strong> patrones <strong>de</strong> diseño . . . . . . . . . . . . 23316.5. El patrón <strong>de</strong> diseño Singleton . . . . . . . . . . . . 23316.5.1. Situación que intenta resolver . . . . . . . . . . . . . 23416.5.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . 23416.6. El patrón <strong>de</strong> diseño Factory Method . . . . . . . . 23516.6.1. Situación que intenta resolver . . . . . . . . . . . . . 23516.6.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . 23616.7. El patrón <strong>de</strong> diseño Abstract Factory . . . . . . . . 23816.7.1. Situación que intenta resolver . . . . . . . . . . . . . 23816.7.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . 23816.8. El patrón <strong>de</strong> diseño Strategy . . . . . . . . . . . . . 24416.8.1. Situación que intenta resolver . . . . . . . . . . . . . 24516.8.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . 24516.9. El patrón <strong>de</strong> diseño Observer . . . . . . . . . . . . 24716.9.1. Situación que intenta resolver . . . . . . . . . . . . . 24716.9.2. Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . 24816.10.El patrón <strong>de</strong> diseño Decorator . . . . . . . . . . . . 24916.10.1.Situación que intenta resolver . . . . . . . . . . . . . 25016.10.2.Ejemplo <strong>de</strong> implementación . . . . . . . . . . . . . . 250IntroducciónEn este capítulo se presenta, en primer lugar y a modo <strong>de</strong> resumen, los Principios<strong>de</strong> Programación Orientada a Objetos.A <strong>con</strong>tinuación se <strong>de</strong>fine lo que son los patrones <strong>de</strong> diseño software y por231


232CAPÍTULO 16. PATRONES DE DISEÑOqué son útiles.El grueso <strong>de</strong>l capítulo lo forma la presentación <strong>de</strong> algunos <strong>de</strong> los patrones <strong>de</strong>diseño más utilizados en el <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong> informáticos. Los patrones<strong>de</strong> diseño no están ligados a ningún lenguaje <strong>de</strong> programación en particular, sondirectrices que nos pue<strong>de</strong>n ayudar en la escritura <strong>de</strong> código.16.1. Principios <strong>de</strong> POOA lo largo <strong>de</strong> este libro se ha intentado presentar no sólo el lenguaje <strong>de</strong> programación<strong>Java</strong>, y las herramientas <strong>de</strong> ayuda en el <strong>de</strong>sarrollo <strong>de</strong> <strong>proyectos</strong>, si notambién las buenas prácticas a seguir en la codificación y en el diseño <strong>de</strong> nuestrasaplicaciones. Las buenas prácticas aplicadas al diseño <strong>de</strong> software orientado aobjetos <strong>con</strong>stituyen sus principios, unas normas <strong>de</strong> carácter general que <strong>con</strong>vieneseguir en la <strong>con</strong>strucción <strong>de</strong> software. Siguiendo las expuestas en las referencias[8] y [9] y <strong>de</strong> modo resumido y son:Encapsular lo que varía.Favorecer la composición frente a la herencia.Programar orientado a la interface no a la implementación.Evitar el acoplamiento entre clases.Reducir la responsabilidad <strong>de</strong> cada clase.Los principios SOLID son otro grupo <strong>de</strong> principios a tener en cuenta en eldiseño <strong>de</strong> software. Estos principios establecen:Single responsability Una clase <strong>de</strong>be tener una única responsabilidad quejustifique su existencia.Open close principle La <strong>de</strong>finición <strong>de</strong> una clase <strong>de</strong>be ser abierta para su extensiónpero cerrada para su modificación.Liskov substitution Siempre <strong>de</strong>be ser posible sustituir una clase padre porotra hija sin que cambie el comportamiento <strong>de</strong> la aplicación.Interface segregation Una clase sólo <strong>de</strong>be implementar un interface si es necesarioque ofrezca todos los métodos que <strong>de</strong>clara el interface, y no sólounos cuantos.Depen<strong>de</strong>ncy inversion Las clases no <strong>de</strong>ben crear instancias <strong>de</strong> otras clases<strong>con</strong> las que trabajen, la <strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> una clase <strong>con</strong> respecto <strong>de</strong> otra<strong>de</strong>be inyectarse <strong>de</strong>s<strong>de</strong> fuera <strong>de</strong> la clase.Estos principios generales pue<strong>de</strong>n <strong>con</strong>cretarse, a veces, es soluciones bien<strong>con</strong>ocidas a problemas recurrentes en el diseño <strong>de</strong>l software. Y precisamenteestas soluciones son lo que se <strong>con</strong>oce como Patrones <strong>de</strong> diseño.


16.2. ¿QUÉ SON LOS PATRONES DE DISEÑO? 23316.2. ¿Qué son los patrones <strong>de</strong> diseño?Es usual que, durante el <strong>de</strong>sarrollo <strong>de</strong> un proyecto informático, nos en<strong>con</strong>tremos<strong>de</strong> modo recurrente <strong>con</strong> el mismo tipo <strong>de</strong> problemas. Por ejemplo cómo garantizarla existencia <strong>de</strong> una única instancia para po<strong>de</strong>r acce<strong>de</strong>r a un <strong>de</strong>terminadodispositivo. O cómo estructurar una aplicación basada en un iterface gráficopara que me permita múltiples representaciones <strong>de</strong> los mismos datos. En esteúltimo caso ya has visto un patrón <strong>de</strong> diseño muy potente, MVC, en el capítulo11.Los patrones <strong>de</strong> diseño son soluciones bien <strong>con</strong>ocidas y ampliamente empleadaspara resolver problemas recurrentes en el diseño <strong>de</strong> aplicaciones informáticas.Cada uno <strong>de</strong> los patrones <strong>de</strong> diseño tiene, o suele tener, un nombreestandarizado, lo que <strong>de</strong>fine un vocabulario común que facilita el intercambio<strong>de</strong> i<strong>de</strong>as, y una plantilla <strong>de</strong> aplicación que muestra cuales son sus componentesy cómo se relacionan entre si.16.3. ¿Qué es el acoplamiento entre clases y porqué hay que evitarlo?Cuando al utilizar una clase <strong>de</strong>s<strong>de</strong> el código <strong>de</strong> otra, la clase cliente <strong>con</strong>oce <strong>de</strong>talles<strong>de</strong> la implementación <strong>de</strong> la clase que utiliza <strong>de</strong>cimos que las dos clases estánfuertemente acopladas. El acoplamiento muchas veces implica que al cambiar laimplementación <strong>de</strong> una clase, las clases cliente fuertemente acopladas <strong>con</strong> ella<strong>de</strong>jan <strong>de</strong> funcionar, <strong>de</strong>ben ser modificadas para reflejar los cambios en la claseinicial. Esta coyuntura finalmente <strong>de</strong>semboca en una serie <strong>de</strong> modificaciones encascada a lo largo y ancho <strong>de</strong>l código <strong>de</strong> la aplicación.Lo que hay que evitar, ante todo, es que una clase <strong>de</strong>penda <strong>de</strong> los <strong>de</strong>talles<strong>de</strong> implementación <strong>de</strong> otra para que pueda utilizarla. Y este es el principiobásico que en<strong>con</strong>tramos en todos los patrones <strong>de</strong> diseño, la in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> laimplementación <strong>con</strong>creta entre clases.16.4. Grupos <strong>de</strong> patrones <strong>de</strong> diseñoLos patrones <strong>de</strong> diseño se agrupan por su cometido, así nos en<strong>con</strong>tramos <strong>con</strong>patrones <strong>de</strong> diseño <strong>de</strong> creación (Singleton, Factory method, Abstract factory), <strong>de</strong>comportamiento (Strategy, Observer) y estructurales (Decorator) entre los más<strong>con</strong>ocidos sin ser una lista exhaustiva. Por cuestión <strong>de</strong> espacio presentamos losindicados entre paréntesis, <strong>de</strong>jando el resto para su <strong>con</strong>sulta en la bibliografía.16.5. El patrón <strong>de</strong> diseño SingletonAparentemente este es un patrón <strong>de</strong> diseño muy sencillo que acaba teniendouna implementación sofisticada cuando se utiliza en ambientes <strong>de</strong> programación<strong>con</strong>currente.


234CAPÍTULO 16. PATRONES DE DISEÑO16.5.1. Situación que intenta resolverEl patrón <strong>de</strong> diseño Singleton garantiza que sólo existe una instancia <strong>de</strong> una<strong>de</strong>terminada clase. La clase no se instancia <strong>con</strong> el operador new si no a través<strong>de</strong> la llamada a un método que siempre <strong>de</strong>vuelve la misma instancia y únicainstancia <strong>de</strong> la clase.Este patrón <strong>de</strong> diseño es útil cuando queremos garantiza la unicidad <strong>de</strong> unainstancia, por ejemplo nuestra aplicación sólo <strong>con</strong>oce una instancia <strong>de</strong> la claseque acce<strong>de</strong> a una impresora, o al sistema <strong>de</strong> ficheros.16.5.2. Ejemplo <strong>de</strong> implementaciónEn el listado 16.1 se muestra una implementación <strong>de</strong> este patrón <strong>de</strong> diseño. Ellas líneas 4-6 <strong>de</strong>finimos como private el <strong>con</strong>structor por <strong>de</strong>fecto <strong>de</strong> la clase, <strong>de</strong>esto modo prohibimos la creación <strong>de</strong> instancias <strong>de</strong> esta clase <strong>con</strong> el operadornew. Por otro lado, al existir únicamente el <strong>con</strong>structor por <strong>de</strong>fecto <strong>con</strong> accesoprivate no se pue<strong>de</strong> exten<strong>de</strong>r la clase. En este caso, el modificador final noes necesario, pero sirve para documentar la clase.En la línea 3, <strong>de</strong>finimos una referencia a la propia clase, que será la que<strong>de</strong>volvamos cada vez que se pida a través <strong>de</strong> la llamada al método getInstancia()<strong>de</strong>finido entra las líneas 8-12.1 public c l a s s S i n g l e t o n {2 private S i n g l e t o n i n s t a n c i a = null ;34 private S i n g l e t o n ( ) {5 super ( ) ;6 }78 public S i n g l e t o n g e t I n s t a n c i a ( ) {9 i f ( i n s t a n c i a == null )10 i n s t a n c i a = new S i n g l e t o n ( ) ;11 return i n s t a n c i a ;12 }13 }Listado 16.1: Implementación sencilla <strong>de</strong>l patrón SingletonComo ves, recuperamos la instancia llamando al método getInstancia()y no <strong>con</strong> el operador new. Esta implementación es completamente funcional sinuestra aplicación no utiliza hilos, pero en caso <strong>de</strong> utilizarlos po<strong>de</strong>mos en<strong>con</strong>trarnos<strong>con</strong> problemas al usar esta sencilla implementación. Veamos cual es elproblema que pue<strong>de</strong> aparecer, antes <strong>de</strong> ello, recor<strong>de</strong>mos que la intención <strong>de</strong>este patrón <strong>de</strong> diseño es que únicamente exista una instancia <strong>de</strong> la clase Singleton.Supongamos ahora que se están ejecutando simultáneamente dos hilos quequieren recuperar una instancia <strong>de</strong> la clase Singleton, y que por simplicidad sólo<strong>con</strong>tamos <strong>con</strong> un procesador (o un procesador <strong>con</strong> un único núcleo). Supongamosque uno <strong>de</strong> los hilos llama al método getInstancia(), que comprueba la<strong>con</strong>dición <strong>de</strong> la línea 9 y que se evalúa a false, y que justo <strong>de</strong>spués <strong>de</strong> evaluarla <strong>con</strong>dición y antes <strong>de</strong> crear la instancia en la línea 10, se ce<strong>de</strong> la ejecución alsegundo hilo, quien también evalúa la <strong>con</strong>dición <strong>de</strong> la línea 9 obteniendo false(ya que la instancia no fue creada por el primer hilo), y que, ahora sí, crea unainstancia <strong>de</strong> la clase Singleton. Cuando el hilo que está a la espera <strong>con</strong>tinúesu ejecución don<strong>de</strong> quedó (justo <strong>de</strong>spués <strong>de</strong> comprobar que no había ningunainstancia <strong>de</strong> Singleton creada), creará una nueva instancia <strong>de</strong> la clase Singleton


16.6. EL PATRÓN DE DISEÑO FACTORY METHOD 235ya que al no volver a comprobar la <strong>con</strong>dición para este hilo sigue siendo válidoque no existe ninguna instancia. Como resultado final nos en<strong>con</strong>traremos <strong>con</strong>dos instancias <strong>de</strong> la misma clase, justo lo que no <strong>de</strong>seábamos que ocurriera.La solución a este problema pasa por sincronizar el bloque <strong>de</strong> creación <strong>de</strong>la instancia <strong>de</strong> Singleton tal y como se muestra en el Listado 16.2. En estecaso, inmediatamente <strong>de</strong>spués <strong>de</strong> comprobado si ya hay una instancia creada,escribimos un bloque sincronizado, <strong>de</strong>ntro <strong>de</strong>l cual lo primero que volvemos acomprobar es si la instancia sigue sin estar creada, si no lo está la creamos <strong>de</strong>ntro<strong>de</strong>l bloque sincronizado garantizando que ningún otro hilo entrará en este bloquesi ya está en posesión <strong>de</strong> otro hilo. La doble comprobación es para evitar queno ocurra lo mismo que en el caso anterior, que justo <strong>de</strong>spués <strong>de</strong> comprobarla yantes <strong>de</strong> entrar en el bloque sincronizado otro hilo gane la carrera y ejecute elbloque sincronizado mientras el primero espera a seguir <strong>con</strong> la ejecución.1 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 {2 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 = null ;34 private S i n g l e t o n C o n c u r r e n t e ( ) {5 super ( ) ;6 }78 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 ( ) {9 i f ( i n s t a n c i a == null )10 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 ) {11 i f ( i n s t a n c i a == null )12 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 ( ) ;13 }14 return i n s t a n c i a ;15 }16 }Listado 16.2: Implementación sencilla <strong>de</strong>l patrón Singleton¿Por qué no hacemos una única comprobación <strong>de</strong>ntro <strong>de</strong>l bloque sincronizado,ya que en este momento garantizamos que sólo hay un hilo ejecutándolo?Para evitar el sobrecoste que implica la ejecución <strong>de</strong> bloques sincronizados. Sihacemos una primera comprobación fuera <strong>de</strong>l bloque sincronizado y obtenemosfalse nunca entraremos en el bloque sincronizado y no caeremos en el sobrecostetemporal que esto implica. Si eliminamos la comprobación fuera <strong>de</strong>l bloque,siempre tendremos que pugnar por el cerrojo <strong>de</strong>l bloque sincronizado <strong>con</strong> el<strong>con</strong>siguiente coste en tiempo <strong>de</strong> ejecución que esto supone.16.6. El patrón <strong>de</strong> diseño Factory MethodFactory Method es otro patrón <strong>de</strong> diseño <strong>de</strong>ntro <strong>de</strong>l grupo <strong>de</strong> patrones <strong>de</strong> diseño<strong>de</strong> creación. Este patrón <strong>de</strong> diseño es relativamente sencillo y las ventajas quepresenta su uso son muchas.16.6.1. Situación que intenta resolverTal y como hemos visto en la introducción <strong>de</strong> este capítulo, el <strong>de</strong>sacoplamientoentre clases muchas veces pasa porque una clase cliente no cree una instancia<strong>de</strong> otra clase <strong>con</strong> la que quiera trabajar. Supongamos, por ejemplo, que estamoscreando una aplicación que representa una fábrica <strong>de</strong> Vehículos pudiendo serestos Coches, Motos y Camiones. Si cada vez que necesitamos una instancia


236CAPÍTULO 16. PATRONES DE DISEÑO<strong>de</strong> un tipo <strong>con</strong>creto usamos el operador new, corremos el riesgo <strong>de</strong> que la implementación<strong>de</strong> las clases <strong>con</strong>cretas Coche, Moto, Camión cambie y nuestrocódigo <strong>de</strong>je <strong>de</strong> funcionar.Una manera <strong>de</strong> <strong>de</strong>sacoplar la clase cliente, la que quiere recuperar instancias<strong>de</strong> Vehículos <strong>con</strong>cretos, y las instancias <strong>con</strong>cretas <strong>de</strong> Coche, Moto, Camión es<strong>de</strong>finir una nueva clase encargada <strong>de</strong> crear las instancias <strong>de</strong> las clases <strong>con</strong>cretas y<strong>de</strong>volver las referencias no al tipo <strong>con</strong>creto si no a un interface o clase abstracta.16.6.2. Ejemplo <strong>de</strong> implementaciónPrimero escribimos un interface <strong>de</strong>l Listado 16.3 que es el tipo <strong>de</strong> datos abstractopara toda clase <strong>de</strong> Vehiculos, <strong>con</strong> in<strong>de</strong>pen<strong>de</strong>ncia <strong>de</strong> si son Coches, Motos oCamiones. Este interface cuenta <strong>con</strong> <strong>con</strong>stantes que i<strong>de</strong>ntifican a los distintostipos <strong>de</strong> vehículos que se pue<strong>de</strong>n crear.1 public interface Vehiculo {2 s t a t i c f i n a l int COCHE = 1 ;3 s t a t i c f i n a l int MOTO = 2 ;4 s t a t i c f i n a l int CAMION = 3 ;5 S t r i n g g e t D e s c r i p c i o n ( ) ;6 }Listado 16.3: El tipo <strong>de</strong> datos abstracto VehiculoEn los Listados 16.4, 16.5 y 16.6 aparecen las clases <strong>con</strong>cretas para cada uno<strong>de</strong> los tres tipos <strong>de</strong> Vehículos que la fábrica pue<strong>de</strong> crear.1 public c l a s s Coche implements V e h i c u l o {2 @Overri<strong>de</strong>3 public S t r i n g g e t D e s c r i p c i o n ( ) {4 return " Soy un c o c h e " ;5 }6 }Listado 16.4: Clase <strong>con</strong>creta que representa un Coche1 public c l a s s Moto implements V e h i c u l o {2 @Overri<strong>de</strong>3 public S t r i n g g e t D e s c r i p c i o n ( ) {4 return " Soy una moto " ;5 }6 }Listado 16.5: Clase <strong>con</strong>creta que representa una Moto1 public c l a s s Camion implements V e h i c u l o {2 @Overri<strong>de</strong>3 public S t r i n g g e t D e s c r i p c i o n ( ) {4 return " Soy un c a m i ó n " ;5 }6 }Listado 16.6: Clase <strong>con</strong>creta que representa un CamionCada una <strong>de</strong> las clases anteriores da una implementación <strong>con</strong>creta para elmétodo getDescripcion().


16.6. EL PATRÓN DE DISEÑO FACTORY METHOD 237El Listado 16.7 muestra la implementación <strong>de</strong> la fábrica <strong>de</strong> vehículos. Estaclase posee un único método estático que recibe el tipo <strong>de</strong>l vehículo que <strong>de</strong>seamosobtener. La fábrica crea la instancia <strong>de</strong>l tipo <strong>con</strong>creto y la <strong>de</strong>vuelve como unareferencia al tipo abstracto Vehículo.1 public c l a s s F a b r i c a V e h i c u l o s {2 public s t a t i c Vehiculo c r e a V e h i c u l o ( int t i p o ) {3 Vehiculo v e h i c u l o ;45 switch ( t i p o ) {6 case Vehiculo .COCHE:7 v e h i c u l o = new Coche ( ) ;8 break ;910 case Vehiculo .MOTO:11 v e h i c u l o = new Moto ( ) ;12 break ;1314 case Vehiculo .CAMION:15 v e h i c u l o = new Camion ( ) ;16 break ;1718 <strong>de</strong>fault :19 v e h i c u l o = new Coche ( ) ;20 break ;21 }2223 return v e h i c u l o ;24 }25 }Listado 16.7: La fábrica <strong>de</strong> vehículosPara finalizar, veamos cómo un cliente <strong>con</strong>creto trabaja <strong>con</strong> esta fábrica <strong>de</strong>vehículos. El cliente se muestra en el Listado 16.8. Lo interesante <strong>de</strong> este clientees que no crea en ningún momento una instancia <strong>con</strong>creta <strong>de</strong> ninguna clase, sino que <strong>de</strong>lega la creación <strong>de</strong> instancias <strong>con</strong>cretas en la clase FabricaVehiculos.Si la implementación <strong>de</strong> una clase <strong>con</strong>creta cambia, el cliente no lo percibe. Sila fábrica <strong>de</strong> vehículos incorpora nuevos vehículos, el cliente pue<strong>de</strong> utilizarlos,<strong>de</strong> nuevo, sin <strong>con</strong>ocer la implementación <strong>con</strong>creta.1 public c l a s s C l i e n t e {2 public s t a t i c void main ( S t r i n g [ ] a r g s ) {3 Vehiculo 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) ;4 System . out . 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 ( ) ) ;5 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) ;6 System . out . 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 ( ) ) ;7 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) ;8 System . out . 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 ( ) ) ;9 }10 }Listado 16.8: Un cliente <strong>de</strong> la fábrica <strong>de</strong> vehículosComo ves, este patrón <strong>de</strong> diseño es muy útil cuando se necesita crear clases<strong>con</strong>cretas <strong>de</strong> un mismo tipo <strong>de</strong> datos abstracto, que en el ejemplo mostrado era elinterface Vehiculo. Podríamos <strong>de</strong>cir que siempre estamos creando instancias<strong>de</strong>l mismo tipo <strong>de</strong> datos. Veamos un nuevo patrón <strong>de</strong> diseño que <strong>de</strong> algúnmodo amplia el patrón <strong>de</strong> diseño Factory Method, permitiéndonos la creación<strong>de</strong> familias <strong>de</strong> tipos <strong>de</strong> datos en vez <strong>de</strong> un único tipo <strong>de</strong> datos.


238CAPÍTULO 16. PATRONES DE DISEÑO16.7. El patrón <strong>de</strong> diseño Abstract FactoryEste nuevo patrón <strong>de</strong> diseño también pertenece al grupo <strong>de</strong> los patrones <strong>de</strong>diseño <strong>de</strong> creación, pero esta vez no crea un único tipo <strong>de</strong> datos si no que creafamilias <strong>de</strong> tipos <strong>de</strong> datos.16.7.1. Situación que intenta resolverSiguiendo <strong>con</strong> el ejemplo <strong>de</strong> la fábrica <strong>de</strong> vehículos, imagina esta vez que seintenta <strong>de</strong>scribir fábricas <strong>de</strong> vehículos genéricas. Dos ejemplos <strong>de</strong> fábricas <strong>de</strong>vehículos <strong>con</strong>cretas pue<strong>de</strong>n ser una fábrica <strong>de</strong> vehículos europea y otra fábrica<strong>de</strong> vehículos japonesa. Ambas fábricas <strong>de</strong> vehículos producen diferentes tipos<strong>de</strong> vehículos, que po<strong>de</strong>mos restringir a Turismos, Berlinas y Deportivos. Lo quedistingue un vehículo <strong>con</strong>creto, por ejemplo un Turismo creado en una fábricaeuropea o en una japonesa no es el proceso <strong>de</strong> <strong>con</strong>strucción, ya que ambosvehículos tienen motor, chasis y ruedas. Lo que distingue a un Turismo europeo<strong>de</strong> uno japones es que una fábrica europea <strong>de</strong> vehículos utiliza componentes europeos(ruedas europeas, chasis europeos, motores europeos), mientras que unafábrica japonesa utiliza componentes japoneses (ruedas japonesas, chasis japoneses,motores japoneses). Resumiendo, misma gama <strong>de</strong> productos (Vehículos)pero <strong>con</strong>struidos <strong>con</strong> componentes distintos (Motor, Chasis y Ruedas).La diferencia <strong>con</strong> el patrón <strong>de</strong> diseño Factory Mehod (Vehiculo) es que eneste se crea un sólo producto, mientras que en Abstract Factory se crea unagama <strong>de</strong> productos (Motor, Chasis y Ruedas).16.7.2. Ejemplo <strong>de</strong> implementaciónPara ver un ejemplo <strong>con</strong>creto vamos a empezar <strong>de</strong>scribiendo las entida<strong>de</strong>s queforman parte <strong>de</strong>l problema. En una fábrica, tanto si es europea como japonesase <strong>con</strong>struyen tres mo<strong>de</strong>los <strong>de</strong> vehículos:Turismos.Berlinas.Deportivos.Vamos a suponer un Turismo está formado únicamente por un Chasis y unMotor. Una Berlina está formada por un Chasis, Motor y Ruedas. Y finalmente,un Deportivo está formado por Chasis, Motor, Ruedas y Extras. En los Listados16.9 a 16.12 se muestra el código para los vehículos. En la interface Vehiculo,hemos añadido un método que permite obtener una <strong>de</strong>scripción <strong>de</strong> cada uno <strong>de</strong>los Vehiculos public void <strong>de</strong>scripcion().1 public abstract c l a s s V e h i c u l o {2 // p r o t e c t e d Rueda ruedas ;3 // p r o t e c t e d Chasis c h a s i s ;4 // p r o t e c t e d Motor motor ;5 // p r o t e c t e d Extras e x t r a s ;6 public abstract void d e s c r i p c i o n ( ) ;7 }Listado 16.9: Un Vehículo como abstracción <strong>de</strong> los tres mo<strong>de</strong>los que pue<strong>de</strong><strong>con</strong>struir una fábrica Turismos, Berlinas y Deportivos


16.7. EL PATRÓN DE DISEÑO ABSTRACT FACTORY 2391 public c l a s s Turismo extends V ehiculo {2 private Chasis c h a s i s ;3 private Motor motor ;45 public Turismo ( FabricaComponentes fabricaComponentes ) {6 c h a s i s = fabricaComponentes . c r e a C h a s i s ( ) ;7 motor = fabricaComponentes . creaMotor ( ) ;8 }910 @Overri<strong>de</strong>11 public void d e s c r i p c i o n ( ) {12 System . out . p r i n t l n ( " - - - D e s c r i p c i ó n <strong>de</strong> un T U R I S M O - - - " ) ;13 c h a s i s . d e s c r i p c i o n ( ) ;14 motor . d e s c r i p c i o n ( ) ;15 System . out . p r i n t l n ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " ) ;16 }17 }Listado 16.10: Un Turismo es una especialización <strong>de</strong> un Vehiculo1 public c l a s s B e r l i n a extends V eh i c u l o {2 private Chasis c h a s i s ;3 private Motor motor ;4 private Rueda ruedas ;56 public B e r l i n a ( FabricaComponentes fabricaComponentes ) {7 c h a s i s = fabricaComponentes . c r e a C h a s i s ( ) ;8 motor = fabricaComponentes . creaMotor ( ) ;9 ruedas = fabricaComponentes . creaRuedas ( ) ;10 }11 @Overri<strong>de</strong>12 public void d e s c r i p c i o n ( ) {13 System . out . p r i n t l n ( " - - - D e s c r i p c i ó n <strong>de</strong> una B E R L I N A - - - " ) ;14 c h a s i s . d e s c r i p c i o n ( ) ;15 motor . d e s c r i p c i o n ( ) ;16 ruedas . d e s c r i p c i o n ( ) ;17 System . out . p r i n t l n ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " ) ;18 }1920 }Listado 16.11: Una Berlina es una especialización <strong>de</strong> un Vehiculo1 public c l a s s Deportivo extends Vehiculo {2 private Chasis c h a s i s ;3 private Extras e x t r a s ;4 private Motor motor ;5 private Rueda ruedas ;67 public Deportivo ( FabricaComponentes fabricaComponentes ) {8 ruedas = fabricaComponentes . creaRuedas ( ) ;9 c h a s i s = fabricaComponentes . c r e a C h a s i s ( ) ;10 motor = fabricaComponentes . creaMotor ( ) ;11 e x t r a s = fabricaComponentes . c r e a E x t r a s ( ) ;12 }1314 @Overri<strong>de</strong>15 public void d e s c r i p c i o n ( ) {16 System . out . p r i n t l n ( " - - - D e s c r i p c i ó n <strong>de</strong> un D E P O R T I V O - - - " ) ;17 ruedas . d e s c r i p c i o n ( ) ;18 c h a s i s . d e s c r i p c i o n ( ) ;19 motor . d e s c r i p c i o n ( ) ;20 e x t r a s . d e s c r i p c i o n ( ) ;21 System . out . p r i n t l n ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " ) ;22 }2324 }


240CAPÍTULO 16. PATRONES DE DISEÑOListado 16.12: Un Deportivo es una especialización <strong>de</strong> un VehiculoVamos ahora a <strong>de</strong>tallar los componentes que forman parte <strong>de</strong> cada uno <strong>de</strong>los diferentes Vehiculos. Si el Vehículo es japones su Chasis es siempre ligeroy <strong>de</strong> aluminio, si es europeo su Chasis es siempre reforzado. El Motor <strong>de</strong> unVehiculo japones es siempre <strong>de</strong> muy bajo <strong>con</strong>sumo y bajas emisiones <strong>de</strong> CO2; sies europeo, el Motor es <strong>de</strong> alto rendimiento. Las Ruedas <strong>de</strong> un Vehículo japonesson siempre <strong>de</strong> muy larga duración; mientras que los Vehículos europeos tienenruedas <strong>de</strong> perfil bajo. Y finalmente, los Vehículos japoneses tienen extras <strong>de</strong>tipo <strong>de</strong>portivo japones y los europeos <strong>de</strong> tipo <strong>de</strong>portivo. El los listados 16.13a 16.24 se muestra cada uno <strong>de</strong> los interface y las especializaciones <strong>de</strong> cadacomponente que pue<strong>de</strong> formar parte <strong>de</strong> un Vehículo.1 public interface Chasis {2 void d e s c r i p c i o n ( ) ;3 }Listado 16.13: Un Chasis como abstracción.1 public c l a s s C h a s i s L i g e r o implements Chasis {2 @Overri<strong>de</strong>3 public void d e s c r i p c i o n ( ) {4 System . out . p r i n t l n ( " C h a s i s l i g e r o <strong>de</strong> a l u m i n i o . " ) ;5 }6 }Listado 16.14: Un ChasisLigero es una especialización <strong>de</strong> un Chasis queserá utilizado sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos japoneses.1 public c l a s s C h a s i s R e f o r z a d o implements Chasis {2 @Overri<strong>de</strong>3 public void d e s c r i p c i o n ( ) {4 System . out . p r i n t l n ( " C h a s i s r e f o r z a d o " ) ;5 }6 }Listado 16.15: Un ChasisReforzado es una especialización <strong>de</strong> un Chasis queserá utilizado sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos europeos1 public interface Extras {2 void d e s c r i p c i o n ( ) ;3 }Listado 16.16: Unos Extras como abstracción.1 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 Extras {23 @Overri<strong>de</strong>4 public void d e s c r i p c i o n ( ) {5 System . out . p r i n t l n ( " E x t r a s d e p o r t i v o s .... pero al e s t i l o j a p o n e s . " ) ;6 }78 }


16.7. EL PATRÓN DE DISEÑO ABSTRACT FACTORY 241Listado 16.17: Unos ExtrasDeportivosEstiloJapones es una especialización <strong>de</strong>unos Extras que serán utilizados sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos japoneses1 public c l a s s E x t r a s D e p o r t i v o s implements Extras {2 @Overri<strong>de</strong>3 public void d e s c r i p c i o n ( ) {4 System . out . p r i n t l n ( " E x t r a s d e p o r t i v o s . " ) ;5 }6 }Listado 16.18: Unos ExtrasDeportivos es una especialización <strong>de</strong> unos Extras queserán utilizados sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos europeos1 public interface Motor {2 void d e s c r i p c i o n ( ) ;3 }Listado 16.19: Un Motor como abstracción.1 public c l a s s MotorBajoConsumo implements Motor {23 @Overri<strong>de</strong>4 public void d e s c r i p c i o n ( ) {5 System . out . p r i n t l n ( " M o t o r <strong>de</strong> muy bajo c o n s u m o y b a j a s e m i s i o n e s <strong>de</strong> CO2. " ) ;6 }78 }Listado 16.20: Un MotorBajoCosumo es una especialización <strong>de</strong> un Motor queserá utilizado sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos japoneses1 public c l a s s MotorAltoRendimiento implements Motor {2 @Overri<strong>de</strong>3 public void d e s c r i p c i o n ( ) {4 System . out . p r i n t l n ( " M o t o r <strong>de</strong> alto r e n d i m i e n t o . " ) ;5 }6 }Listado 16.21: Un MotorAltoRendimienot es una especialización <strong>de</strong> un Motorque será utilizado sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos europeos1 public interface Rueda {2 void d e s c r i p c i o n ( ) ;3 }Listado 16.22: Una Rueda como abstracción.1 public c l a s s RuedaLargaDuracion implements Rueda {23 @Overri<strong>de</strong>4 public void d e s c r i p c i o n ( ) {5 System . out . p r i n t l n ( " R u e d a s <strong>de</strong> l a r g a d u r a c i ó n " ) ;6 }7


242CAPÍTULO 16. PATRONES DE DISEÑO8 }Listado 16.23: Una RuedaLargaDuracion es una especialización <strong>de</strong> una Ruedaque será utilizada sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos japoneses1 public c l a s s R u e d a P e r f i l B a j o implements Rueda {23 @Overri<strong>de</strong>4 public void d e s c r i p c i o n ( ) {5 System . out . p r i n t l n ( " R u e d a <strong>de</strong> p e r f i l bajo . " ) ;6 }78 }Listado 16.24: Una RuedaPerfilBajo es una especialización <strong>de</strong> una Rueda queserá utilizada sólo en la <strong>con</strong>strucción <strong>de</strong> Vehículos europeosEs el momento <strong>de</strong> <strong>con</strong>struir las fábricas <strong>de</strong> componentes. Las fábricas <strong>de</strong> componentesson las encargadas <strong>de</strong> crear cada uno <strong>de</strong> los componentes que formanun Vehículo <strong>de</strong>pendiendo <strong>de</strong> si estamos en Europa o Japón. Para implementarlas,vamos a utilizar el patrón <strong>de</strong> diseño Factory Method. Los listados 16.25 a16.27 muestran el código para cada una <strong>de</strong> las fábricas <strong>de</strong> componentes.1 public interface Rueda {2 void d e s c r i p c i o n ( ) ;3 }Listado 16.25: Una FabricaComponentes como abstracción.1 public c l a s s FabricaComponentesJaponesa implements FabricaComponentes {23 @Overri<strong>de</strong>4 public Rueda creaRuedas ( ) {5 return new RuedaLargaDuracion ( ) ;6 }78 @Overri<strong>de</strong>9 public Chasis c r e a C h a s i s ( ) {10 return new C h a s i s L i g e r o ( ) ;11 }1213 @Overri<strong>de</strong>14 public Motor creaMotor ( ) {15 return new MotorBajoConsumo ( ) ;16 }1718 @Overri<strong>de</strong>19 public Extras c r e a E x t r a s ( ) {20 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 ( ) ;21 }2223 }Listado 16.26: Una FabricaComponentesJaponesa es una especialización <strong>de</strong> unaFabricaComponentes que crea los distintos componentes para cada tipo <strong>de</strong>Vehículos japoneses1 public c l a s s FabricaComponentesEuropea implements FabricaComponentes {23 @Overri<strong>de</strong>


16.7. EL PATRÓN DE DISEÑO ABSTRACT FACTORY 2434 public Rueda creaRuedas ( ) {5 return new R u e d a P e r f i l B a j o ( ) ;6 }78 @Overri<strong>de</strong>9 public Chasis c r e a C h a s i s ( ) {10 return new C h a s i s R e f o r z a d o ( ) ;11 }1213 @Overri<strong>de</strong>14 public Motor creaMotor ( ) {15 return new MotorAltoRendimiento ( ) ;16 }1718 @Overri<strong>de</strong>19 public Extras c r e a E x t r a s ( ) {20 return new E x t r a s D e p o r t i v o s ( ) ;21 }2223 }Listado 16.27: Una FabricaComponentesEuropea es una especialización <strong>de</strong> unaFabricaComponentes que crea los distintos componentes para cada tipo <strong>de</strong>Vehículos europeosYa nos en<strong>con</strong>tramos en la recta final para acabar <strong>de</strong> ver la implementación<strong>de</strong> este patrón <strong>de</strong> diseño. Vemos por último, la implementación <strong>de</strong> cada una <strong>de</strong>las fábricas <strong>de</strong> vehículos, la japonesa y la europea en los listados 16.28 al 16.30.1 public interface F a b r i c a V e h i c u l o s {2 Vehiculo c r e a V e h i c u l o ( TipoVehiculo t i p o V e h i c u l o ) ;3 }Listado 16.28: Una FabricaVehiculos como abstracción.1 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 {23 @Overri<strong>de</strong>4 public Vehiculo c r e a V e h i c u l o ( TipoVehiculo t i p o V e h i c u l o ) {5 FabricaComponentes fabricaComponentes = new FabricaComponentesJaponesa( ) ;6 Vehiculo v e h i c u l o ;78 switch ( t i p o V e h i c u l o ) {9 case TURISMO:10 System . out . p r i n t l n ( " F a b r i c a n d o un t u r i s m o j a p o n e s ... " ) ;11 v e h i c u l o = new Turismo ( fabricaComponentes ) ;12 break ;1314 case BERLINA :15 System . out . p r i n t l n ( " F a b r i c a n d o una b e r l i n a j a p o n e s a ... " ) ;16 v e h i c u l o = new B e r l i n a ( fabricaComponentes ) ;17 break ;1819 case DEPORTIVO:20 System . out . p r i n t l n ( " F a b r i c a n d o un d e p o r t i v o j a p o n e s ... " ) ;21 v e h i c u l o = new Deportivo ( fabricaComponentes ) ;22 break ;2324 <strong>de</strong>fault :25 System . out . p r i n t l n ( " F a b r i c a n d o un t u r i s m o j a p o n e s ... " ) ;26 v e h i c u l o = new Turismo ( fabricaComponentes ) ;27 break ;28 }2930 return v e h i c u l o ;31 }32


244CAPÍTULO 16. PATRONES DE DISEÑO33 }Listado 16.29: Una FabricaVehiculosJaponesa es una especialización <strong>de</strong> unaFabricaVehiculos que crea los distintos tipos <strong>de</strong> Vehículos japoneses1 public c l a s s F a bricaVehiculosEuropea implements F a b r i c a V e h i c u l o s {23 @Overri<strong>de</strong>4 public Vehiculo c r e a V e h i c u l o ( TipoVehiculo t i p o V e h i c u l o ) {5 FabricaComponentes fabricaComponentes = new FabricaComponentesEuropea( ) ;6 Vehiculo v e h i c u l o = null ;78 switch ( t i p o V e h i c u l o ) {9 case TURISMO:10 System . out . p r i n t l n ( " F a b r i c a n d o un t u r i s m o e u r o p e o ... " ) ;11 v e h i c u l o = new Turismo ( fabricaComponentes ) ;12 break ;1314 case BERLINA :15 System . out . p r i n t l n ( " F a b r i c a n d o una b e r l i n a e u r o p e a ... " ) ;16 v e h i c u l o = new B e r l i n a ( fabricaComponentes ) ;17 break ;1819 case DEPORTIVO:20 System . out . p r i n t l n ( " F a b r i c a n d o un d e p o r t i v o e u r o p e o ... " ) ;21 v e h i c u l o = new Deportivo ( fabricaComponentes ) ;22 break ;2324 <strong>de</strong>fault :25 System . out . p r i n t l n ( " F a b r i c a n d o un t u r i s m o e u r o p e o ... " ) ;26 v e h i c u l o = new Turismo ( fabricaComponentes ) ;27 break ;28 }2930 return v e h i c u l o ;31 }3233 }Listado 16.30: Una FabricaVehiculosEuropea es una especialización <strong>de</strong> unaFabricaVehiculos que crea los distintos tipos <strong>de</strong> Vehículos europeosEl <strong>de</strong>talle <strong>de</strong> interés en los listados 16.29 y 16.30 está en la línea 5 <strong>de</strong> amboslistados, es la fábrica <strong>de</strong> vehículos japonesa la que se provee <strong>de</strong> componentesjaponeses y la fábrica europea la que se provee <strong>de</strong> componentes europeos.Otro ejemplo que comúnmente se utiliza como ejemplo <strong>de</strong>l patrón <strong>de</strong> diseñoAbstract Factory es la programación <strong>de</strong> un framework para componer ventanasen distintos sistemas operativos. Las ventanas que queremos crear están formadaspor el mismo tipo <strong>de</strong> componentes (botones, listas, combo-box, etcétera),pero quien proporciona los componentes es el sistema operativo sobre el quese vaya a ejecutar la aplicación, <strong>de</strong> modo que, por ejemplo, se crean botonesdistintos si la fábrica <strong>de</strong> componentes es, digamos por caso, Mac OS X que si lafábrica es Linux.16.8. El patrón <strong>de</strong> diseño StrategyEste patrón es el primero, <strong>de</strong> los dos que vamos a ver, <strong>de</strong>l grupo <strong>de</strong> patrones <strong>de</strong>comportamiento.


16.8. EL PATRÓN DE DISEÑO STRATEGY 24516.8.1. Situación que intenta resolverCuando intentamos resolver un problema <strong>con</strong>creto, en la mayoría <strong>de</strong> las ocasionesexiste más <strong>de</strong> un algoritmo para en<strong>con</strong>trar la solución. Piensa por ejemploen los algoritmos <strong>de</strong> or<strong>de</strong>nación, el objetivo <strong>de</strong> todos ellos es el mismo, or<strong>de</strong>naruna secuencia <strong>de</strong> elemento teniendo en cuenta cierta función <strong>de</strong> comparaciónentre ellos; tomando un caso sencillo po<strong>de</strong>mos <strong>con</strong>cretar que los elementos sonnúmeros naturales. Para or<strong>de</strong>nar una secuencia <strong>de</strong> números naturales po<strong>de</strong>mosutilizar el algoritmo <strong>de</strong> intercambio, el algoritmo <strong>de</strong> la burbuja o el algoritmoquicksort.La codificación más flexible será aquella que permita intercambiar el algoritmo<strong>de</strong> or<strong>de</strong>nación <strong>con</strong> el mínimo, o nulo, impacto sobre la aplicación. Elpatrón Strategy nos dice que <strong>de</strong>bemos encapsular cada algoritmo <strong>de</strong>ntro <strong>de</strong> unaclase y hacer estas clases intercambiables para el cliente que las use <strong>de</strong> modotransparente.16.8.2. Ejemplo <strong>de</strong> implementaciónComo ejemplo <strong>de</strong> implementación, supongamos que nuestro algoritmo cuentanúmeros naturales. Existirán casos en los que nos interese <strong>con</strong>tar <strong>de</strong> modo ascen<strong>de</strong>ntey otros casos en los que nos interese <strong>con</strong>tar <strong>de</strong> modo <strong>de</strong>scen<strong>de</strong>nte.Incluso pue<strong>de</strong> que a veces nos interese <strong>con</strong>tar <strong>de</strong> modo ascen<strong>de</strong>nte sólo númerospares o sólo números impares. Fíjate que en los cuatros casos anteriores elalgoritmo es el mismo: Contar números.Para hacer los algoritmos intercambiables, todos ellos van a implementar elinterface que se muestra en el listado 16.31. Las cuatro implementaciones <strong>de</strong><strong>con</strong>tadores particulares se muestran en los Listados 16.32 al 16.35.1 public interface Contador {2 s t a t i c f i n a l int ASCENDENTE = 1 ;3 s t a t i c f i n a l int DESCENDENTE = 2 ;4 s t a t i c f i n a l int PARES = 3 ;5 s t a t i c f i n a l int IMPARES = 4 ;67 S t r i n g cuenta ( ) ;8 }Listado 16.31: Este es el comportamiento común a todos los algoritmo: Contar1 public c l a s s ContadorAscen<strong>de</strong>nte implements Contador {2 @Overri<strong>de</strong>3 public S t r i n g cuenta ( ) {4 S t r i n g cuenta = " " ;56 for ( int i = 0 ; i < 1 0 ; i ++)7 cuenta += i + " , " ;89 return cuenta ;10 }11 }Listado 16.32: Implementación <strong>de</strong> un <strong>con</strong>tador ascen<strong>de</strong>nte.1 public c l a s s ContadorDescen<strong>de</strong>nte implements Contador {23 @Overri<strong>de</strong>


246CAPÍTULO 16. PATRONES DE DISEÑO4 public S t r i n g cuenta ( ) {5 S t r i n g cuenta = " " ;67 for ( int i = 9 ; i >= 0 ; i −−)8 cuenta += i + " , " ;910 return cuenta ;11 }1213 }Listado 16.33: Implementación <strong>de</strong> un <strong>con</strong>tador <strong>de</strong>scen<strong>de</strong>nte.1 public c l a s s ContadorImpares implements Contador {23 @Overri<strong>de</strong>4 public S t r i n g cuenta ( ) {5 S t r i n g cuenta = " " ;67 for ( int i = 1 ; i < 1 0 ; i += 2)8 cuenta += i + " , " ;910 return cuenta ;11 }1213 }Listado 16.34: Implementación <strong>de</strong> un <strong>con</strong>tador <strong>de</strong> números impares.1 public c l a s s ContadorPares implements Contador {23 @Overri<strong>de</strong>4 public S t r i n g cuenta ( ) {5 S t r i n g cuenta = " " ;67 for ( int i = 0 ; i < 1 0 ; i += 2)8 cuenta += i + " , " ;910 return cuenta ;11 }1213 }Listado 16.35: Implementación <strong>de</strong> un <strong>con</strong>tador <strong>de</strong> números pares.Ahora escribamos un cliente que pueda utilizar estos algoritmos <strong>de</strong> modointercambiable, tal y como muestra el Listado 16.36:1 import c o n t a r . Contador ;23 public c l a s s C l i e n t e {4 private Contador <strong>con</strong>tador ;56 public void cuenta ( ) {7 <strong>con</strong>tador . cuenta ( ) ;8 }910 public void setContador ( Contador <strong>con</strong>tador ) {11 t h i s . <strong>con</strong>tador = <strong>con</strong>tador ;12 }13 }Listado 16.36: Un cliente que pue<strong>de</strong> utilizar cualquiera <strong>de</strong> los anterioresalgoritmo para <strong>con</strong>tar.


16.9. EL PATRÓN DE DISEÑO OBSERVER 247Esta clase Cliente tiene una característica interesante para po<strong>de</strong>r utilizarcualquier tipo <strong>de</strong> algoritmo para <strong>con</strong>tar, el algoritmo particular se le inyecta através <strong>de</strong>l método public void setContador(Contador <strong>con</strong>tador). A esta técnica<strong>de</strong> relación entre clase se le llama Inyección <strong>de</strong> Depen<strong>de</strong>ncias o Inversión <strong>de</strong>Control.Finalmente, y por completar el ejemplo, el Listado 16.37 muestra cómo utilizarla clase Cliente inyectándole los cuatro tipos <strong>de</strong> <strong>con</strong>tadores.1 import c o n t a r . ContadorAscen<strong>de</strong>nte ;2 import c o n t a r . ContadorDescen<strong>de</strong>nte ;3 import c o n t a r . ContadorImpares ;4 import c o n t a r . ContadorPares ;56 public c l a s s P r i n c i p a l {7 private void e j e c u t a ( ) {8 C l i e n t e c l i e n t e = new C l i e n t e ( ) ;9 c l i e n t e . setContador (new ContadorAscen<strong>de</strong>nte ( ) ) ;10 c l i e n t e . cuenta ( ) ;11 c l i e n t e . setContador (new ContadorDescen<strong>de</strong>nte ( ) ) ;12 c l i e n t e . cuenta ( ) ;13 c l i e n t e . setContador (new ContadorPares ( ) ) ;14 c l i e n t e . cuenta ( ) ;15 c l i e n t e . setContador (new ContadorImpares ( ) ) ;16 c l i e n t e . cuenta ( ) ;17 }1819 public s t a t i c void main ( S t r i n g [ ] a r g s ) {20 new P r i n c i p a l ( ) . e j e c u t a ( ) ;21 }22 }Listado 16.37: Po<strong>de</strong>mos cambiar dinámicamente el tipo <strong>de</strong> <strong>con</strong>tador que utilizala clase Cliente.16.9. El patrón <strong>de</strong> diseño ObserverObserver es otro patrón <strong>de</strong> diseño <strong>de</strong> comportamiento. Lo hemos utilizado ampliamenteen el capítulo 11 <strong>de</strong>dicado a la programación <strong>de</strong> interfaces gráficos <strong>de</strong>usuario.16.9.1. Situación que intenta resolverCuando intentamos monitorizar el estado, por ejemplo, <strong>de</strong> un atributo en una<strong>de</strong>terminada instancia, la opción directa, y altamente ineficiente, es interrogarcada cierto tiempo por el estado <strong>de</strong> ese atributo. La ineficiencia <strong>de</strong> este métodose <strong>de</strong>be a que aunque no cambie el estado <strong>de</strong>l atributo, estamos <strong>con</strong>sumiendotiempo en averiguar si lo ha hecho.Es más sencillo que sea la propia instancia quien nos avise <strong>de</strong> que su atributoha cambiado <strong>de</strong> valor, no es el cliente el que <strong>con</strong>sulta, si no que espera a quela instancia monitorizada le avise <strong>de</strong>l cambio. Este comportamiento se <strong>con</strong>ocecomo Principio <strong>de</strong> Hollywood que se pue<strong>de</strong> resumir en la frase No me llames,ya te llamaré yo.Recuerda cómo programábamos, en Swing, los escuchadores para un componentegráfico, por ejemplo un botón. Lo que hacíamos era escribir un escuchadorque era notificado cada vez que ocurría un evento sobre el botón. Y este es, precisamente,el patrón <strong>de</strong> diseño Observer.


248CAPÍTULO 16. PATRONES DE DISEÑO16.9.2. Ejemplo <strong>de</strong> implementaciónComo ejemplo, escribamos una pequeña novela <strong>de</strong> espías basada en la guerrafría, cuando aún existía el KGB. El KGB tiene espías que informan <strong>de</strong> todossus movimientos a sus superiores. Un mismo espía pue<strong>de</strong> trabajar para más <strong>de</strong>un superior, el mismo espía pue<strong>de</strong> estar trabajando al mismo tiempo para sujefe directo y para el ministerio <strong>de</strong>l interior. Quien genera los informes (eventos)es el espía, el espía está siendo observado; y quien recibe los informes son sussuperiores que actúan como observadores. Para que los superiores <strong>de</strong> un espíareciban mensajes establecen el mecanismo <strong>de</strong> que el espía haga una llamada almétodo informaObservadores(String accion) <strong>de</strong> sus jefes. Y un espía <strong>con</strong>oce entodo momento quienes son los jefes para los que trabaja, los mantiene en unlista don<strong>de</strong> pue<strong>de</strong> añadir nuevos jefes para informar cuando inicia una misión,o eliminarlos cuando acaba su misión.Los Listados 16.38 y 16.39 muestran el código referente al espía.1 public interface Observado {2 public void addObservador ( Observador o b s e r v a d o r ) ;3 public void removeObservador ( Observador o b s e r v a d o r ) ;4 public void informaObservadores ( S t r i n g a c c i o n ) ;5 }Listado 16.38: Esta interface es una abstracción <strong>de</strong>l comportamiento comúna todos los espías.1 import j a v a . u t i l . A r r a y L i s t ;2 import j a v a . u t i l . L i s t ;34 public c l a s s EspiaKGB implements Observado {5 private S t r i n g nombre ;6 private L i s t o b s e r v a d o r e s = new ArrayList () ;789 public EspiaKGB ( S t r i n g nombre ) {10 super ( ) ;11 t h i s . nombre = nombre ;12 }1314 @Overri<strong>de</strong>15 public void addObservador ( Observador o b s e r v a d o r ) {16 o b s e r v a d o r e s . add ( o b s e r v a d o r ) ;17 }1819 @Overri<strong>de</strong>20 public void removeObservador ( Observador o b s e r v a d o r ) {21 o b s e r v a d o r e s . remove ( o b s e r v a d o r ) ;22 }2324 @Overri<strong>de</strong>25 public void informaObservadores ( S t r i n g a c c i o n ) {26 for ( Observador o b s e r v a d o r : o b s e r v a d o r e s )27 o b s e r v a d o r . i n f o r m e ( nombre + " : " + a c c i o n ) ;28 }29 }Listado 16.39: Esta es la clase que representa a un espía.Es a través <strong>de</strong>l método informaObservadores(String accion) por el que elespía envía los informes a todos y cada uno <strong>de</strong> sus jefes. Los Listados 16.40 y16.41 muestran el código relativo a los jefes.


16.10. EL PATRÓN DE DISEÑO DECORATOR 2491 public interface Observador {2 public void i n f o r m e ( S t r i n g evento ) ;3 }Listado 16.40: Esta interface es una abstracción <strong>de</strong>l comportamiento comúna todos los jefes.1 public c l a s s JefeEspiasKGB implements Observador {2 private S t r i n g nombre ;34 public JefeEspiasKGB ( ) {5 super ( ) ;6 nombre = " A n ó n i m o " ;7 }89 public JefeEspiasKGB ( S t r i n g nombre ) {10 super ( ) ;11 t h i s . nombre = nombre ;12 }1314 @Overri<strong>de</strong>15 public void i n f o r m e ( S t r i n g evento ) {16 System . out . p r i n t l n ( nombre + " r e c i b e <strong>de</strong> " + evento ) ;17 }1819 }Listado 16.41: Esta es la clase que representa a un jefe <strong>de</strong> espía.Y finalmente en el Listado 16.42 escribimos una sencillísima novela <strong>de</strong> espías.1 public c l a s s PruebaEspias {2 private void e j e c u t a ( ) {3 Observado e s p i a B o r i s = new EspiaKGB ( " B o r i s " ) ;4 Observador jefeBorisKGB = new JefeEspiasKGB ( " Jefe <strong>de</strong> B o r i s " ) ;5 e s p i a B o r i s . addObservador ( jefeBorisKGB ) ;6 Observador ministroDefensaURSS = new JefeEspiasKGB ( " M i n i s t e r i o <strong>de</strong>li n t e r i o r " ) ;7 e s p i a B o r i s . addObservador ( ministroDefensaURSS ) ;8 e s p i a B o r i s . informaObservadores ( " E s t o y s i g u i e n d o al topo " ) ;9 }1011 public s t a t i c void main ( S t r i n g [ ] a r g s ) {12 new PruebaEspias ( ) . e j e c u t a ( ) ;13 }14 }Listado 16.42: Cada vez que Boris realiza una acción informa a sus superiores.Y este es el resultado <strong>de</strong> su ejecución:Jefe <strong>de</strong> Boris recibe <strong>de</strong> Boris: Estoy siguiendo al topoMinisterio <strong>de</strong>l interior recibe <strong>de</strong> Boris: Estoy siguiendo al topo16.10. El patrón <strong>de</strong> diseño DecoratorEste es el único patrón <strong>de</strong> diseño estructural que vamos a ver. Igual que el patrónObserver, este patrón es ampliamente utilizado <strong>Java</strong>, en este caso en el paquetejava java.io <strong>de</strong> entrada/salida en <strong>Java</strong>.


250CAPÍTULO 16. PATRONES DE DISEÑO16.10.1. Situación que intenta resolverHemos visto algunos ejemplos <strong>de</strong> este patrón en las clases <strong>de</strong> entrada/salida,cuando íbamos recubriendo una clase <strong>de</strong> partida <strong>con</strong> nuevas clases hasta alcanzaruna clase <strong>con</strong> la funcionalidad que necesitábamos.En algunos casos necesitamos añadir nuevas características a nuestras clasespero el uso directo <strong>de</strong> la herencia dispara exponencialmente el número <strong>de</strong> clasesque tendríamos que implementar. Veámoslo <strong>con</strong> un ejemplo. Supongamos queestamos <strong>de</strong>sarrollando una aplicación para un <strong>con</strong>cesionario <strong>de</strong> coches. El cochebásico <strong>de</strong> cada serie tiene un precio, y este precio se incrementa cuando elcomprador va añadiendo nuevos extras al coche. Por ejemplo, sobre el cochebásico po<strong>de</strong>mos elegir pintura metalizada o aire a<strong>con</strong>dicionado. Si cada extraestuviese codificado como una nueva clase que extien<strong>de</strong> a la clase que representael coche básico <strong>de</strong>beríamos escribir una nueva clase para el coche básico <strong>con</strong>aire a<strong>con</strong>dicionado, otra para el coche básico <strong>con</strong> pintura metalizada, y comono, una tercera clase hija para el caso en que algún cliente quiera añadir airea<strong>con</strong>dicionado y pintura metalizada al coche básico. Claramente el uso <strong>de</strong> laherencia no es buena i<strong>de</strong>a en este caso.Lo que necesitamos es que cada uno <strong>de</strong> los extras se añada sobre la clasebase <strong>de</strong> manera in<strong>de</strong>pendiente <strong>de</strong>l resto <strong>de</strong> los extras, y fíjate que no por ello<strong>de</strong>jamos <strong>de</strong> tener un vehículo.La i<strong>de</strong>a <strong>de</strong>l patrón <strong>de</strong> diseño Decorator es tomar una clase base e ir añadiéndolenuevas características sin utilizar exclusivamente la herencia.16.10.2. Ejemplo <strong>de</strong> implementaciónComo ejemplo <strong>de</strong> implementación vamos a utilizar una pequeña modificación<strong>de</strong>l ejemplo expuesto en la sección anterior, un <strong>con</strong>cesionario que quiere calcularel precio final <strong>de</strong> los vehículos que ven<strong>de</strong> pudiendo ser estos coches o camiones.Al vehículo básico el comprador le pue<strong>de</strong> añadir extras, tales como aire a<strong>con</strong>dicionadoo pintura metalizada.Abstraigamos la i<strong>de</strong>a <strong>de</strong> un Vehículo como una clase abstracta <strong>de</strong> la queCoche y Camión serán dos clases <strong>con</strong>cretas, tal y como muestran los Listados16.43 a 16.45.1 public abstract c l a s s V e h i c u l o {2 private S t r i n g d e s c r i p c i o n ;3 private f l o a t p r e c i o ;45 public Vehiculo ( ) {6 super ( ) ;7 }89 public Vehiculo ( 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 ) {10 super ( ) ;11 t h i s . d e s c r i p c i o n = d e s c r i p c i o n ;12 t h i s . p r e c i o = p r e c i o ;13 }1415 public S t r i n g g e t D e s c r i p c i o n ( ) {16 return d e s c r i p c i o n ;17 }181920 public f l o a t g e t P r e c i o ( ) {21 return p r e c i o ;22 }


16.10. EL PATRÓN DE DISEÑO DECORATOR 25123 }Listado 16.43: Abstracción <strong>de</strong> un Vehículo.1 public c l a s s Coche extends V ehiculo {2 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 ) {3 super ( d e s c r i p c i o n , p r e c i o ) ;4 }5 }Listado 16.44: Un Coche como clase <strong>con</strong>creta que extien<strong>de</strong> a Vehículo.1 public c l a s s Camion extends Vehiculo {2 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 ) {3 super ( d e s c r i p c i o n , p r e c i o ) ;4 }5 }Listado 16.45: Un Camión como clase <strong>con</strong>creta que extien<strong>de</strong> a Vehículo.La clase que <strong>de</strong>cora a Vehículo es VehiculoConExtras y es ella quien resuelve<strong>de</strong> una manera elegante el problema, por una parte extien<strong>de</strong> a Vehículo ya queun VehiculoConExtras sigue siendo un Vehículo, y por otra parte <strong>con</strong>tiene unareferencia al Vehículo que <strong>de</strong>cora para po<strong>de</strong>r <strong>de</strong>legar la llamada a sus métodos.En los Listados 16.46 a 16.48 se muestran las clases que aña<strong>de</strong>n extras a losVehículos base.1 public abstract c l a s s VehiculoConExtras extends V e h i c u l o {2 protected Vehiculo v e h i c u l o ;34 public VehiculoConExtras ( Vehiculo v e h i c u l o ) {5 super ( ) ;6 t h i s . v e h i c u l o = v e h i c u l o ;7 }8 }Listado 16.46: Abstración <strong>de</strong> un Vehículo que aña<strong>de</strong> equipamiento extra.1 public c l a s s VehiculoConAireA<strong>con</strong>dicionado extends VehiculoConExtras {2 public VehiculoConAireA<strong>con</strong>dicionado ( V e h i c u l o v e h i c u l o ) {3 super ( v e h i c u l o ) ;4 }56 @Overri<strong>de</strong>7 public S t r i n g g e t D e s c r i p c i o n ( ) {8 return v e h i c u l o . g e t D e s c r i p c i o n ( ) + " , aire a c o n d i c i o n a d o " ;9 }1011 @Overri<strong>de</strong>12 public f l o a t g e t P r e c i o ( ) {13 return v e h i c u l o . g e t P r e c i o ( ) + 3 0 0 . 6 7 f ;14 }15 }Listado 16.47: Un Vehículo que aña<strong>de</strong> el extra aire a<strong>con</strong>dicionado.1 public c l a s s VehiculoConPinturaMetalizada extends VehiculoConExtras {2 public VehiculoConPinturaMetalizada ( V e h i c u l o v e h i c u l o ) {3 super ( v e h i c u l o ) ;


252CAPÍTULO 16. PATRONES DE DISEÑO4 }56 @Overri<strong>de</strong>7 public S t r i n g g e t D e s c r i p c i o n ( ) {8 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 " ;9 }1011 @Overri<strong>de</strong>12 public f l o a t g e t P r e c i o ( ) {13 return v e h i c u l o . g e t P r e c i o ( ) + 6 0 0 . 4 5 f ;14 }15 }Listado 16.48: Un Vehículo que aña<strong>de</strong> el extra pintura metalizada.Y finalmente el Listado 16.49 muestra cómo utilizar las clases <strong>de</strong>coradoraspara un par <strong>de</strong> Vehículos.1 public c l a s s PruebaDecorator {2 private void e j e c u t a ( ) {3 Vehiculo v e h i c u l o = new Coche ( " B e r l i n a " , 20000) ;4 v e h i c u l o = new VehiculoConAireA<strong>con</strong>dicionado ( v e h i c u l o ) ;5 v e h i c u l o = new VehiculoConPinturaMetalizada ( v e h i c u l o ) ;6 System . out . p r i n t l n ( " El p r e c i o <strong>de</strong> este c o c h e es : " + v e h i c u l o . g e t P r e c i o( ) ) ;7 System . out . 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 ( ) ) ;89 v e h i c u l o = new Camion ( " T r a n s p o r t e " , 100000) ;10 v e h i c u l o = new VehiculoConAireA<strong>con</strong>dicionado ( v e h i c u l o ) ;11 System . out . p r i n t l n ( " El p r e c i o <strong>de</strong> este c a m i ó n es : " + v e h i c u l o .g e t P r e c i o ( ) ) ;12 System . out . 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 ( ) ) ;13 }1415 public s t a t i c void main ( S t r i n g [ ] a r g s ) {16 new PruebaDecorator ( ) . e j e c u t a ( ) ;17 }18 }Listado 16.49: Ejemplo <strong>de</strong> creación <strong>de</strong> un par <strong>de</strong> Vehículos <strong>con</strong> algunos extras.El resultado <strong>de</strong> la ejecución <strong>de</strong> este ejemplo es:El precio <strong>de</strong> este coche es: 20901.12Berlina, aire a<strong>con</strong>dicionado, pintura metalizadaEl precio <strong>de</strong> este camión es: 100300.67Transporte, aire a<strong>con</strong>dicionadoTanto el precio como la <strong>de</strong>scripción <strong>de</strong> cada vehículo se obtienen a partir <strong>de</strong>la clase base y las clases que van <strong>de</strong>corando a esta clase base.Ejercicios.1. Escribe una aplicación don<strong>de</strong> sea posible intercambiar <strong>de</strong> forma sencillacada uno <strong>de</strong> los algoritmos <strong>de</strong> or<strong>de</strong>nación.2. Escribe una aplicación para calcular el precio <strong>de</strong> un café. Al café se lepue<strong>de</strong> añadir una pizca <strong>de</strong> leche, leche <strong>con</strong><strong>de</strong>sada o el toque <strong>de</strong> algún licor.Evi<strong>de</strong>ntemente, el precio final <strong>de</strong>pen<strong>de</strong> <strong>de</strong>l número <strong>de</strong> antojos añadidos alcafé base.


16.10. EL PATRÓN DE DISEÑO DECORATOR 253Lecturas recomendadas.El libro <strong>de</strong> referencia para los patrones <strong>de</strong> diseño es el escrito por Thegang of four <strong>de</strong>l que existe traducción al español [8].De nuevo, los libro <strong>de</strong> la colección Head first son <strong>de</strong> una muy clara exposición,la manera <strong>de</strong> presentar los <strong>con</strong>tenidos es muy didáctica y losejemplos utilizados claros y representativos. En particular la referencia [9]sobre patrones <strong>de</strong> diseños es casi <strong>de</strong> obligada lectura.


254CAPÍTULO 16. PATRONES DE DISEÑO


Apéndice Abuild.xml1 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 2829 30 31 < f i l e s e t d i r=" ${ j u n i t . dir } " i n c l u d e s=" *. jar " />32 < 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=" org . 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 . jar " />33 < f i l e l i s t >34 3536 37 38 < f i l e s e t d i r=" ${ lib } " i n c l u d e s=" *. jar " />39 40 4142 43 44 45 46 47255


256APÉNDICE A. BUILD.XML48 49 50 51 52 5354 55 56 58 5960 63 64 66 67 68 69 7071 74 75 76 77 80 81 82 83 85 86 8788 91 92 93 96 97 98 99 100 < f i l e s e t d i r=" ${ test . c l a s s e s . dir } ">101 102 103 104 105 106107 110 111 < f i l e s e t d i r=" ${ r e p o r t s . xml . dir } ">112 113 114 116 117 < f a i l i f =" test . f a i l u r e s "118 message=" Se han p r o d u c i d o e r r o r e s en los t e s t s . " />119 120121


257122 <strong>de</strong>pends=" c o m p i l e "123 d e s c r i p t i o n=" G e n e r a la d o c u m e n t a c i o n <strong>de</strong>l p r o y e c t o . ">124 130 131 132 133 134 135 136137 140 141 142 < f i l e s e t d i r=" ${ b u i l d . c l a s s e s . dir } " />143 144 147 148 149 150151 154 157 158 Listado A.1: Fichero Ant para la <strong>con</strong>strucción <strong>de</strong>l proyecto <strong>de</strong> <strong>con</strong>versión <strong>de</strong>temperaturas


258APÉNDICE A. BUILD.XML


Apéndice BAplicación HipotecaCódigo fuente <strong>de</strong> la aplicación <strong>de</strong>l cálculo <strong>de</strong> la cuota mensual <strong>de</strong> una hipoteca1 package g u i . h i p o t e c a . mo<strong>de</strong>lo ;23 import g u i . h i p o t e c a . v i s t a . V i s t a ;45 public interface Mo<strong>de</strong>lo {6 public void s e t V i s t a ( V i s t a v i s t a ) ;7 public void s e t D a t o s ( double cantidad , int tiempo , double i n t e r e s ) ;8 public double getCuota ( ) ;9 }Listado B.1: interface Mo<strong>de</strong>lo1 package g u i . h i p o t e c a . mo<strong>de</strong>lo ;23 import g u i . h i p o t e c a . v i s t a . V i s t a ;45 public c l a s s Mo<strong>de</strong>loImpl implements Mo<strong>de</strong>lo {6 private V i s t a v i s t a ;7 private double c a n t i d a d ;8 private int tiempo ;9 private double i n t e r e s ;10 private double cuota ;1112 public Mo<strong>de</strong>loImpl ( ) {13 super ( ) ;14 }1516 @Overri<strong>de</strong>17 public void s e t V i s t a ( V i s t a v i s t a ) {18 t h i s . v i s t a = v i s t a ;19 }2021 @Overri<strong>de</strong>22 public synchronized void s e t D a t o s ( double cantidad , int tiempo , doublei n t e r e s ) {23 t h i s . c a n t i d a d = c a n t i d a d ;24 t h i s . tiempo = tiempo ;25 t h i s . i n t e r e s = i n t e r e s ;26 c a l c u l a C u o t a ( ) ;27 v i s t a . c u o t a A c t u a l i z a d a ( ) ;28 }2930 private void c a l c u l a C u o t a ( ) {31 double n = i n t e r e s / 1 2 0 0 ;32 cuota = c a n t i d a d ∗n/(1 −(1/Math . pow(1+n , 12∗ tiempo ) ) ) ;259


260APÉNDICE B. APLICACIÓN HIPOTECA33 }3435 @Overri<strong>de</strong>36 public synchronized double getCuota ( ) {37 return cuota ;38 }39 }Listado B.2: Implementación <strong>de</strong>l interface Mo<strong>de</strong>lo1 package g u i . h i p o t e c a . v i s t a ;23 import j a v a . awt . Container ;45 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 ntrolador ;6 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>lo ;78 public interface V i s t a {9 public void s e t C o n t r o l a d o r ( C o n t rolador c o n t r o l a d o r ) ;10 public void setMo<strong>de</strong>lo ( Mo<strong>de</strong>lo mo<strong>de</strong>lo ) ;11 public Container getContenedor ( ) ;12 public double getCantidad ( ) ;13 public int getTiempo ( ) ;14 public double g e t I n t e r e s ( ) ;15 public void c u o t a A c t u a l i z a d a ( ) ;16 }Listado B.3: interface Vista1 package g u i . h i p o t e c a . v i s t a ;23 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 ntrolador ;4 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>lo ;56 import j a v a . awt . Bor<strong>de</strong>rLayout ;7 import j a v a . awt . Container ;8 import j a v a . awt . event . ActionEvent ;9 import j a v a . awt . event . A c t i o n L i s t e n e r ;10 import j a v a . l a n g . r e f l e c t . 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 ;1112 import javax . swing . JButton ;13 import javax . swing . JLabel ;14 import javax . swing . JPanel ;15 import javax . swing . JTextField ;16 import javax . swing . S w i n g U t i l i t i e s ;1718 public c l a s s VistaImpl implements V i s t a {19 private Mo<strong>de</strong>lo mo<strong>de</strong>lo ;20 private Controlador c o n t r o l a d o r ;21 // Componentes g r á f i c o s22 private Container c o n t e n e d o r ;23 private JTextField j t f C a n t i d a d ;24 private JTextField jtfTiempo ;25 private JTextField j t f I n t e r e s ;26 private JLabel j l C u o t a ;2728 public VistaImpl ( ) {29 super ( ) ;30 creaGUI ( ) ;31 }3233 private void creaGUI ( ) {34 try {35 S w i n g U t i l i t i e s . invokeAndWait (new Runnable ( ) {36 @Overri<strong>de</strong>37 public void run ( ) {38 c o n t e n edor = new Container ( ) ;39 c o n t e n edor . setLayout (new Bor<strong>de</strong>rLayout ( ) ) ;40 JPanel jpDatos = new JPanel ( ) ;


26141 jpDatos . add (new JLabel ( " C a n t i d a d : " ) ) ;42 j t f C a n t i d a d = new JTextField ( 8 ) ;43 jpDatos . add ( j t f C a n t i d a d ) ;44 jpDatos . add (new JLabel ( " A~nos : " ) ) ;45 jtfTiempo = new JTextField ( 3 ) ;46 jpDatos . add ( jtfTiempo ) ;47 jpDatos . add (new JLabel ( " I n t e r e s : " ) ) ;48 j t f I n t e r e s = new JTextField ( 5 ) ;49 jpDatos . add ( j t f I n t e r e s ) ;50 JButton j b C a l c u l a = new JButton ( " C a l c u l a " ) ;51 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 Escuchador ( ) ) ;52 jpDatos . add ( j b C a l c u l a ) ;53 c o n t e nedor . add ( jpDatos , Bor<strong>de</strong>rLayout .NORTH) ;54 j l C u o t a = new JLabel ( " C u o t a m e n s u a l : " ) ;55 JPanel jpCuota = new JPanel ( ) ;56 jpCuota . add ( j l C u o t a ) ;57 c o n t e nedor . add ( jpCuota ) ;58 }59 }) ;60 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {61 e . p r i n t S t a c k T r a c e ( ) ;62 } 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 ) {63 e . p r i n t S t a c k T r a c e ( ) ;64 }6566 // ventana . pack ( ) ;67 // ventana . 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) ;68 // ventana . s e t V i s i b l e ( t r u e ) ;69 }7071 @Overri<strong>de</strong>72 public Container getContenedor ( ) {73 return c o n t e nedor ;74 }7576 @Overri<strong>de</strong>77 public void setMo<strong>de</strong>lo ( Mo<strong>de</strong>lo mo<strong>de</strong>lo ) {78 t h i s . mo<strong>de</strong>lo = mo<strong>de</strong>lo ;79 }8081 @Overri<strong>de</strong>82 public void s e t C o n t r o l a d o r ( Controlador c o n t r o l a d o r ) {83 t h i s . c o n t r o l a d o r = c o n t r o l a d o r ;84 }8586 private c l a s s Escuchador implements A c t i o n L i s t e n e r {87 @Overri<strong>de</strong>88 public void a c t i o n P e r f o r m e d ( ActionEvent e ) {89 c o n t r o l a d o r . s o l i c i t a d o C a l c u l o ( ) ;90 }91 }9293 @Overri<strong>de</strong>94 public double getCantidad ( ) {95 return Double . parseDouble ( j t f C a n t i d a d . getText ( ) ) ;96 }9798 @Overri<strong>de</strong>99 public int getTiempo ( ) {100 return I n t e g e r . p a r s e I n t ( jtfTiempo . getText ( ) ) ;101 }102103 @Overri<strong>de</strong>104 public double g e t I n t e r e s ( ) {105 return Double . parseDouble ( j t f I n t e r e s . getText ( ) ) ;106 }107108 @Overri<strong>de</strong>109 public void c u o t a A c t u a l i z a d a ( ) {110 S t r i n g cuota = S t r i n g . format ( " C u o t a m e n s u a l : %.2 f " , mo<strong>de</strong>lo . getCuota ( ) );111 j l C u o t a . s e t T ext ( cuota ) ;112 }113 }


262APÉNDICE B. APLICACIÓN HIPOTECAListado B.4: Implementación <strong>de</strong>l interface Vista1 package g u i . h i p o t e c a . c o n t r o l a d o r ;23 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>lo ;4 import g u i . h i p o t e c a . v i s t a . V i s t a ;56 public interface Controlador {7 public void setMo<strong>de</strong>lo ( Mo<strong>de</strong>lo mo<strong>de</strong>lo ) ;8 public void s e t V i s t a ( V i s t a v i s t a ) ;9 public void s o l i c i t a d o C a l c u l o ( ) ;10 }Listado B.5: interface Controlador1 package g u i . h i p o t e c a . c o n t r o l a d o r ;23 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>lo ;4 import g u i . h i p o t e c a . v i s t a . V i s t a ;56 public c l a s s ControladorImpl implements C o n trolador {7 private Mo<strong>de</strong>lo mo<strong>de</strong>lo ;8 private V i s t a v i s t a ;910 public ControladorImpl ( ) {11 super ( ) ;12 }1314 @Overri<strong>de</strong>15 public void setMo<strong>de</strong>lo ( Mo<strong>de</strong>lo mo<strong>de</strong>lo ) {16 t h i s . mo<strong>de</strong>lo = mo<strong>de</strong>lo ;17 }1819 @Overri<strong>de</strong>20 public void s e t V i s t a ( V i s t a v i s t a ) {21 t h i s . v i s t a = v i s t a ;22 }2324 public void s o l i c i t a d o C a l c u l o ( ) {25 double c a n t i d a d = v i s t a . getCantidad ( ) ;26 int tiempo = v i s t a . getTiempo ( ) ;27 double i n t e r e s = v i s t a . g e t I n t e r e s ( ) ;28 mo<strong>de</strong>lo . s e t D a t o s ( cantidad , tiempo , i n t e r e s ) ;29 }30 }Listado B.6: Implementación <strong>de</strong>l interface Controlador1 package g u i . h i p o t e c a ;23 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 ntrolador ;4 import g u i . h i p o t e c a . c o n t r o l a d o r . ControladorImpl ;5 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>lo ;6 import g u i . h i p o t e c a . mo<strong>de</strong>lo . Mo<strong>de</strong>loImpl ;7 import g u i . h i p o t e c a . v i s t a . V i s t a ;8 import g u i . h i p o t e c a . v i s t a . VistaImpl ;910 import javax . swing . JFrame ;1112 public f i n a l c l a s s Hipoteca2 {13 private Hipoteca2 ( ) {14 super ( ) ;15 }1617 private void e j e c u t a ( ) {


26318 V i s t a v i s t a = new VistaImpl ( ) ;19 Mo<strong>de</strong>lo mo<strong>de</strong>lo = new Mo<strong>de</strong>loImpl ( ) ;20 Controlador c o n t r o l a d o r = new ControladorImpl ( ) ;21 mo<strong>de</strong>lo . s e t V i s t a ( v i s t a ) ;22 v i s t a . 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 ) ;23 v i s t a . setMo<strong>de</strong>lo ( mo<strong>de</strong>lo ) ;24 c o n t r o l a d o r . setMo<strong>de</strong>lo ( mo<strong>de</strong>lo ) ;25 c o n t r o l a d o r . s e t V i s t a ( v i s t a ) ;2627 JFrame ventana = new JFrame ( " C á l c u l o <strong>de</strong> la c u o t a m e n s u a l <strong>de</strong> unah i p o t e c a " ) ;28 ventana . setContentPane ( v i s t a . getContenedor ( ) ) ;29 ventana . pack ( ) ;30 ventana . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE) ;31 ventana . s e t V i s i b l e ( true ) ;3233 }3435 public s t a t i c void main ( S t r i n g [ ] a r g s ) {36 new Hipoteca2 ( ) . e j e c u t a ( ) ;37 }38 }Listado B.7: Programa principal


264APÉNDICE B. APLICACIÓN HIPOTECA


Apéndice CEjemplo sincronizaciónEl código <strong>de</strong>l Listado C.1 muestra un ejemplo <strong>de</strong> uso <strong>de</strong> los Buffer <strong>de</strong>finidos enel Capítulo 14. Descomenta alguna <strong>de</strong> las líneas 7 u 8 para ver el resultado, que<strong>de</strong>be ser el mismo.1 package b u f f e r ;23 import j a v a . u t i l . Random ;45 public f i n a l c l a s s P r i n c i p a l {6 // Deja una <strong>de</strong> l o s B u f f e r <strong>de</strong>scomentados para ver l a e j e c u c i ó n7 private BufferSinLock c o n t e n e d o r = new BufferSinLock (10) ;8 // p r i v a t e BufferConLock c o n t e n e d o r = new BufferConLock(10) ;9 private Random a l e a t o r i o = new Random ( 0 ) ;1011 private P r i n c i p a l ( ) {12 super ( ) ;13 }1415 private void e j e c u t a ( f i n a l int tProducto , f i n a l int tConsumidor ) {16 Thread p r o d u c t o r = new Thread (new Runnable ( ) {17 @Overri<strong>de</strong>18 public void run ( ) {19 try {20 while ( true ) {21 c o n t e nedor . setDato ( a l e a t o r i o . n e x t I n t ( 1 0 0 ) ) ;22 Thread . s l e e p ( tProducto ) ;23 }24 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {25 e . p r i n t S t a c k T r a c e ( ) ;26 }27 }28 }) ;2930 Thread <strong>con</strong>sumidor = new Thread (new Runnable ( ) {31 @Overri<strong>de</strong>32 public void run ( ) {33 try {34 while ( true ) {35 c o n t e nedor . getDato ( ) ;36 Thread . s l e e p ( tConsumidor ) ;37 }38 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {39 e . p r i n t S t a c k T r a c e ( ) ;40 }41 }42 }) ;4344 p r o d u c t o r . s t a r t ( ) ;265


266APÉNDICE C. EJEMPLO SINCRONIZACIÓN45 <strong>con</strong>sumidor . s t a r t ( ) ;46 }4748 public s t a t i c void main ( S t r i n g [ ] a r g s ) {49 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 (a r g s [ 1 ] ) ) ;50 }51 }Listado C.1: Código <strong>de</strong> ejemplo que usa los Buffer <strong>de</strong>l capítulo 14


Bibliografía[1] Brian Goetz Tim Peierls Joshua Bloch Joseph Bowbeer David HolmesDoug Lea. <strong>Java</strong> Concurrency in Practice. Addison Wesley Professional,2006. [219, 220][2] Ken Arnold, James Gosling, and David Holmes. El lenguaje <strong>de</strong> programación<strong>Java</strong>. Pearson Educación, 2001. [49, 73, 92, 115, 131, 220][3] Bert Bates and Kathy Sierra. Head First <strong>Java</strong>. O’Reilly & Associates, 2edition, 2005. [49, 73, 115, 171][4] Joshua Bloch. Effective <strong>Java</strong>. Addison Wesley, 2001. [73, 220][5] Daniel Bolaños Alonso, Almu<strong>de</strong>na Sierra Alonso, and Miren IdoiaAlarcón Rodríguez. Pruebas <strong>de</strong> software y JUnit. Perarson, 2007. [103,152][6] Fran Reyes Perdomo y Gregorio Mena Carlos Blé Jurado, Juan GutiérrezPlaza. Diseño Ágil <strong>con</strong> TDD. Lulu, 1 edition, 2010. [103][7] Ben Collins-Sussman, Brian W. Fitzpatrick, and C. Michael Pilato. VersionControl with Subversion. O’Reilly Media, 2008. [86][8] John Erich Gamma Richard Helm Ralph Johnson Vlissi<strong>de</strong>s. Patrones <strong>de</strong>diseño. Pearson - Addison Wesley, 2003. [171, 232, 253][9] Eric Freeman and Elisabeth Freeman. Head first <strong>de</strong>sign patterns. O’Reilly& Associates, 2004. [232, 253][10] Robert C. Martin. Clean co<strong>de</strong>. Prentice Hall, 2009. [49, 104][11] Ricardo Peña Mari. De Eucli<strong>de</strong>s a <strong>Java</strong>. Historia <strong>de</strong> los algoritmos y <strong>de</strong>los lenguajes <strong>de</strong> programación. Nivola, 2006. [22][12] Eliotte Rusty Harold. <strong>Java</strong> Network Programming. O’Reilly, 2005. [229][13] John Ferguson Smart. <strong>Java</strong> Power Tools. O’Reilly & Associates, 2008. [86,104, 152, 203, 204]267


Índice alfabéticoAntEjecució y limpieza, 135AntCompilar un proyecto, 130Definición <strong>de</strong> un objetivo, 129Definición <strong>de</strong> un proyecto, 128Definición <strong>de</strong> una tarea, 129Ejecución <strong>de</strong> Pruebas Unitarias, 132Empaquetado <strong>de</strong> la aplicación, 135Estructurs path-like, 131Generación <strong>de</strong> la documentación,134Propieda<strong>de</strong>s, 130<strong>Java</strong> Collection Framework, véase ColeccionesAnt, 127Calendar,clase, 115Date,clase, 114GregorianCalendar,clase, 115Math,clase, 115Random,clase, 116Scanner,clase, 103StringBuffer,clase, 107StringBuil<strong>de</strong>r,clase, 107abstract, 50Acoplamiento, 215Anotación Overri<strong>de</strong>, 44Applet, 157Ciclo <strong>de</strong> vida, 158Etiquetas HTML, 159Autoboxing, 109AWT(Abstract Window Toolkit), 138Bugzilla, 172Colecciones, 109Comentarios <strong>de</strong> documentación, 33Constructor por <strong>de</strong>fecto, 47Control <strong>de</strong> Versiones, 61Detección <strong>de</strong> eventos, 141EclEmma, 89enum, 55Enumeraciones, 55Escuchadores, véase Detección <strong>de</strong> eventosExcepcionescatch, 74finally, 74try, 74<strong>de</strong>legar una, 76inidcar que se lanza una, 78lanzar una, 78Extensión <strong>de</strong> una clase, 40File,clase, 96Finalización, 32Flujos, 92a ficheros, 96<strong>de</strong> bytes, 93<strong>de</strong> caracteres, 93GenéricosBorrado <strong>de</strong> tipo, 125Comodines, 123Límite superior, 123GenericosAmpliación <strong>de</strong>l tipo, 122Gestores <strong>de</strong> aspecto, 139Clases abstractas, 50Clases e interface anidados, 58Clases genéricas, 119Clases recubridoras, 108Cobertura <strong>de</strong> las pruebas, 88Herencia, 40Herencia simple, 40Hilos, 190Cerrojos, 197Sincronización, 197268


ÍNDICE ALFABÉTICO 269implements, 53import static, 58interface, 53JUnit, 58, 79, 81After, 84AfterClass, 85Baterías <strong>de</strong> prueba, 86Before, 84BeforeClass, 85Parameters, 86RunWith, 86Test, 82Test Suites, 87Layout Managers, véase Gestores <strong>de</strong>aspectoMétodos get y set, 29Métodos genéricos, 118Mock Objects, 80MyLyn, 165Ocultación <strong>de</strong> atributos, 42package, 56Paquetes, 56Patrón <strong>de</strong> diseñoSingleton, 215Mo<strong>de</strong>lo/Vista/Controlador, 152PatronesAbstract Factory, 219Decorator, 231Factory Method, 217Observer, 229Strategy, 226Patrones <strong>de</strong> diseño, 214Programación Orientada a Objetos, 11Pruebas unitarias, 79Principios FIRST, 80Subverioncommit, 66Subversionimport, 64Integración <strong>con</strong> Eclipse, 70merge, 67Repositorio, 62svnserve, 62super, 46Swing, 138JButton, 146JCheckBox, 150JLabel, 146JList, 150JRadioButton, 148JTextField, 147Componentes, 139Contenedores, 139Test unitarios, 73Threads, véase HilosTipos <strong>de</strong> datos, 21Tipos genéricos, 117URL, 204Vinculación dinámica, 40, 46Recolector <strong>de</strong> basura, 31Serialización, 98Signatura <strong>de</strong> un método, 20Sistema <strong>de</strong> ficheros, 96Sobrescritura <strong>de</strong> atributos, 42Socket, 207TCP, 207UDP, 209Streams, véase Flujos

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!