You are on page 1of 384

Tutorial de Java

por

Agustn Froufe
(froufe@arrakis.es) Editado en PDF por Alonso de la Barra 2001 adlbarra@vtr.net

Tutorial de Java

Indice
Introduccin a Java Origen de Java Caractersticas de Java HotJava Java para aplicaciones corporativas Simple Orientado a Objetos Distribuido Robusto De Arquitectura Neutral Seguro Portable Interpretado Multithreaded Dinmico

Tabla de Contenido
15 16 17 17 18 19 19 19 21 23 23 23 24 25 25 26 26 27 27 28 29 30 31 32 32 32 33 35 35

Desarrollo rpido de aplicaciones Aplicaciones efectivas y eficientes Portabilidad para programador y programa Costes de desarrollo Mantenimiento y soporte Aprendizaje Resumen

Instalacin del JDK Windows 95 Solaris Linux Compilacin sin JDK Conceptos Bsicos de Java Programacin en Java

Tutorial de Java

Comentarios Identificadores Palabras clave Palabras reservadas

35 36 36 36 36 37 38 39 39 39 40 40 41 41 42 42 43 43 44 44 45 45 46 47 48 49 50 50

Clases

Literales Arrays Operadores Separadores Sentencias de Salto Sentencias de Bucles Excepciones Control General del Flujo Tipos de Clases Ambito de una variable Mtodos y Constructores Finalizadores

Control del Flujo

Variables y Mtodos de Instancia

Alcance de Objetos y Reciclado de Memoria Herencia Control de Acceso Variables y Mtodos Estticos this y super Clases Abstractas Interfaces Mtodos Nativos Paquetes Import Paquetes de Java Punteros Referencias Referencias Referencias Referencias 51 51

Referencias en C++ en Java y Arrays y Listas 52 53 53 55 56

52

Tutorial de Java

Punteros C/C++ y Referencias Java

57 59 59 59 60 60 61 61 61 62 62 63 63 64 64 66 67 67 68 68 69 69 69 70 70 70 71 71 72 74 75 77 78 79 79

Programas Bsicos en Java Una mnima aplicacin en Java HolaMundo Compilacin y Ejecucin de HolaMundo Ficheros fuente java Compilacin Ejecucin Problemas de compilacin Applet Llamadas a Applets con appletviewer Arquitectura de appletviewer Ciclo de vida de un Applet Mtodos de appletviewer Sinopsis Ejemplo de uso Funciones de men de appletviewer HolaMundo Componentes bsicos de un Applet Clases incluidas La clase Applet Mtodos de Applet Compilacin de un Applet Llamada a Applets Prueba de un Applet La marca APPLET de html Atributos de APPLET Paso de parmetros a Applets Tokens en parmetros de llamada El parmetro ARCHIVE Depuracin general Ciclo de vida de un Applet Proteccin de Applets Un Applet bsico en Java El visor de Applets de Sun (appletviewer)

Tutorial de Java

Escribir Applets Java init destroy start stop resize width height paint update repaint getParameter getDocumentBase getCodeBase print 80 81 81 81 81 81 82 82 82 83 83 83 84 84

80

La aplicacin Fecha (Aproximacin a OOP) El Depurador de Java - jdb Depurar HolaMundo Comando help Comando threadgroups Comando threads Comando run Comando where Comando use Comando list Comando dump Comando step Clases Java La clase Math Funciones matemticas Declaraciones Comprobaciones booleanas Traslaciones de caracteres Traslaciones de carcter/dgito Mtodos de la clase Character 93 La clase Character 94 94 95 95 95

84 87 87 88 89 89 89 90 90 90 91

91
93 93 94

Tutorial de Java

La clase Float Declaraciones Valores de Float Conversiones de Clase/Cadena Comprobaciones Conversiones de Objetos Otros Mtodos Declaraciones Valores de Double Mtodos de Double Declaraciones Valores de Integer Mtodos de Integer Declaraciones Valores de Long Mtodos de Long Declaraciones Valores de Boolean Mtodos de Boolean Funciones Bsicas Funciones de Comparacin de Strings Funciones de Comparacin de Subcadenas Funciones ValueOf Funciones de Conversin La clase StringBuffer Cambio de Tamao Modificacin del Contenido Operadores de Concatenacin 102 102 103 101 95 95 95 96 96 96

95

La clase Double 96 96 97

96

La clase Integer 97 97 97

97

La clase Long 98 98 98

98

La clase Boolean 99 99 99

98

La clase String 100

99 100 100 101 101

Uso de Conversiones AWT Introduccin al AWT Interface de Usuario

104 105 105 105

Tutorial de Java

Estructura del AWT Componentes y Contenedores Tipos de Componentes Botones Eventos Button De pulsacin (Push) De lista (Choice) De marcacin (CheckBox) De seleccin (Radio) Autocontenidos Paneles Layouts FlowLayout BorderLayout GridLayout GridBagLayout CardLayout Crear un Layout propio 142 142 143 144 147 148 Etiquetas Listas Campos de Texto Areas de Texto Canvas Barras de Desplazamiento Diseo de Componentes propios Window Frame Dialog Panel Crear un Contenedor Aadir Componentes a un Contendor Crear el Marco de la Aplicacin Inicializar Fuentes, Colores y Recursos Crear Mens y Barras de Mens Dilogos y Ventanas 114 115 117 119 120 122 124 108 Componentes 110

106 106 109 110 111 111 112 113 113

Contenedores 128 128 128 128 128 130

127

Creacin de Aplicaciones con AWT 131 132 134 136

130

138 140

Tutorial de Java

Control de Eventos La clase Event Tipos de Eventos Eventos de Ventana Eventos de Teclado Eventos de Ratn Eventos de Barras Eventos de Lista Eventos Varios Grficos Objetos Grficos Mtodos para Dibujos Lneas Rectngulos Crculos, Elipses Polgonos Funciones Grficas Fractales Lneas Flotantes 171 172 175 179 180 182 184 Generacin y Propagacin de Eventos Mtodos de Control de Eventos ACTION_EVENT Cambio de Font de Caracteres Colores de Fondo y Texto Fijar el Tamao Preferido Uso de Insets Habilitar y Deshabilitar Componentes Botn Grfico 155 158 159 151 152

150

152 152 152 153 153 153

Mejorar el Diseo de Interfaces 160 162 163 164 165 166

160

169 169 170

Mtodos para Imgenes Doble Buffering de Grficos Contextos Grficos Creacin de Contextos Grficos Utilizacin de Contextos Grficos 187 188 188

186 187

Nuevas Clases para Dibujo La Clase MediaTracker Manejo de Imgenes para Animacin 191

189 191

Tutorial de Java

Creacin de un Objeto MediaTracker Ejemplo de Animacin Reproduccin de Sonido Reproduccin Repetitiva

192 192 194 194 195 196 197 197 198 199 201 202 202 202 203 204 209 209 209 210 211 211 213 213 214 214 217 216 217 217 218 218 219 220 220

Sonido en Java

Entrada por Ratn Excepciones en Java Manejo de Excepciones Generar Excepciones en Java Excepciones Predefinidas Crear Excepciones Propias Capturar Excepciones try catch finally

Propagacin de Excepciones Threads y Multithreading Flujo en Programas Programas de Flujo Unico Programas de Flujo Mltiple Creacin de un Thread Arranque de un Thread Manipulacin de un Thread Suspensin de un Thread Parada de un Thread

Creacin y Control de Threads

Arrancar y Parar Threads Suspender y Reanudar Threads Estados de un Thread Nuevo Thread Ejecutable Parado Muerto El mtodo isAlive()

Scheduling

10

Tutorial de Java

Prioridades, demonios... Prioridades Threads Demonio Diferencia de Threads con fork() 221 221 221

221

Ejemplo de Animacin Comunicacin entre Threads Cdigo Nativo Escribir Cdigo Java Compilar el Cdigo Java Crear el fichero de Cabecera Crear el fichero de Stubs Escribir la funcin C Crear la Librera Dinmica Unix Windows 95 233 233 Productor Consumidor Monitor Monitorizacin del Productor 224 225 226 228

222 224

229 229 230 230 232 232 233

Ejecutar el Programa Entrada/Salida Estndar La clase System Stdin Stdout Stderr 235 235 236

234 235 235

Clases comunes de Entrada/Salida Ficheros en Java Ficheros Creacin de un objeto File Comprobaciones y Utilidades Objetos FileInputStream Apertura de un FileInputStream Lectura de un FileInputStream 237 238

236 237 237

Streams de Entrada 239

239 239 240

Tutorial de Java

11

Cierre de FileInputStream Ejemplo: Visualizacin de un fichero Objetos DataInputStream Apertura y cierre de DataInputStream Lectura de un DataInputStream Streams de entrada de URLs Apertura de un Stream de entrada Streams de Salida Objetos FileOutputStream Apertura de un FileOutputStream Escritura en un FileOutputStream Cierre de FileOutputStream Ejemplo: Almacenamiento de Informacin Streams de salida con buffer Creacin de Streams de salida con buffer Volcado y Cierre de Streams de salida con buffer Streams DataOutput Apertura y cierre de objetos DataOutputStream Escritura en un objeto DataOutputStream Contabilidad de la salida Ficheros de Acceso Aleatorio Creacin de un Fichero de Acceso Aleatorio Acceso a la Informacin Actualizacin de Informacin 247 247 248 246 245 243 242 241

240 240 241 241 242 243 243 243 244 244 245 245 246 246 247 247

Comunicaciones en Java Comunicaciones en Unix Sockets Sockets Stream Sockets Datagrama Sockets Raw 250 250 250

249 249 250

Diferencias entre Sockets Stream y Datagrama Uso de Sockets

251 251

12

Tutorial de Java

Puertos y Servicios La clase URL Dominio Unix Dominio Internet Apertura de Sockets Creacin de Streams de Entrada Creacin de Streams de Salida Cierre de Sockets Mnimo Cliente SMTP Servidor de Eco Mnimo Servidor TCP/IP Mnimo Cliente TCP/IP Servidor Simple de HTTP Red en Windows 95 (sin conexin) Configuracin del TCP/IP de Windows 95 Crear una entrada en la Red Comprobacin de la Red Problemas ms frecuentes

251 252 253 254 254 254 255 256 257 258 258 258 260 261 262 266 267 267 268 268 269 273 273 274 275 276 276 276 276 277 277 278 279

Dominios de Comunicaciones

Modelo de Comunicaciones con Java

Clases Utiles en Comunicaciones Arquitectura MVC en Java La Arquitectura MVC Definicin de las partes Funciones de Observer y Observable Observer Observable Cmo utilizar Observer y Observable Extender un Observable Implementar un Observador Usando Observador y Observable Ejemplo de aplicacin MVC Observador y Observable

Tutorial de Java

13

Aplicaciones en Java Etiqueta Reloj Digital Persiana Solapas Transparencia Calculadora Cuenta-Kilmetros Potencimetro Cartel (de Luis Angel Ortega) Final y Agradecimientos Agradecimientos Copyrights

281 281 290 303 310 329 341 350 354 369 383 383 383

14

Tutorial de Java

Captulo 1

Introduccin a Java

El uso principal que se hace de Internet e incluso de las redes internas (corporativas) es correo electrnico (e-mail), aunque actualmente hay un auge sorprendente de la navegacin web. Los documentos web pueden contener variedad de texto, grficos de todas clases y proporcionar enlaces hipertexto hacia cualquier lugar de la red. Los navegadores utilizan documentos escritos en lenguaje HTML. La combinacin actual de navegadores HTML/ WWW estn limitados pues, a texto y grficos. Si se quiere reproducir un sonido o ejecutar un programa de demostracin, primero hemos de bajarnos (download) el fichero en cuestin y luego utilizar un programa en nuestro ordenador capaz de entender el formato de ese fichero, o bien cargar un mdulo (plug-in) en nuestro navegador para que pueda interpretar el fichero que hemos bajado. Hasta ahora, la nica forma de realizar una pgina web con contenido interactivo, era mediante la interfaz CGI (Common Gateway Interface), que permite pasar parmetros entre formularios definidos en lenguaje HTML y programas escritos en Perl o en C. Esta interfaz resulta muy incmoda de programar y es pobre en sus posibilidades. El lenguaje Java y los navegadores con soporte Java, proporcionan una forma diferente de hacer que ese navegador sea capaz de ejecutar programas. Con Java se puede reproducir sonido directamente desde el navegador, se pueden visitar home pages con animaciones, se puede ensear al navegador a manejar nuevos formatos de ficheros, e incluso, cuando se pueda transmitir video por las lneas telefnicas, nuestro navegador estar preparado para mostrar esas imgenes. Utilizando Java, se pueden eliminar los inconvenientes de la interfaz CGI y tambin se pueden aadir aplicaciones que vayan desde experimentos cientficos interactivos de propsito educativo a juegos o aplicaciones especializadas para la televenta. Es posible implementar publicidad interactiva y peridicos personalizados. Por ejemplo, alguien podra escribir un programa Java que implementara una simulacin qumica interactiva (una cadena de adn). Utilizando un navegador con soporte Java, un usuario podra recibir fcilmente esa simulacin e interaccionar con ella, en lugar de conseguir simplemente un dibujo esttico y algo de texto. Lo recibido cobra vida. Adems, con Java podemos estar seguros de que el cdigo que hace funcionar el experimento qumico no contiene ningn trozo de cdigo malicioso que dae al sistema. El cdigo que intente actuar destructivamente o que contenga errores, no podr traspasar los muros defensivos colocados por las caractersticas de seguridad y robustez de Java. Adems, Java proporciona una nueva forma de acceder a las aplicaciones. El software viaja

Tutorial de Java

15

transparentemente a travs de la red. No hay necesidad de instalar las aplicaciones, ellas mismas vienen cuando se necesitan. Por ejemplo, la mayora de los navegadores del Web pueden procesar un reducido nmero de formatos grficos (tpicamente GIF y JPEG). Si se encuentran con otro tipo de formato, el navegador estndar no tiene capacidad para procesarlo, tendra que ser actualizado para poder aprovechar las ventajas del nuevo formato. Sin embargo, un navegador con soporte Java puede enlazar con el servidor que contiene el algoritmo que procesa ese nuevo formato y mostrar la imagen. Por lo tanto, si alguien inventa un nuevo algoritmo de compresin para imgenes, el inventor slo necesita estar seguro de que hay una copia en cdigo Java de ese algoritmo instalada en el servidor que contiene las imgenes que quiere publicar. Es decir, los navegadores con soporte Java se actualizan a s mismos sobre la marcha, cuando encuentran un nuevo tipo de fichero o algoritmo.

Origen de Java
Sun Microsystems, lder en servidores para Internet, uno de cuyos lemas desde hace mucho tiempo es the network is the computer (lo que quiere dar a entender que el verdadero ordenador es la red en su conjunto y no cada mquina individual), es quien ha desarrollado el lenguaje Java, en un intento de resolver simultneamente todos los problemas que se le plantean a los desarrolladores de software por la proliferacin de arquitecturas incompatibles, tanto entre las diferentes mquinas como entre los diversos sistemas operativos y sistemas de ventanas que funcionaban sobre una misma mquina, aadiendo la dificultad de crear aplicaciones distribuidas en una red como Internet. He podido leer ms de cinco versiones distintas sobre el origen, concepcin y desarrollo de Java, desde la que dice que este fue un proyecto que rebot durante mucho tiempo por distintos departamentos de Sun sin que nadie le prestara ninguna atencin, hasta que finalmente encontr su nicho de mercado en la aldea global que es Internet; hasta la ms difundida, que justifica a Java como lenguaje de pequeos electrodomsticos. Hace algunos aos, Sun Microsystems decidi intentar introducirse en el mercado de la electrnica de consumo y desarrollar programas para pequeos dispositivos electrnicos. Tras unos comienzos dudosos, Sun decidi crear una filial, denominada FirstPerson Inc., para dar margen de maniobra al equipo responsable del proyecto. El mercado inicialmente previsto para los programas de FirstPerson eran los equipos domsticos: microondas, tostadoras y, fundamentalmente, televisin interactiva. Este mercado, dada la falta de pericia de los usuarios para el manejo de estos dispositivos, requera unos interfaces mucho ms cmodos e intuitivos que los sistemas de ventanas que proliferaban en el momento. Otros requisitos importantes a tener en cuenta eran la fiabilidad del cdigo y la facilidad de desarrollo. James Gosling, el miembro del equipo con ms experiencia en lenguajes de programacin, decidi que las ventajas aportadas por la eficiencia de C++ no compensaban el gran coste de pruebas y depuracin. Gosling haba estado trabajando en su tiempo libre en un lenguaje de programacin que l haba llamado Oak, el cual, an partiendo de la sintaxis de C++, intentaba remediar las deficiencias que iba observando.

16

Tutorial de Java

Los lenguajes al uso, como C o C++, deben ser compilados para un chip, y si se cambia el chip, todo el software debe compilarse de nuevo. Esto encarece mucho los desarrollos y el problema es especialmente acusado en el campo de la electrnica de consumo. La aparicin de un chip ms barato y, generalmente, ms eficiente, conduce inmediatamente a los fabricantes a incluirlo en las nuevas series de sus cadenas de produccin, por pequea que sea la diferencia en precio ya que, multiplicada por la tirada masiva de los aparatos, supone un ahorro considerable. Por tanto, Gosling decidi mejorar las caractersticas de Oak y utilizarlo. El primer proyecto en que se aplic este lenguaje recibi el nombre de proyecto Green y consista en un sistema de control completo de los aparatos electrnicos y el entorno de un hogar. Para ello se construy un ordenador experimental denominado *7 (Star Seven). El sistema presentaba una interfaz basada en la representacin de la casa de forma animada y el control se llevaba a cabo mediante una pantalla sensible al tacto. En el sistema apareca Duke, la actual mascota de Java. Posteriormente se aplic a otro proyecto denominado VOD (Video On Demand) en el que se empleaba como interfaz para la televisin interactiva. Ninguno de estos proyectos se convirti nunca en un sistema comercial, pero fueron desarrollados enteramente en un Java primitivo y fueron como su bautismo de fuego. Una vez que en Sun se dieron cuenta de que a corto plazo la televisin interactiva no iba a ser un gran xito, urgieron a FirstPerson a desarrollar con rapidez nuevas estrategias que produjeran beneficios. No lo consiguieron y FirstPerson cerr en la primavera de 1994. Pese a lo que pareca ya un olvido definitivo, Bill Joy, cofundador de Sun y uno de los desarrolladores principales del Unix de Berkeley, juzg que Internet podra llegar a ser el campo de juego adecuado para disputar a Microsoft su primaca casi absoluta en el terreno del software, y vio en Oak el instrumento idneo para llevar a cabo estos planes. Tras un cambio de nombre y modificaciones de diseo, el lenguaje Java fue presentado en sociedad en agosto de 1995. Lo mejor ser hacer caso omiso de las historias que pretenden dar carta de naturaleza a la clarividencia industrial de sus protagonistas; porque la cuestin es si independientemente de su origen y entorno comercial, Java ofrece soluciones a nuestras expectativas. Porque tampoco vamos a desechar la penicilina aunque haya sido su origen fruto de la casualidad.

Caracteristicas de Java
Las caractersticas principales que nos ofrece Java respecto a cualquier otro lenguaje de programacin, son: Es SIMPLE: Java ofrece toda la funcionalidad de un lenguaje potente, pero sin las caractersticas menos usadas y ms confusas de stos. C++ es un lenguaje que adolece de falta de seguridad, pero C y C++ son lenguajes ms difundidos, por ello Java se dise para ser parecido a C++ y as facilitar un rpido y fcil aprendizaje. Java elimina muchas de las caractersticas de otros lenguajes como C++, para mantener Tutorial de Java 17

reducidas las especificaciones del lenguaje y aadir caractersticas muy tiles como el garbage collector (reciclador de memoria dinmica). No es necesario preocuparse de liberar memoria, el reciclador se encarga de ello y como es un thread de baja prioridad, cuando entra en accin, permite liberar bloques de memoria muy grandes, lo que reduce la fragmentacin de la memoria. Java reduce en un 50% los errores ms comunes de programacin con lenguajes como C y C++ al eliminar muchas de las caractersticas de stos, entre las que destacan: aritmtica de punteros no existen referencias registros (struct) definicin de tipos (typedef) macros (#define) necesidad de liberar memoria (free)

Aunque, en realidad, lo que hace es eliminar las palabras reservadas (struct, typedef), ya que las clases son algo parecido. Adems, el intrprete completo de Java que hay en este momento es muy pequeo, solamente ocupa 215 Kb de RAM. Es ORIENTADO A OBJETOS: Java implementa la tecnologa bsica de C++ con algunas mejoras y elimina algunas cosas para mantener el objetivo de la simplicidad del lenguaje. Java trabaja con sus datos como objetos y con interfaces a esos objetos. Soporta las tres caractersticas propias del paradigma de la orientacin a objetos: encapsulacin, herencia y polimorfismo. Las plantillas de objetos son llamadas, como en C++, clases y sus copias, instancias. Estas instancias, como en C++, necesitan ser construidas y destruidas en espacios de memoria. Java incorpora funcionalidades inexistentes en C++ como por ejemplo, la resolucin dinmica de mtodos. Esta caracterstica deriva del lenguaje Objective C, propietario del sistema operativo Next. En C++ se suele trabajar con libreras dinmicas (DLLs) que obligan a recompilar la aplicacin cuando se retocan las funciones que se encuentran en su interior. Este inconveniente es resuelto por Java mediante una interfaz especfica llamada RTTI (RunTime Type Identification) que define la interaccin entre objetos excluyendo variables de instancias o implementacin de mtodos. Las clases en Java tienen una representacin en el runtime que permite a los programadores interrogar por el tipo de clase y enlazar dinmicamente la clase con el resultado de la bsqueda. Es DISTRIBUIDO: Java se ha construido con extensas capacidades de interconexin TCP/IP. Existen libreras de rutinas para acceder e interactuar con protocolos como http y ftp. Esto permite a los programadores acceder a la informacin a travs de la red con tanta facilidad como a los ficheros locales. La verdad es que Java en s no es distribuido, sino que proporciona las libreras y herramientas para que los programas puedan ser distribuidos, es decir, que se corran en varias

18

Tutorial de Java

mquinas, interactuando. Es ROBUSTO: Java realiza verificaciones en busca de problemas tanto en tiempo de compilacin como en tiempo de ejecucin. La comprobacin de tipos en Java ayuda a detectar errores, lo antes posible, en el ciclo de desarrollo. Java obliga a la declaracin explcita de mtodos, reduciendo as las posibilidades de error. Maneja la memoria para eliminar las preocupaciones por parte del programador de la liberacin o corrupcin de memoria. Tambin implementa los arrays autnticos, en vez de listas enlazadas de punteros, con comprobacin de lmites, para evitar la posibilidad de sobreescribir o corromper memoria resultado de punteros que sealan a zonas equivocadas. Estas caractersticas reducen drsticamente el tiempo de desarrollo de aplicaciones en Java. Adems, para asegurar el funcionamiento de la aplicacin, realiza una verificacin de los byte-codes, que son el resultado de la compilacin de un programa Java. Es un cdigo de mquina virtual que es interpretado por el intrprete Java. No es el cdigo mquina directamente entendible por el hardware, pero ya ha pasado todas las fases del compilador: anlisis de instrucciones, orden de operadores, etc., y ya tiene generada la pila de ejecucin de rdenes. Java proporciona, pues: Comprobacin de punteros Comprobacin de lmites de arrays Excepciones Verificacin de byte-codes

Es de ARQUITECTURA NEUTRAL: Para establecer Java como parte integral de la red, el compilador Java compila su cdigo a un fichero objeto de formato independiente de la arquitectura de la mquina en que se ejecutar. Cualquier mquina que tenga el sistema de ejecucin (run-time) puede ejecutar ese cdigo objeto, sin importar en modo alguno la mquina en que ha sido generado. Actualmente existen sistemas run-time para Solaris 2.x, SunOs 4.1.x, Windows 95, Windows NT, Linux, Irix, Aix, Mac, Apple y probablemente haya grupos de desarrollo trabajando en el porting a otras plataformas.

Tutorial de Java

19

El cdigo fuente Java se compila a un cdigo de bytes de alto nivel independiente de la mquina. Este cdigo (byte-codes) est diseado para ejecutarse en una mquina hipottica que es implementada por un sistema run-time, que s es dependiente de la mquina. En una representacin en que tuvisemos que indicar todos los elementos que forman parte de la arquitectura de Java sobre una plataforma genrica, obtendramos una figura como la siguiente:

En ella podemos ver que lo verdaderamente dependiente del sistema es la Mquina Virtual

20

Tutorial de Java

Java (JVM) y las libreras fundamentales, que tambin nos permitiran acceder directamente al hardware de la mquina. Adems, habr APIs de Java que tambin entren en contacto directo con el hardware y sern dependientes de la mquina, como ejemplo de este tipo de APIs podemos citar:
Java 2D: grficos 2D y manipulacin de imgenes Java Media Framework : Elementos crticos en el tiempo: audio, video... Java Animation: Animacin de objetos en 2D Java Telephony: Integracin con telefona Java Share: Interaccin entre aplicaciones multiusuario Java 3D: Grficos 3D y su manipulacin

Es SEGURO: La seguridad en Java tiene dos facetas. En el lenguaje, caractersticas como los punteros o el casting implcito que hacen los compiladores de C y C++ se eliminan para prevenir el acceso ilegal a la memoria. Cuando se usa Java para crear un navegador, se combinan las caractersticas del lenguaje con protecciones de sentido comn aplicadas al propio navegador. El lenguaje C, por ejemplo, tiene lagunas de seguridad importantes, como son los errores de alineacin. Los programadores de C utilizan punteros en conjuncin con operaciones aritmticas. Esto le permite al programador que un puntero referencie a un lugar conocido de la memoria y pueda sumar (o restar) algn valor, para referirse a otro lugar de la memoria. Si otros programadores conocen nuestras estructuras de datos pueden extraer informacin confidencial de nuestro sistema. Con un lenguaje como C, se pueden tomar nmeros enteros aleatorios y convertirlos en punteros para luego acceder a la memoria:
printf( Escribe un valor entero: ); scanf( %u,&puntero ); printf( Cadena de memoria: %s\n,puntero );

Otra laguna de seguridad u otro tipo de ataque, es el Caballo de Troya. Se presenta un programa como una utilidad, resultando tener una funcionalidad destructiva. Por ejemplo, en UNIX se visualiza el contenido de un directorio con el comando ls. Si un programador deja un comando destructivo bajo esta referencia, se puede correr el riesgo de ejecutar cdigo malicioso, aunque el comando siga haciendo la funcionalidad que se le supone, despus de lanzar su carga destructiva. Por ejemplo, despus de que el caballo de Troya haya enviado por correo el /etc/shadow a su creador, ejecuta la funcionalidad de ls persentando el contenido del directorio. Se notar un retardo, pero nada inusual. El cdigo Java pasa muchos tests antes de ejecutarse en una mquina. El cdigo se pasa a travs de un verificador de byte-codes que comprueba el formato de los fragmentos de cdigo y aplica un probador de teoremas para detectar fragmentos de cdigo ilegal -cdigo que falsea punteros, viola derechos de acceso sobre objetos o intenta cambiar el tipo o clase de un objeto-. Si los byte-codes pasan la verificacin sin generar ningn mensaje de error, entonces sabemos que: El cdigo no produce desbordamiento de operandos en la pila El tipo de los parmetros de todos los cdigos de operacin son conocidos y correctos

Tutorial de Java

21

No ha ocurrido ninguna conversin ilegal de datos, tal como convertir enteros en punteros El acceso a los campos de un objeto se sabe que es legal: public, private, protected No hay ningn intento de violar las reglas de acceso y seguridad establecidas El Cargador de Clases tambin ayuda a Java a mantener su seguridad, separando el espacio de nombres del sistema de ficheros local, del de los recursos procedentes de la red. Esto limita cualquier aplicacin del tipo Caballo de Troya, ya que las clases se buscan primero entre las locales y luego entre las procedentes del exterior. Las clases importadas de la red se almacenan en un espacio de nombres privado, asociado con el origen. Cuando una clase del espacio de nombres privado accede a otra clase, primero se busca en las clases predefinidas (del sistema local) y luego en el espacio de nombres de la clase que hace la referencia. Esto imposibilita que una clase suplante a una predefinida. En resumen, las aplicaciones de Java resultan extremadamente seguras, ya que no acceden a zonas delicadas de memoria o de sistema, con lo cual evitan la interaccin de ciertos virus. Java no posee una semntica especfica para modificar la pila de programa, la memoria libre o utilizar objetos y mtodos de un programa sin los privilegios del kernel del sistema operativo. Adems, para evitar modificaciones por parte de los crackers de la red, implementa un mtodo ultraseguro de autentificacin por clave pblica. El Cargador de Clases puede verificar una firma digital antes de realizar una instancia de un objeto. Por tanto, ningn objeto se crea y almacena en memoria, sin que se validen los privilegios de acceso. Es decir, la seguridad se integra en el momento de compilacin, con el nivel de detalle y de privilegio que sea necesario. Dada, pues la concepcin del lenguaje y si todos los elementos se mantienen dentro del estndar marcado por Sun, no hay peligro. Java imposibilita, tambin, abrir ningn fichero de la mquina local (siempre que se realizan operaciones con archivos, stas trabajan sobre el disco duro de la mquina de donde parti el applet), no permite ejecutar ninguna aplicacin nativa de una plataforma e impide que se utilicen otros ordenadores como puente, es decir, nadie puede utilizar nuestra mquina para hacer peticiones o realizar operaciones con otra. Adems, los intrpretes que incorporan los navegadores de la Web son an ms restrictivos. Bajo estas condiciones (y dentro de la filosofa de que el nico ordenador seguro es el que est apagado, desenchufado, dentro de una cmara acorazada en un bunker y rodeado por mil soldados de los cuerpos especiales del ejrcito), se puede considerar que Java es un lenguaje seguro y que los applets estn libres de virus. Respecto a la seguridad del cdigo fuente, no ya del lenguaje, JDK proporciona un desemsamblador de byte-code, que permite que cualquier programa pueda ser convertido a cdigo fuente, lo que para el programador significa una vulnerabilidad total a su cdigo. Utilizando javap no se obtiene el cdigo fuente original, pero s desmonta el programa mostrando el algoritmo que se utiliza, que es lo realmente interesante. La proteccin de los programadores ante esto es utilizar llamadas a programas nativos, externos (incluso en C o C++) de forma que no sea descompilable todo el cdigo; aunque as se pierda portabilidad. Esta es otra de las cuestiones que Java tiene pendientes.

22

Tutorial de Java

Es PORTABLE: Ms all de la portabilidad bsica por ser de arquitectura independiente, Java implementa otros estndares de portabilidad para facilitar el desarrollo. Los enteros son siempre enteros y adems, enteros de 32 bits en complemento a 2. Adems, Java construye sus interfaces de usuario a travs de un sistema abstracto de ventanas de forma que las ventanas puedan ser implantadas en entornos Unix, Pc o Mac. Es INTERPRETADO: El intrprete Java (sistema run-time) puede ejecutar directamente el cdigo objeto. Enlazar (linkar) un programa, normalmente, consume menos recursos que compilarlo, por lo que los desarrolladores con Java pasarn ms tiempo desarrollando y menos esperando por el ordenador. No obstante, el compilador actual del JDK es bastante lento. Por ahora, que todava no hay compiladores especficos de Java para las diversas plataformas, Java es ms lento que otros lenguajes de programacin, como C++, ya que debe ser interpretado y no ejecutado como sucede en cualquier programa tradicional. Se dice que Java es de 10 a 30 veces ms lento que C, y que tampoco existen en Java proyectos de gran envergadura como en otros lenguajes. La verdad es que ya hay comparaciones ventajosas entre Java y el resto de los lenguajes de programacin, y una ingente cantidad de folletos electrnicos que supuran fanatismo en favor y en contra de los distintos lenguajes contendientes con Java. Lo que se suele dejar de lado en todo esto, es que primero habra que decidir hasta que punto Java, un lenguaje en pleno desarrollo y todava sin definicin definitiva, est maduro como lenguaje de programacin para ser comparado con otros; como por ejemplo con Smalltalk, que lleva ms de 20 aos en cancha. La verdad es que Java para conseguir ser un lenguaje independiente del sistema operativo y del procesador que incorpore la mquina utilizada, es tanto interpretado como compilado. Y esto no es ningn contrasentido, me explico, el cdigo fuente escrito con cualquier editor se compila generando el byte-code. Este cdigo intermedio es de muy bajo nivel, pero sin alcanzar las instrucciones mquina propias de cada plataforma y no tiene nada que ver con el p-code de Visual Basic. El byte-code corresponde al 80% de las instrucciones de la aplicacin. Ese mismo cdigo es el que se puede ejecutar sobre cualquier plataforma. Para ello hace falta el run-time, que s es completamente dependiente de la mquina y del sistema operativo, que interpreta dinmicamente el byte-code y aade el 20% de instrucciones que faltaban para su ejecucin. Con este sistema es fcil crear aplicaciones multiplataforma, pero para ejecutarlas es necesario que exista el run-time correspondiente al sistema operativo utilizado. Es MULTITHREADED: Al ser multithreaded (multihilvanado, en mala traduccin), Java permite muchas actividades simultneas en un programa. Los threads (a veces llamados, procesos ligeros), son bsicamente pequeos procesos o piezas independientes de un gran proceso. Al estar los threads contruidos en el lenguaje, son ms fciles de usar y ms robustos que sus homlogos en C o C++. El beneficio de ser miltithreaded consiste en un mejor rendimiento interactivo y mejor com-

Tutorial de Java

23

portamiento en tiempo real. Aunque el comportamiento en tiempo real est limitado a las capacidades del sistema operativo subyacente (Unix, Windows, etc.), an supera a los entornos de flujo nico de programa (single-threaded) tanto en facilidad de desarrollo como en rendimiento. Cualquiera que haya utilizado la tecnologa de navegacin concurrente, sabe lo frustrante que puede ser esperar por una gran imagen que se est trayendo. En Java, las imgenes se pueden ir trayendo en un thread independiente, permitiendo que el usuario pueda acceder a la informacin en la pgina sin tener que esperar por el navegador. Es DINAMICO: Java se beneficia todo lo posible de la tecnologa orientada a objetos. Java no intenta conectar todos los mdulos que comprenden una aplicacin hasta el tiempo de ejecucin. Las librera nuevas o actualizadas no paralizarn las aplicaciones actuales (siempre que mantengan el API anterior).

Java tambin simplifica el uso de protocolos nuevos o actualizados. Si su sistema ejecuta una aplicacin Java sobre la red y encuentra una pieza de la aplicacin que no sabe manejar, tal como se ha explicado en prrafos anteriores, Java es capaz de traer automticamente cualquiera de esas piezas que el sistema necesita para funcionar.

24

Tutorial de Java

Java, para evitar que los mdulos de byte-codes o los objetos o nuevas clases, haya que estar trayndolos de la red cada vez que se necesiten, implementa las opciones de persistencia, para que no se eliminen cuando de limpie la cach de la mquina.

HotJava
HotJava, en pocas palabras, es un navegador con soporte Java (Java-enabled), desarrollado en Java. Como cualquier navegador de Web, HotJava puede decodificar HTML estndar y URLs estndares, aunque no soporta completamente el estndar HTML 3.0. La ventaja sobre el resto de navegadores, sin soporte Java, es que puede ejecutar programas Java sobre la red. La diferencia con Netscape, es que tiene implementado completamente los sistemas de seguridad que propone Java, esto significa que puede escribir y leer en el disco local, aunque esto hace disminuir la seguridad, ya que se pueden grabar en nuestro disco programas que contengan cdigo malicioso e introducirnos un virus, por ejemplo. No obstante, el utilizar esta caracterstica de HotJava es decisin del usuario.

Java para aplicaciones corporativas


Java actualmente est en boca de todos, Java e Intranet son las palabras de moda. Pero, surge la pregunta de si esta es una buena tecnologa para desarrollar aplicaciones corporativas. Y la respuesta es afirmativa y voy a proponer argumentos para esa afirmacin. En donde la red sea algo crtico, Java facilita tremendamente la vida de la programacin corporativa. Durante aos, las grandes empresas se han convencido de que la red corporativa es la arteria por donde fluye la sangre que mantiene vivo su negocio. Desde el gran servidor de sus oficinas centrales, hasta los servidores de las delegaciones, las estaciones de trabajo de los programadores y la marabunta de PCs, la informacin va fluyendo de unos a otros. Para muchas compaas, la Red es la Empresa. Si esta red no se mantiene sana, los pedidos no llegan, el inventario no se actualiza, el software no se desarrolla adecuadamente, los clientes no estn satisfechos y, fundamentalmente, el dinero no entra. La necesidad de diagnosticar y reducir la arterioesclerosis de la red, hace que se estn inyectando continuamente nuevas metodologas que subsanen este grave problema. Es Java la medicina? Est claro que cuando vemos un cepillo animado limpiando los dientes, cubos movindose en 3-D, o una banda de gatos locos en applets de Java, nos convencemos de que es el lenguaje idneo para Internet. Pero, qu pasa con las aplicaciones corporativas, sera una buena tecnologa all donde la red es el punto crtico? Vamos a intentar responder comparando las capacidades de Java contra la lista de necesidades de la red corporativa.

Tutorial de Java

25

Desarrollo rpido de aplicaciones


Hace aos, se deca que los programadores pronto desapareceran. Los generadores automticos de programas, eliminaran a los generadores humanos y el mundo sera un lugar mejor para vivir. Desafortunadamente, quienes decan esto no tuvieron en cuenta una acelerada demanda de software de calidad para muy diferentes aplicaciones. Sin embargo, la tecnologa de objetos pronto vino a intentar facilitar la tarea, adoptando el modelo de generar parte de un programa, as, generando la parte bsica de un programa (los objetos), se podra conectar con otras partes para proporcionar diferentes utilidades al usuario. El lenguaje C++ es una buena herramienta, pero no cumple totalmente la premisa. Visual Basic y NextStep, se acercan cada vez ms al poder de los objetos. Java facilita la creacin de entornos de desarrollo-aplicaciones de modo similar, pero adems es flexible, poderoso y efectivo. Los programadores ahora disponen de herramientas de programacin de calidad beta, que apuntan hacia esa meta, como son el Java WorkShop de SunSoft, el entorno Java de Borland, el Caf de Symantec, y pronto, herramientas ms sofisticadas como Netcode o FutureTense. Esto proporciona una gran progresin a los entornos de desarrollo Java.

Aplicaciones efectivas y eficientes


Las aplicaciones que se crean en grandes empresas deben ser ms efectivas que eficientes; es decir, conseguir que el programa funcione y el trabajo salga adelante es ms importante que el que lo haga eficientemente. Esto no es una crtica, es una realidad de la programacin corporativa. Al ser un lenguaje ms simple que cualquiera de los que ahora estn en el cajn de los programadores, Java permite a stos concentrarse en la mecnica de la aplicacin, en vez de pasarse horas y horas incorporando APIs para el control de las ventanas, controlando minuciosamente la memoria, sincronizando los ficheros de cabecera y corrigiendo los agnicos mensajes del linker. Java tiene su propio toolkit para interfaces, maneja por s mismo la memoria que utilice la aplicacin, no permite ficheros de cabecera separados (en aplicaciones puramente Java) y solamente usa enlace dinmico. Muchas de las implementaciones de Java actuales son puros intrpretes. Los byte-codes son interpretados por el sistema run-time de Java, la Mquina Virtual Java (JVM), sobre el ordenador del usuario. Aunque ya hay ciertos proveedores que ofrecen compiladores nativos Just-In-Time (JIT). Si la Mquina Virtual Java dispone de un compilador instalado, las secciones (clases) del byte-code de la aplicacin se compilarn hacia la arquitectura nativa del ordenador del usuario. Los programas Java en ese momento rivalizarn con el rendimiento de programas en C++. Los compiladores JIT no se utilizan en la forma tradicional de un compilador; los programadores no compilan y distribuyen binarios Java a los usuarios. La compilacin JIT tiene lugar a partir del byte-code Java, en el sistema del usuario, como una parte (opcional) del entorno run-time local de Java. Muchas veces, los programadores corporativos, ansiosos por exprimir al mximo la eficiencia de su aplicacin, empiezan a hacerlo demasiado pronto en el ciclo de vida de la aplicacin. Java permite algunas tcnicas innovadoras de optimizacin. Por ejemplo, Java es inherentemente multithreaded, a la vez que ofrece posibilidades de multithread como la

26

Tutorial de Java

clase Thread y mecanismos muy sencillos de usar de sincronizacin; Java en s utiliza threads. Los desarrolladores de compiladores inteligentes pueden utilizar esta caracterstica de Java para lanzar un thread que compruebe la forma en que se est utilizando la aplicacin. Ms especficamente, este thread podra detectar qu mtodos de una clase se estn usando con ms frecuencia e invocar a sucesivos niveles de optimizacin en tiempo de ejecucin de la aplicacin. Cuanto ms tiempo est corriendo la aplicacin o el applet, los mtodos estarn cada vez ms optimizados (Guava de Softway es de este tipo). Si un compilador JIT est embebido en el entorno run-time de Java, el programador no se preocupa de hacer que la aplicacin se ejecute ptimamente. Siempre he pensado que en los Sistemas Operativos tendra que aplicarse esta filosofa; un optimizador progresivo es un paso ms hacia esta idea.

Portabilidad para programador y programa


En una empresa de relativo tamao hay una plyade diferente de ordenadores. Probablemente nos encontremos con estaciones de trabajo Sun para el desarrollo de software, hordas de PCs para cada empleado, algn Mac en el departamento de documentacin, una estacin de trabajo HP en administracin y una estacin SGI en la sala de demos. Desarrollar aplicaciones corporativas para un grupo tan diferente de plataformas en excesivamente complejo y caro. Hasta ahora era complicado convencer a los programadores de cada arquitectura que utilizasen un API comn para reducir el coste de las aplicaciones. Con un entorno run-time de Java portado a cada una de las arquitecturas de las plataformas presentes en la empresa y una buena librera de clases (packages en Java), los programadores pueden entenderse y encontrar muy interesante trabajar con Java. Esta posibilidad har tender a los programadores hacia Java, justo donde otros intentos anteriores con entornos universales (como Galaxy o XVT) han fracasado. Estos APIs eran simplemente inadecuados, no orientados a redes y, verdaderamente, pesados. Una vez que los programas estn escritos en Java, otro lado interesante del asunto es que los programadores tambin son portables. El grupo de programadores de la empresa puede ahora enfrentarse a un desarrollo para cualquiera de las plataformas. La parte del cliente y del servidor de una aplicacin estarn ahora escritas en el mismo lenguaje. Ya no ser necesario tener un grupo que desarrolle en Solaris en del departamento de I+D, programadores trabajando sobre Visual Basic en el departamento de documentacin y programadores sobre GNU en proyectos especiales; ahora todos ellos podrn estar juntos y formar el grupo de software de la empresa.

Costes de desarrollo
En contraste con el alto coste de los desarrollos realizados sobre estaciones de trabajo, el coste de creacin de una aplicacin Java es similar al de desarrollar sobre un PC. Desarrollar utilizando un software caro para una estacin de trabajo (ahora barata) es un

Tutorial de Java

27

problema en muchas empresas. La eficiencia del hardware y el poco coste de mantenimiento de una estacin de trabajo Sun, por ejemplo, resulta muy atractivo para las empresas; pero el coste adicional del entorno de desarrollo con C++ es prohibitivo para la gran mayora de ellas. La llegada de Java e Intranet reducen considerablemente estos costes. Las herramientas Java ya no estn en el entorno de precios de millones de pesetas, sino a los niveles confortables de precio de las herramientas de PCs. Y con el crecimiento cada da mayor de la comunidad de desarrolladores de software freeware y shareware que incluso proporcionan el cdigo fuente, los programadores corporativos tienen un amplio campo donde moverse y muchas oportunidades de aprender y muchos recursos a su disposicin. El xito que Internet ha proporcionado a los equipos de software corporativos es un regalo. El precio del software es ahora el mismo para un poderoso equipo corriendo Unix que para un PC. Incluso Netscape tiene al mismo precio la versin Unix de su servidor Web SuiteSpot que la versin PC/NT. Esta es la filosofa de precios que parece ser ser la que se siga con las herramientas basadas en Java.

Mantenimiento y soporte
Un problema bien conocido que ocurre con el software corporativo es la demanda de cuidados y realimentacin. Java no es, ciertamente, la cura para la enfermedad del mantenimiento, pero tiene varias caractersticas que harn la vida del enfermero ms fcil. Uno de los componentes del JDK es javadoc. Si se usan ciertas convenciones en el cdigo fuente Java (como comenzar un comentario con /** y terminarlo con */), javadoc se puede fcilmente generar pginas HTML con el contenido de esos comentarios, que pueden visualizarse en cualquier navegador. La documentacin del API de Java ha sido creada de este modo. Esto hace que el trabajo de documentar el cdigo de nuevas clases Java sea trivial. Otro gran problema del desarrollador corporativo es la creacin y control de makefiles. Leerse un makefile es como estar leyendo la historia de empresa. Normalmente se pasan de programador a programador, quitando la informacin que no es esencial, siempre que se puede. Esto hace que muchos de los makefiles de las aplicaciones contengan docenas de libreras, una mirada de ficheros de cabecera y ultra-confusos macros. Es como mirar en el estmago de la ballena de Jons. Java reduce las dependencia de complejos makefiles drsticamente. Primero, no hay ficheros de cabecera. Java necesita que todo el cdigo fuente de una clase se encuentre en un solo fichero. Java tiene la inteligencia de make en el propio lenguaje para simplificar la compilacin de byte-codes. Por ejemplo:
public class pepe { // Fichero: pepe.java Guitarra flamenca ; } public class guitarra { // Fichero: guitarra.java } % javac -verbose pepe.java [parsed pepe.java in 720ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 220ms]

28

Tutorial de Java

[checking class pepe] [parsed .\\Guitarra.java in 50ms] [wrote pepe.class] [checking class Guitarra] [wrote .\\Guitarra.class] [done in 2300ms]

El compilador Java se da cuenta de que necesita compilar el fichero guitarra.java. Ahora vamos a forzarlo a que recompile pepe.java sin cambiar guitarra.java, podremos comprobar que el compilador de byte-code Java no recompila innecesariamente el fichero guitarra.java.
% javac -verbose pepe.java [parsed pepe.java in 440ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 160ms] [checking class pepe] [loaded .\\Guitarra.java in 0ms] [wrote pepe.class] [done in 1860ms]

Ahora, si modificamos guitarra.java (aadiendo, por ejemplo, otro miembro a la clase) y compilamos pepe.java, el compilador Java se dar cuenta de que debe recompilar tanto pepe.java como guitarra.java
% javac -verbose pepe.java [parsed pepe.java in 710ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 220ms] [checking class pepe] [parsed .\\Guitarra.java in 0ms] [wrote pepe.class] [checking class Guitarra] [wrote .\\Guitarra.class] [done in 2640ms]

En el libro Just Java de Peter van der Linden hay un captulo excelente acerca del compilador de Java, si tienes oportunidad, no dejes de leerlo.

Aprendizaje
Si la empresa est llena de programadores de C++ con alguna experiencia en el manejo de librera grficas, aprendern rpidamente lo esencial de Java. Si el equipo de ingenieros no conoce C++, pero maneja cualquier otro lenguaje de programacin orientada a objetos, les llevar pocas semanas dominar la base de Java. Lo que s que no es cierto es que haya que aprender C++ antes de aprender Java. Si los ingenieros de la empresa no conocen ningn lenguaje orientado a objetos, s que tienen que aprender los fundamentos de esta tecnologa antes de nada, y luego aplicarlos a la programacin con Java. El anlisis y diseo orientado a objetos debe ser comprendido antes de intentar nada con Java. Los programadores de Java sin un fondo de conocimientos de OOA/D producirn cdigo pobre. Adems, los libros sobre Java crecen como la espuma, ya hay ms de 25 publicados, y si buscas Progamming in Java en la Red, encontrars 312 Web sites, y 30 ms dedicados a Learning Java. Y si esto, evidentemente, no es el sustituto de un instructor humano, hay ya varias empresas que ofrecen enseanza de Java, entre ellas, Sun.

Tutorial de Java

29

Resumen
En base a los argumentos que acabamos de exponer, podra una empresa utilizar Java para sus aplicaciones crticas? En este instante, sera suficiente un acercamiento a Java. Porque ms importante que la eleccin de Java o cualquier otro lenguaje de programacin es un buen diseo de la arquitectura de la aplicacin. Disear la aplicacin para que est distribuida entre servidores y clientes, y la lnea de partida debe ser el diseo modular. Algunas sugerencias para adoptar Java como tecnologa corporativa, seran: Usar Java en el desarrollo de la interface del cliente; Java es suficientemente estable para desarrollar una interface portable. Utilizar herramientas de programacin ms estables en los servidores, porque son la parte crtica. Portar o crear un servidor no-crtico en Java, de forma que tanto cliente como servidor estn escritos en Java. Utilizar Java en proyectos de envergadura tanto en el cliente como en el servidor, para valorar la efectividad de Java. Intranet est creciendo actualmente ms rpido que Internet. Las organizaciones corporativas estn adoptando la metodologa Internet para proporcionar soluciones a sus usuarios y clientes. Java tiene todas las cartas para ser una herramienta de inestimable valor en el desarrollo de aplicaciones corporativas.

30

Tutorial de Java

Captulo 2

Instalacin del JDK

En el instante en que estoy escribiendo esto, Java todava no es un producto comercial. No todas las mquinas disponen de la versin del Java Development Kit para ejecutarse en ellas. Por ello, solamente comentar la instalacin de JDK en Solaris, Windows 95 y Linux. Actualmente ya hay entornos de desarrollo integrados completos para Java, diferentes del JDK de Sun. Symantec dispone de un compilador de Java para Windows 95 y Windows NT, con las ventajas del aumento de velocidad de proceso y capacidades multimedia que esto proporciona, Symantec Caf. Borland tambin est trabajando en ello y la nueva versin de su entorno de desarrollo soporta Java. Sun ha lanzado la versin comercial de su propio entorno de desarrollo para Java, el Java Workshop, enteramente escrito en Java. Y Microsoft ha puesto en el mercado Visual J++, que sigue el estilo de todas sus herramientas de desarrollo. No obstante, trataremos solamente el JDK, que hasta el momento es lo ms conocido. El entorno bsico del JDK de Java que proporciona Sun est formado por herramientas en modo texto, que son: java, intrprete que ejecuta programas en byte-code. javac, compilador de Java que convierte el cdigo fuente en byte-code. javah, crea ficheros de cabecera para implementar mtodos para cualquier clase. javap, es un descompilador de byte-code a cdigo fuente Java. javadoc, es un generador automtico de documentos HTML a partir del cdigo fuente Java. javaprof, es un profiler para aplicaciones de un solo thread. HotJava, es un navegador Web escrito completamente en Java. El entorno habitual pues, consiste en un navegador que pueda ejecutar applets, un compilador que convierta el cdigo fuente Java a byte-code y el intrprete Java para ejecutar los programas. Estos son los componenetes bsicos para desarrollar algo en Java. No obstante se necesita un editor para escribir el cdigo fuente, y no son estrictamente necesarias otras herramientas como el debugger, un entorno visual, la documentacin o un visualizador de jerarqua de clases. Tan es as, que disponiendo del navegador Netscape 2.0 no se necesita ni tan siquiera el JDK (a peticin de varios amigos que disfrutan del uso de Linux pero no disponen de soporte ELF para poder utilizar el JDK portado por Randy Chapman, les indicar como conseguir utilizar el compilador embebido en Netscape).

Tutorial de Java

31

Windows
La versin del JDK para Windows es un archivo autoextraible. Se necesitan alrededor de 6 Mb de espacio libre en disco. Ejecutar el fichero, que desempaquetar el contenido del archivo. El directorio donde se instale no es importante, pero supondremos que se instala en el raiz del disco C:, en cuyo caso los archivos colgarn de c:\java. Es necesario aadir c:\java\bin a la variable de entorno PATH. Adems de los ficheros java, el JDK incluye dos libreras dinmicas, MSVCRT20.DLL y MFC30.DLL, que se instalarn en el directorio de Java. Si tienes ninguna copia de estos ficheros en tu ordenador (probablemente en el directorio system de Windows) copia estos ficheros en el directorio c:\java\bin. Si estos ficheros ya estn en tu ordenador, elimina las copias extra que instala el JDK.

Solaris
La versin del JDK para Solaris es un fichero tar comprimido. Se necesitan alrededor de 9 Mb de disco para descomprimir el JDK, aunque el doble de espacio sera una cifra ms cmoda. Ejecutar los siguientes comandos:
% uncompress JDK-beta-solaris2-sparc.tar.Z % tar xvf JDK-beta-solaris2-sparc-tar

Puedes descomprimir el archivo en tu directorio home, o, si tienes privilegios de supervisor, en algn sitio ms conveniente de /usr/local para que todos los usuarios tengan acceso a los ficheros. Sin embargo, los privilegios del supervisor no son necesarios para instalar y ejecutar Java. Por simplicidad, supondr que has descomprimido el JDK en /usr/local, aunque el path completo donde se haga no tiene relevancia (tambin es posible colocarlo en /opt que es donde residen todas las aplicaciones de Solaris). Si lo has colocado en un sitio diferente, simplemente sustituye /usr/local por ese directorio (si lo has descomprimido en tu home, puedes utilizar ~/java y ~/hotjava, en vez del path completo). Es necesario aadir /usr/local/java/bin a la variable de entorno PATH. Utiliza el siguiente comando (suponiendo que tengas el shell csh o tcsh):
set path=($PATH /usr/local/java/bin)

Tambin puedes aadir esta lnea al final del fichero .profile y .cshrc, y ya tienes el sistema listo para ejecutar applets. Si quieres desembarazarte de la ventana que aparece cada vez que lances el appletviewer con la licencia de Sun, crea un directorio que se llame .hotjava en el directorio java/bin y ya no volvers a verla.

Linux
Necesitas un kernel que soporte binarios ELF, por lo tanto tu Linux debe ser la versin 1.2.13 o superior, las anteriores tienen un bug que hacen que javac no funcione. Necesitas tambin Netscape, versin 2.0b4 o posterior. Sobre la versin 1.2.13 del kernel de Linux, hay que seguir los pasos que indico para conseguir que JDK funcione:

32

Tutorial de Java

Bajarse el JDK, linux.jdk-1.0-try4.static-motif.tar.gz y l i n u x . j d k - 1 . 0 try1.common.tar.gz a /usr/local, descomprimirlo y hacer tar xvf En el fichero .java_wrapper (si no existe, crearlo) cambiar las variable J_HOME y PRG, para que queden como:
J_HOME=/usr/local/java PRG=/usr/local/java/bin

Bajarse la librera libc.5.2.18.bin.tar.gz a /, descomprimirla, hacer tar xvf. Asegurarse de que /lib/libc.so.5 es un link simblico a este nuevo fichero. Si no lo es, hacer el /lib ln -s libc.so.5.2.18 libc.so.5 Bajarse ld-so.1.7.14.tar.gz a un directorio temporal, descomprimirlo y hacer tar xvf. Ejecutar instldso.sh y eliminar el directorio temporal. Aadir /usr/local/java a la variable de entorno PATH. Si se desea que est fijada para todos los usuarios, incorporar el directorio a la varible PATH que se fija en el fichero /etc/profile. Bajarse netscape-v202-export.i486-unknown-linux.tar.z a /usr/local/netscape, descomprimirlo y hacer tar xvf Crear un link en /usr/local/bin a /usr/local/netscape/netscape Esto debera ser suficiente para compilar cualquier cosa en Java/Linux. En caso de tener problemas, es el momento de recurrir a las FAQ. Siguiendo los pasos indicados ya se puede ejecutar el ejemplo del Tic-Tac-Toe que propone la hoja de instalacin que Sun ha incluido en todas sus versiones y que en Linux consistira en cambiarse al directorio de la demo:
% cd /usr/local/java/demo/TicTacToe

ejecutar el visualizador de applets sobre la pgina html:


% appletviewer example1.html

y a jugar a las tres en raya. Por cierto, que el algoritmo que usa el ordenador est falseado por lo que es posible ganarle.

Compilacin sin JDK


Parece raro, pero se puede conseguir. Lo nico necesario es el navegador Netscape 2.0. Este navegador, junto con la mquina virtual Java (JVM) y el sistema run-time, tiene un compilador Java. Si no se dispone del Java Development Kit (JDK), que no est disponible para todas las plataformas, pero s de la versin de Netscape para nuestra plataforma, aqu van los pasos a seguir para utilizar el compilador de Java embebido en Netscape. Como necesito partir de algn punto para tomarlo como referencia, voy a suponer que estamos sobre Linux y que vamos a prescindir del JDK de Randy Chapman. Lo que habra que hacer sera lo siguiente. Primero. Instalar Netscape en el ordenador. Asegurarse de entender perfectamente y leerse hasta el final el fichero README, para seguir las instrucciones especficas de la instalacin de Netscape en la plataforma y que Netscape funcione perfectamente. En nuestro caso, en que vamos a intentar compilar cdigo Java con Netscape sobre Linux, la pieza clave es

Tutorial de Java

33

la situacin del fichero moz2_0.zip, que en mi mquina est en /usr/local/netscape/java/ classes. Segundo. Extraer de una copia cualquiera del JDK (aunque sea de otra plataforma), el fichero java/lib/classes.zip y guardarlo en el mismo sitio que el fichero moz2_0.zip; esta localizacin no es necesaria, pero simplifica la estructura. Tercero. Fijar la variable de entorno CLASSPATH para que Netscape pueda encontrar sus propias clases adems de las clases del Java de Sun. Asegurarse de incluir el directorio actual, para poder compilar a la vez que se usan los ficheros .zip de Netscape y Sun. Por ejemplo:
setenv CLASSPATH .:/usr/local/netscape/java/classes/moz2_0.zip : /usr/local/netscape/java/classes/classes.zip

Cuarto. Compilar el cdigo Java (applet o aplicacin) con el comando:


netscape -java sun.tools.javac.Main [fichero].java

(sustituir el nombre del fichero con el cdigo Java en vez de [fichero]). Esto convertir el cdigo fuente Java en byte-code, generndose el archivo [fichero].class. Quinto. Comprobar si se puede ejecutar la aplicacin con el comando:
netscape -java [clase]

(sustituir el nombre de la clase de la aplicacin -la que contiene la rutina main- en vez de [clase]). Sexto. Si se ha compilado un applet Java, construir una pgina html que lo utilice para visualizarlo con el navegador en su forma normal. O tambin se puede visualizar utilizando el appletviewer, ejecutando:
netscape -java sun.applet.AppletViewer [clase]

Desgraciadamente, la sentencia anterior no parece funcionar en todos los sistemas. Hay amigos mos que no han sido capaces de visualizar applets con este mtodo. Para aprovechar el tiempo, se puede crear un script que recoja los pasos 3, 4 y 6. Si estamos utilizando el csh, el contenido del script sera:
#/bin/csh -f setenv CLASSPATH .:/usr/local/netscape/java/classes/moz2_0.zip: /usr/local/netscape/java/classes/classes.zip netscape -java sun.tools.javac.Main $1

y lo almacenaramos como javac. Se ha de hacer el script ejecutable y cambiar /bin/csh por el path completo donde est situado el csh. De forma semejante podemos definir el intrprete java y el appletviewer, sustituyendo la lnea adecuada de llamada a Netscape.

34

Tutorial de Java

Captulo 3
Programacin en Java

Conceptos bsicos de Java

Ahora que ya hemos visto a grandes rasgos lo que Java puede ofrecernos, y antes de entrar a saco en la generacin de nuestro primer cdigo Java, vamos a echar un vistazo al lenguaje Java en s. Lo bsico resultar muy familiar a los que tengan conocimientos de C/C++. Los programadores con experiencia en otros lenguajes procedurales reconocern la mayor parte de las construcciones. Esperemos que este captulo no resulte demasiado intenso, no obstante, s debe estar presente, porque ms de una vez recurriremos a l como referencia. En posteriores captulos profundizaremos sobre aspectos de la programacin en Java por los que aqu pasaremos de puntillas e iremos presentando ejemplos de cdigo de cada uno de esos aspectos de la programacin en Java.

Cuando se programa en Java, se coloca todo el cdigo en mtodos, de la misma forma que se escriben funciones en lenguajes como C.

Comentarios
En Java hay tres tipos de comentarios:
// comentarios para una sola lnea /* comentarios de una o ms lneas */ /** comentario de documentacin, de una o ms lneas */

Los dos primeros tipos de comentarios son los que todo programador conoce y se utilizan del mismo modo. Los comentarios de documentacin, colocados inmediatamente antes de una declaracin (de variable o funcin), indican que ese comentario ha de ser colocado en la documentacin que se genera automticamente cuando se utiliza la herramienta de Java, javadoc. Dichos comentarios sirven como descripcin del elemento declarado permitiendo generar una documentacin de nuestras clases escrita al mismo tiempo que se genera el cdigo. En este tipo de comentario para documentacin, se permite la introduccin de algunos tokens

Tutorial de Java

35

o palabras clave, que harn que la informacin que les sigue aparezca de forma diferente al resto en la documentacin.

Identificadores
Los identificadores nombran variables, funciones, clases y objetos; cualquier cosa que el programador necesite identificar o usar. En Java, un identificador comienza con una letra, un subrayado (_) o un smbolo de dlar ($). Los siguientes caracteres pueden ser letras o dgitos. Se distinguen las maysculas de las minsculas y no hay longitud mxima. Seran identificadores vlidos:
identificador nombre_usuario Nombre_Usuario _variable_del_sistema $transaccion

y su uso sera, por ejemplo:


int contador_principal; char _lista_de_ficheros; float $cantidad_en_Ptas;

Palabras clave Las siguientes son las palabras clave que estn definidas en Java y que no se pueden utilizar como indentificadores:
abstract boolean break byte byvalue case catch char class const continue for new switch default goto null synchronized do if package this double implements private threadsafe else import protected throw extends instanceof public transient false int return true final interface short try finally long static void float native super while

Palabras Reservadas Adems, el lenguaje se reserva unas cuantas palabras ms, pero que hasta ahora no tienen un cometido especfico. Son:
cast future operator outer generic rest inner var

Literales
Un valor constante en Java se crea utilizando una representacin literal de l. Java utiliza cinco tipos de elementos: enteros, reales en coma flotante, booleanos, caracteres y cadenas, que se pueden poner en cualquier lugar del cdigo fuente de Java. Cada uno de estos

36

Tutorial de Java

literales tiene un tipo correspondiente asociado con l. Enteros:


byte short int long Por ejemplo: 8 bits 16 bits 32 bits 64 bits 21 077 complemento a dos complemento a dos complemento a dos complemento a dos 0xDC00

Reales en coma flotante:


float double Por ejemplo: 32 bits 64 bits 3.14 2e12 IEEE 754 IEEE 754 3.1E12

Booleanos:
true false

Caracteres:
Por ejemplo: a \t \u???? [????] es un nmero unicode

Cadenas:
Por ejemplo: Esto es una cadena literal

Arrays
Se pueden declarar en Java arrays de cualquier tipo:
char s[]; int iArray[];

Incluso se pueden construir arrays de arrays:


int tabla[][] = new int[4][5];

Los lmites de los arrays se comprueban en tiempo de ejecucin para evitar desbordamientos y la corrupcin de memoria. En Java un array es realmente un objeto, porque tiene redefinido el operador []. Tiene una funcin miembro: length. Se puede utilizar este mtodo para conocer la longitud de cualquier array.
int a[][] = new int[10][3]; a.length; /* 10 */ a[0].length; /* 3 */

Para crear un array en Java hay dos mtodos bsicos. Crear un array vaco:
int lista[] = new int[50];

o se puede crear ya el array con sus valores iniciales:


String nombres[] = { Juan,Pepe,Pedro,Maria };

Esto que es equivalente a:

Tutorial de Java

37

String nombres[]; nombres = new String[4]; nombres[0] = new String( Juan ); nombres[1] = new String( Pepe ); nombres[2] = new String( Pedro ); nombres[3] = new String( Maria );

No se pueden crear arrays estticos en tiempo de compilacin:


int lista[50]; // generar un error en tiempo de compilacin

Tampoco se puede rellenar un array sin declarar el tamao con el operador new:
int lista[]; for( int i=0; i < 9; i++ ) lista[i] = i;

Es decir, todos los arrays en Java son estticos. Para convertir un array en el equivalente a un array dinmico en C/C++, se usa la clase vector, que permite operaciones de insercin, borrado, etc. en el array.

Operadores
Los operadores de Java son muy parecidos en estilo y funcionamiento a los de C. En la siguiente tabla aparecen los operadores que se utilizan en Java, por orden de precedencia:
. [] () ++ ! ~ instanceof * / % + << >> >>> < > <= >= & ^ | && || ? : = op= (*= /=

==

!=

%=

+=

-=

etc.)

Los operadores numricos se comportan como esperamos:


int + int = int

Los operadores relacionales devuelven un valor booleano. Para las cadenas, se pueden utilizar los operadores relacionales para comparaciones adems de + y += para la concatenacin:
String nombre = nombre + Apellido;

El operador = siempre hace copias de objetos, marcando los antiguos para borrarlos, y ya se encargar el garbage collector de devolver al sistema la memoria ocupada por el objeto eliminado.

38

Tutorial de Java

Separadores
Slo hay un par de secuencias con otros caracteres que pueden aparecer en el cdigo Java; son los separadores simples, que van a definir la forma y funcin del cdigo. Los separadores admitidos en Java son:

() - parntesis. Para contener listas de parmetros en la definicin y llamada a mtodos. Tambin se utiliza para definir precedencia en expresiones, contener expresiones para control de flujo y rodear las conversiones de tipo. {} - llaves. Para contener los valores de matrices inicializadas automticamente. Tambin se utiliza para definir un bloque de cdigo, para clases, mtodos y mbitos locales. [] - corchetes. Para declarar tipos matriz. Tambin se utiliza cuando se referencian valores de matriz. ; - punto y coma. Separa sentencias. , - coma. Separa identificadores consecutivos en una declaracin de variables. Tambin se utiliza para encadenar sentencias dentro de una sentencia for. . - punto. Para separar nombres de paquete de subpaquetes y clases. Tambin se utiliza para separar una variable o mtodo de una variable de referencia.

Control de Flujo
Muchas de las sentencias de control del flujo del programa se han tomado del C:

Sentencias de Salto
if/else
if( Boolean ) { sentencias; } else { sentencias; }

switch
switch( expr1 ) { case expr2: sentencias; break; case expr3: sentencias; break; default:

Tutorial de Java

39

sentencias; break; }

Sentencias de Bucle
Bucles for
for( expr1 inicio; expr2 test; expr3 incremento ) { sentencias; }

El siguiente trocito de cdigo Java que dibuja varias lneas en pantalla alternando sus colores entre rojo, azul y verde. Este fragmento sera parte de una funcin Java (mtodo):
int contador; for( contador=1; contador <= 12; contador++ ) { switch( contador % 3 ) { case 0: setColor( Color.red ); break; case 1: setColor( Color.blue ); break; case 2: setColor( Color.green ); break; } g.drawLine( 10,contador*10,80,contador*10 ); }

Tambin se soporta el operador coma (,) en los bucles for


for( a=0,b=0; a < 7; a++,b+=2 )

Bucles while
while( Boolean ) { sentencias; }

Bucles do/while
do { sentencias; }while( Boolean );

Excepciones
try-catch-throw
try { sentencias; } catch( Exception ) { sentencias; }

Java implementa excepciones para facilitar la construccin de cdigo robusto. Cuando ocu-

40

Tutorial de Java

rre un error en un programa, el cdigo que encuentra el error lanza una excepcin, que se puede capturar y recuperarse de ella. Java proporciona muchas excepciones predefinidas.

Control General del Flujo


break [etiqueta] continue [etiqueta] return expr; etiqueta: sentencia;

En caso de que nos encontremos con bucles anidados, se permite el uso de etiquetas para poder salirse de ellos, por ejemplo:
uno: for( ) { dos: for( ) { continue; continue uno; break uno; } }

// seguira en el bucle interno // seguira en el bucle principal // se saldra del bucle principal

En el cdigo de una funcin siempre hay que ser consecuentes con la declaracin que se haya hecho de ella. Por ejemplo, si se declara una funcin para que devuelva un entero, es imprescindible que se coloque un return final para salir de esa funcin, independientemente de que haya otros en medio del cdigo que tambin provoquen la salida de la funcin. En caso de no hacerlo se generar un Warning, y el cdigo Java no se puede compilar con Warnings.
int func() { if( a == 0 ) return 1; return 0; // es imprescindible porque se retorna un entero }

Clases
Las clases son lo ms simple de Java. Todo en Java forma parte de una clase, es una clase o describe como funciona una clase. El conocimiento de las clases es fundamental para poder entender los programas Java. Todas las acciones de los programas Java se colocan dentro del bloque de una clase o un objeto. Todos los mtodos se definen dentro del bloque de la clase, Java no soporta funciones o variables globales. Esto puede despistar a los programadores de C++, que pueden definir mtodos fuera del bloque de la clase, pero esta posibilidad es ms un intento de no separarse mucho y ser compatible con C, que un buen diseo orientado a objetos. As pues, el esqueleto de cualquier aplicacin Java se basa en la definicin de una clase. Todos los datos bsicos, como los enteros, se deben declarar en las clases antes de hacer

Tutorial de Java

41

uso de ellos. En C la unidad fundamental son los ficheros con cdigo fuente, en Java son las clases. De hecho son pocas las sentencias que se pueden colocar fuera del bloque de una clase. La palabra clave import (equivalente al #include) puede colocarse al principio de un fichero, fuera del bloque de la clase. Sin embargo, el compilador reemplazar esa sentencia con el contenido del fichero que se indique, que consistir, como es de suponer, en ms clases.

Tipos de Clases
Hasta ahora slo se ha utilizado la palabra clave public para calificar el nombre de las clases que hemos visto, pero hay tres modificadores ms. Los tipos de clases que podemos definir son: abstract Una clase abstract tiene al menos un mtodo abstracto. Una clase abstracta no se instancia, sino que se utiliza como clase base para la herencia. final Una clase final se declara como la clase que termina una cadena de herencia. No se puede heredar de una clase final. Por ejemplo, la clase Math es una clase final. public Las clases public son accesibles desde otras clases, bien sea directamente o por herencia. Son accesibles dentro del mismo paquete en el que se han declarado. Para acceder desde otros paquetes, primero tienen que ser importadas. synchronizable Este modificador especifica que todos los mtodos definidos en la clase son sincronizados, es decir, que no se puede acceder al mismo tiempo a ellos desde distintos threads; el sistema se encarga de colocar los flags necesarios para evitarlo. Este mecanismo hace que desde threads diferentes se puedan modificar las mismas variables sin que haya problemas de que se sobreescriban.

Variables y mtodos de instancia


Una clase en Java puede contener variables y mtodos. Las variables pueden ser tipos primitivos como int, char, etc. Los mtodos son funciones. Por ejemplo, en el siguiente trozo de cdigo podemos observarlo:
public MiClase { int i; public MiClase() { i = 10;

42

Tutorial de Java

} public void Suma_a_i( int j ) { i = i + j; } }

La clase MiClase contiene una variable (i) y dos mtodos, MiClase que es el constructor de la clase y Suma_a_i( int j ).

Ambito de una variable


Los bloques de sentencias compuestas en Java se delimitan con dos llaves. Las variables de Java slo son vlidas desde el punto donde estn declaradas hasta el final de la sentencia compuesta que la engloba. Se pueden anidar estas sentencias compuestas, y cada una puede contener su propio conjunto de declaraciones de variables locales. Sin embargo, no se puede declarar una variable con el mismo nombre que una de mbito exterior. El siguiente ejemplo intenta declarar dos variables separadas con el mismo nombre. En C y C++ son distintas, porque estn declaradas dentro de mbitos diferentes. En Java, esto es ilegal.
Class Ambito { int i = 1; // mbito exterior { // crea un nuevo mbito int i = 2; // error de compilacin } }

Mtodos y Constructores
Los mtodos son funciones que pueden ser llamadas dentro de la clase o por otras clases. El constructor es un tipo especfico de mtodo que siempre tiene el mismo nombre que la clase. Cuando se declara una clase en Java, se pueden declarar uno o ms constructores opcionales que realizan la inicializacin cuando se instancia (se crea una ocurrencia) un objeto de dicha clase. Utilizando el cdigo de ejemplo anterior, cuando se crea una nueva instancia de MiClase, se crean (instancian) todos los mtodos y variables, y se llama al constructor de la clase:
MiClase mc; mc = new MiClase();

La palabra clave new se usa para crear una instancia de la clase. Antes de ser instanciada con new no consume memoria, simplemente es una declaracin de tipo. Despus de ser instanciado un nuevo objeto mc, el valor de i en el objeto mc ser igual a 10. Se puede referenciar la variable (de instancia) i con el nombre del objeto:
mc.i++; // incrementa la instancia de i de mc

Al tener mc todas las variables y mtodos de MiClase, se puede usar la primera sintaxis para

Tutorial de Java

43

llamar al mtodo Suma_a_i() utilizando el nuevo nombre de clase mc:


mc.Suma_a_i( 10 );

y ahora la variable mc.i vale 21.

Finalizadores
Java no utiliza destructores (al contrario que C++) ya que tiene una forma de recoger automticamente todos los objetos que se salen del alcance. No obstante proporciona un mtodo que, cuando se especifique en el cdigo de la clase, el reciclador de memoria (garbage collector) llamar:
// Cierra el canal cuando este objeto es reciclado protected void finalize() { close(); }

Alcance de objetos y reciclado de memoria


Los objetos tienen un tiempo de vida y consumen recursos durante el mismo. Cuando un objeto no se va a utilizar ms, debera liberar el espacio que ocupaba en la memoria de forma que las aplicaciones no la agoten (especialmente las grandes). En Java, la recoleccin y liberacin de memoria es responsabilidad de un thread llamado automatic garbage collector (recolector automtico de basura). Este thread monitoriza el alcance de los objetos y marca los objetos que se han salido de alcance. Veamos un ejemplo:
String s; // no se ha asignado todavia s = new String( abc ); // memoria asignada s = def; // se ha asignado nueva memoria // (nuevo objeto)

Ms adelante veremos en detalle la clase String, pero una breve descripcin de lo que hace esto es; crear un objeto String y rellenarlo con los caracteres abc y crear otro (nuevo) String y colocarle los caracteres def. En esencia se crean dos objetos:
Objeto String abc Objeto String def

Al final de la tercera sentencia, el primer objeto creado de nombre s que contiene abc se ha salido de alcance. No hay forma de acceder a l. Ahora se tiene un nuevo objeto llamado s y contiene def. Es marcado y eliminado en la siguiente iteracin del thread reciclador de memoria.

44

Tutorial de Java

Herencia
La Herencia es el mecanismo por el que se crean nuevos objetos definidos en trminos de objetos ya existentes. Por ejemplo, si se tiene la clase Ave, se puede crear la subclase Pato, que es una especializacin de Ave.
class Pato extends Ave { int numero_de_patas; }

La palabra clave extends se usa para generar una subclase (especializacin) de un objeto. Una Pato es una subclase de Ave. Cualquier cosa que contenga la definicin de Ave ser copiada a la clase Pato, adems, en Pato se pueden definir sus propios mtodos y variables de instancia. Se dice que Pato deriva o hereda de Ave. Adems, se pueden sustituir los mtodos proporcionados por la clase base. Utilizando nuestro anterior ejemplo de MiClase, aqu hay un ejemplo de una clase derivada sustituyendo a la funcin Suma_a_i():
import MiClase; public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); } }

Ahora cuando se crea una instancia de MiNuevaClase, el valor de i tambin se inicializa a 10, pero la llamada al mtodo Suma_a_i() produce un resultado diferente:
MiNuevaClase mnc; mnc = new MiNuevaClase(); mnc.Suma_a_i( 10 );

En Java no se puede hacer herencia mltiple. Por ejemplo, de la clase aparato con motor y de la clase animal no se puede derivar nada, sera como obtener el objeto toro mecnico a partir de una mquina motorizada (aparato con motor) y un toro (aminal). En realidad, lo que se pretende es copiar los mtodos, es decir, pasar la funcionalidad del toro de verdad al toro mecnico, con lo cual no sera necesaria la herencia mltiple sino simplemente la comparticin de funcionalidad que se encuentra implementada en Java a travs de interfaces.

Control de acceso
Cuando se crea una nueva clase en Java, se puede especificar el nivel de acceso que se quiere para las variables de instancia y los mtodos definidos en la clase: public
public void CualquieraPuedeAcceder(){}

Cualquier clase desde cualquier lugar puede acceder a las variables y mtodos de instacia pblicos. protected

Tutorial de Java

45

protected void SoloSubClases(){}

Slo las subclases de la clase y nadie ms puede acceder a las variables y mtodos de instancia protegidos. private
private String NumeroDelCarnetDeIdentidad;

Las variables y mtodos de instancia privados slo pueden ser accedidos desde dentro de la clase. No son accesibles desde las subclases. friendly (sin declaracin especfica)
void MetodoDeMiPaquete(){}

Por defecto, si no se especifica el control de acceso, las variables y mtodos de instancia se declaran friendly (amigas), lo que significa que son accesibles por todos los objetos dentro del mismo paquete, pero no por los externos al paquete. Es lo mismo que protected. Los mtodos protegidos (protected) pueden ser vistos por las clases derivadas, como en C++, y tambin, en Java, por los paquetes (packages). Todas las clases de un paquete pueden ver los mtodos protegidos de ese paquete. Para evitarlo, se deben declarar como private protected, lo que hace que ya funcione como en C++ en donde slo se puede acceder a las variables y mtodos protegidos de las clases derivadas.

Variables y mtodos estticos


En un momento determinado se puede querer crear una clase en la que el valor de una variable de instancia sea el mismo (y de hecho sea la misma variable) para todos los objetos instanciados a partir de esa clase. Es decir, que exista una nica copia de la variable de instancia. Se usar para ello la palabra clave static.
class Documento extends Pagina { static int version = 10; }

El valor de la variable version ser el mismo para cualquier objeto instanciado de la clase Documento. Siempre que un objeto instanciado de Documento cambie la variable version, sta cambiar para todos los objetos. De la misma forma se puede declarar un mtodo como esttico, lo que evita que el mtodo pueda acceder a las variables de instancia no estticas:
class Documento extends Pagina { static int version = 10; int numero_de_capitulos; static void annade_un_capitulo() { numero_de_capitulos++; // esto no funciona } static void modifica_version( int i ) { version++; // esto si funciona }

46

Tutorial de Java

La modificacin de la variable numero_de_capitulos no funciona porque se est violando una de las reglas de acceso al intentar acceder desde un mtodo esttico a una variable no esttica. Todas las clases que se derivan, cuando se declaran estticas, comparten la misma pgina de variables; es decir, todos los objetos que se generen comparten la misma zona de memoria. Las funciones estticas se usan para acceder solamente a variables estticas.
class UnaClase { int var; UnaClase() { var = 5; } UnaFuncion() { var += 5; } }

En el cdigo anterior, si se llama a la funcin UnaFuncion a travs de un puntero a funcin, no se podra acceder a var, porque al utilizar un puntero a funcin no se pasa implcitamente el puntero al propio objeto (this). Sin embargo, s se podra acceder a var si fuese esttica, porque siempre estara en la misma posicin de memoria para todos los objetos que se creasen de UnaClase.

this Y super
Al acceder a variables de instancia de una clase, la palabra clave this hace referencia a los miembros de la propia clase. Volviendo al ejemplo de MiClase, se puede aadir otro constructor de la forma siguiente:
public class MiClase { int i; public MiClase() { i = 10; } // Este constructor establece el valor de i public MiClase( int valor ) { this.i = valor; // i = valor } public void Suma_a_i( int j ) { i = i + j; } }

Aqu this.i se refiere al entero i en la clase MiClase. Si se necesita llamar al mtodo padre dentro de una clase que ha reemplazado ese mtodo, se puede hacer referencia al mtodo padre con la palabra clave super:
import MiClase;

Tutorial de Java

47

public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); super.Suma_a_i( j ); } }

En el siguiente cdigo, el constructor establecer el valor de i a 10, despus lo cambiar a 15 y finalmente el mtodo Suma_a_i() de la clase padre (MiClase) lo dejar en 25:
MiNuevaClase mnc; mnc = new MiNuevaClase();

Clases abstractas
Una de las caractersticas ms tiles de cualquier lenguaje orientado a objetos es la posibilidad de declarar clases que definen como se utiliza solamente, sin tener que implementar mtodos. Esto es muy til cuando la implementacin es especfica para cada usuario, pero todos los usuarios tienen que utilizar los mismos mtodos. Un ejemplo de clase abstracta en Java es la clase Graphics:
public abstract class Graphics { public abstract void drawLine( int x1,int y1,int x2, int y2 ); public abstract void drawOval( int x,int y,int width, int height ); public abstract void drawArc( int x,int y,int width, int height,int startAngle,int arcAngle ); ... }

Los mtodos se declaran en la clase Graphics, pero el cdigo que ejecutar el mtodo est en algn otro sitio:
public class MiClase extends Graphics { public void drawLine( int x1,int y1,int x2,int y2 ) { <cdigo para pintar lneas -especfico de la arquitectura-> } }

Cuando una clase contiene un mtodo abstracto tiene que declararse abstracta. No obstante, no todos los mtodos de una clase abstracta tienen que ser abstractos. Las clases abstractas no pueden tener mtodos privados (no se podran implementar) ni tampoco estticos. Una clase abstracta tiene que derivarse obligatoriamente, no se puede hacer un new de una clase abstracta. Una clase abstracta en Java es lo mismo que en C++ virtual func() = 0; lo que obliga a que al derivar de la clase haya que implementar forzosamente los mtodos de esa clase abstracta.

48

Tutorial de Java

Interfaces
Los mtodos abstractos son tiles cuando se quiere que cada implementacin de la clase parezca y funcione igual, pero necesita que se cree una nueva clase para utilizar los mtodos abstractos. Los interfaces proporcionan un mecanismo para abstraer los mtodos a un nivel superior. Un interface contiene una coleccin de mtodos que se implementan en otro lugar. Los mtodos de una clase son public, static y final. La principal diferencia entre interface y abstract es que un interface proporciona un mecanismo de encapsulacin de los protocolos de los mtodos sin forzar al usuario a utilizar la herencia. Por ejemplo:
public interface VideoClip { // comienza la reproduccion del video void play(); // reproduce el clip en un bucle void bucle(); // detiene la reproduccion void stop(); }

Las clases que quieran utilizar el interface VideoClip utilizarn la palabra implements y proporcionarn el cdigo necesario para implementar los mtodos que se han definido para el interface:
class MiClase implements VideoClip { void play() { <cdigo> } void bucle() { <cdigo> } void stop() { <cdigo> }

Al utilizar implements para el interface es como si se hiciese una accin de copiar-y-pegar del cdigo del interface, con lo cual no se hereda nada, solamente se pueden usar los mtodos. La ventaja principal del uso de interfaces es que una clase interface puede ser implementada por cualquier nmero de clases, permitiendo a cada clase compartir el interfaz de programacin sin tener que ser consciente de la implementacin que hagan las otras clases que implementen el interface.
class MiOtraClase implements VideoClip { void play() { <cdigo nuevo> } void bucle() { <cdigo nuevo>

Tutorial de Java

49

} void stop() { <cdigo nuevo> }

Mtodos nativos
Java proporciona un mecanismo para la llamada a funciones C y C++ desde nuestro cdigo fuente Java. Para definir mtodos como funciones C o C++ se utiliza la palabra clave native.
public class Fecha { int ahora; public Fecha() { ahora = time(); } private native int time(); static { System.loadLibrary( time ); } }

Una vez escrito el cdigo Java, se necesitan ejecutar los pasos siguientes para poder integrar el cdigo C o C++: Utilizar javah para crear un fichero de cabecera (.h) Utilizar javah para crear un fichero de stubs, es decir, que contiene la declaracin de las funciones Escribir el cdigo del mtodo nativo en C o C++, es decir, rellenar el cdigo de la funcin, completando el trabajo de javah al crear el fichero de stubs Compilar el fichero de stubs y el fichero .c en una librera de carga dinmica (DLL en Windows 95 o libXX.so en Unix) Ejecutar la aplicacin con el appletviewer Ms adelante trataremos en profundidad los mtodos nativos, porque aaden una gran potencia a Java, al permitirle integrar a travs de librera dinmica cualquier algoritmo desarrollado en C o C++, lo cual, entre otras cosas, se utiliza como mtodo de proteccin contra la descompilacin completa del cdigo Java.

Paquetes
La palabra clave package permite agrupar clases e interfaces. Los nombres de los paquetes son palabras separadas por puntos y se almacenan en directorios que coinciden con esos nombres. Por ejemplo, los ficheros siguientes, que contienen cdigo fuente Java:
Applet.java, AppletContext.java, AppletStub.java, AudioClip.java

contienen en su cdigo la lnea:


package java.applet;

50

Tutorial de Java

Y las clases que se obtienen de la compilacin de los ficheros anteriores, se encuentran con el nombre nombre_de_clase.class, en el directorio:
java/applet

Import
Los paquetes de clases se cargan con la palabra clave import, especificando el nombre del paquete como ruta y nombre de clase (es lo mismo que #include de C/C++). Se pueden cargar varias clases utilizando un asterisco.
import java.Date; import java.awt.*;

Si un fichero fuente Java no contiene ningn package, se coloca en el paquete por defecto sin nombre. Es decir, en el mismo directorio que el fichero fuente, y la clase puede ser cargada con la sentencia import:
import MiClase;

Paquetes de Java
El lenguaje Java proporciona una serie de paquetes que incluyen ventanas, utilidades, un sistema de entrada/salida general, herramientas y comunicaciones. En la versin actual del JDK, los paquetes Java que se incluyen son: java.applet Este paquete contiene clases diseadas para usar con applets. Hay una clase Applet y tres interfaces: AppletContext, AppletStub y AudioClip. java.awt El paquete Abstract Windowing Toolkit (awt) contiene clases para generar widgets y componentes GUI (Interfaz Grfico de Usuario). Incluye las clases Button, Checkbox, Choice, Component, Graphics, Menu, Panel, TextArea y TextField. java.io El paquete de entrada/salida contiene las clases de acceso a ficheros: FileInputStream y FileOutputStream. java.lang Este paquete incluye las clases del lenguaje Java propiamente dicho: Object, Thread, Exception, System, Integer, Float, Math, String, etc. java.net Este paquete da soporte a las conexiones del protocolo TCP/IP y, adems, incluye las clases Socket, URL y URLConnection.

Tutorial de Java

51

java.util Este paquete es una miscelnea de clases tiles para muchas cosas en programacin. Se incluyen, entre otras, Date (fecha), Dictionary (diccionario), Random (nmeros aleatorios) y Stack (pila FIFO).

Referencias
Java se asemeja mucho a C y C++. Esta similitud, evidentemente intencionada, es la mejor herramienta para los programadores, ya que facilita en gran manera su transicin a Java. Desafortunadamente, tantas similitudes hacen que no nos paremos en algunas diferencias que son vitales. La terminologa utilizada en estos lenguajes, a veces es la misma, pero hay grandes diferencias subyacentes en su significado. C tiene tipos de datos bsicos y punteros. C++ modifica un poco este panorama y le aade los tipos referencia. Java tambin especifica sus tipos primitivos, elimina cualquier tipo de punteros y tiene tipos referencia mucho ms claros. Todo este maremgnum de terminologa provoca cierta consternacin, as que vamos a intentar aclarar lo que realmente significa. Conocemos ya ampliamente todos los tipos bsicos de datos: datos base, integrados, primitivos e internos; que son muy semejantes en C, C++ y Java; aunque Java simplifica un poco su uso a los desarrolladores haciendo que el chequeo de tipos sea bastante ms rgido. Adems, Java aade los tipos boolean y hace imprescindible el uso de este tipo booleano en sentencias condicionales.

Punteros y referencias C++


Punteros
C y C++ permiten la declaracin y uso de punteros, que pueden ser utilizados en cualquier lugar. Esta tremenda flexibilidad resulta muy til, pero tambin es la causa de que podamos colgar todo el sistema. La intencin principal en el uso de los punteros es comunicarnos ms directamente con el hardware, haciendo que el cdigo se acelere. Desafortunadamente, este modelo de tan bajo nivel hace que perdamos robustez y seguridad en la programacin y hace muy difciles tareas como la liberacin automtica de memoria, la defragmentacin de memoria, o realizar programacin distribuida de forma clara y eficiente.

52

Tutorial de Java

Referencias en C++
Las referencias se incorporaron a C++ en un intento de manejar punteros de C de forma ms limpia y segura. Sin embargo, como no elimina los punteros, la verdad es que su propsito lo consigue a medias. Es ms, podramos decir que con las referencias C++, el lenguaje se vuelve ms complicado y no es ms poderoso que antes. Las referencias deben ser inicializadas cuando se declaran y no se pueden alterar posteriormente. Esto permite incrementar la eficiencia en tiempo de ejecucin sobre la solucin basada en punteros, pero es ms por las deficiencias de los punteros que por las ventajas de las referencias.

Referencias en Java
Las referencias en Java no son punteros ni referencias como en C++. Este hecho crea un poco de confusin entre los programadores que llegan por primera vez a Java. Las referencias en Java son identificadores de instancias de las clases Java. Una referencia dirige la atencin a un objeto de un tipo especfico. No tenemos por qu saber cmo lo hace ni necesitamos saber qu hace ni, por supuesto, su implementacin. Pensemos en una referencia como si se tratase de la llave electrnica de la habitacin de un hotel. Vamos a utilizar precisamente este ejemplo del Hotel para demostrar el uso y la utilizacin que podemos hacer de las referencias en Java. Primero crearemos la clase Habitacion, implementada en el fichero Habitacion.java, mediante instancias de la cual construiremos nuestro Hotel:
public class Habitacion { private int numHabitacion; private int numCamas; public Habitacion() { habitacion( 0 ); } public Habitacion( int numeroHab ) { habitacion( numeroHab ); } public Habitacion( int numeroHab,int camas ) { habitacion( numeroHab ); camas( camas ); } public synchornized int habitacion() { return( numHabitacion ); } public synchronized void habitacion( int numeroHab ) { numHabitacion = numeroHab; } public synchronized int camas() { return( camas );

Tutorial de Java

53

} public syncronized void camas( int camas ) { numCamas = camas; } }

El cdigo anterior sera el corazn de la aplicacin. Vamos pues a construir nuestro Hotel creando Habitaciones y asignndole a cada una de ellas su llave electrnica; tal como muestra el cdigo siguiente, Hotel1.java:
public class Hotel1 { public static void main( String args[] ) { Habitacion llaveHab1; // paso 1 Habitacion llaveHab2; llaveHab1 = new Habitacion( 222 ); llaveHab2 = new Habitacion( 1144,3 ); ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^ A ByD C } } // pasos 2, 3, 4 y 5

// //

Para explicar el proceso, dividimos las acciones en los cinco pasos necesarios para poder entrar en nuestra habitacin. Aunque no se incluye, podemos tambin considerar el caso de que necesitemos un cerrajero, para que cuando perdamos la llave, nos abra la puerta; y que en nuestro caso sera el garbage collector, que recicla la habitacin una vez que se hayan perdido todas las llaves. El primer paso es la creacin de la llave, es decir, definir la variable referencia, por defecto nula. El resto de los pasos se agrupan en una sola sentencia Java. La parte B en el cdigo anterior indica al gerente del Hotel que ya dispone de una nueva habitacin. La parte C llama al decorador de interiores para que vista la habitacin segn un patrn determinado, para que no desentonen unas habitaciones con otras y no se pierdan las seas de identidad del hotel. El cdigo electrnico que nos permitir acceder a la habitacin se genera en la parte D, una vez conocido el interior de la habitacin y se programa en la llave en la parte A. Si dejamos el ejemplo real a un lado y nos vamos a lo que ocurre en la ejecucin del cdigo, vemos que el operador new busca espacio para una instancia de un objeto de una clase determinada e inicializa la memoria a los valores adecuados. Luego invoca al mtodo constructor de la clase, proporcionndole los argumentos adecuados. El operador new devuelve una referencia a s mismo, que es inmediatamente asignada a la variable referencia. Podemos tener mltiples llaves para una misma habitacin:
... Habitacion llaveHab3,llaveHab4; llaveHab3 = llaveHab1; llaveHab4 = llavehab2;

De este modo conseguimos copias de las llaves. Las habitaciones en s mismas no se

54

Tutorial de Java

han tocado en este proceso. As que, ya tenemos dos llaves para la habitacin 222 y otras dos para la habitacin 1144. Una llave puede ser programada para que funcione solamente con una habitacin en cualquier momento, pero podemos cambiar su cdigo electrnico para que funcione con alguna otra habitacin; por ejemplo, para cambiar una habitacin anteriormente utilizada por un empedernido fumador por otra limpia de olores y con vistas al mar. Cambiemos pues la llave duplicada de la habitacin del fumador (la 222) por la habitacin con olor a sal marina, 1144:
... llaveHab3 = llaveHab2;

Ahora tenemos una llave para la habitacin 222 y tres para la habitacin 1144. Mantendremos una llave para cada habitacin en la conserjera, para poder utilizarla como llave maestra, en el caso de que alguien pierda su llave propia. Alguien con la llave de una habitacin puede hacer cambios en ella, y los compaeros que tengan llave de esa misma habitacin, no tendrn conocimiento de esos cambios hasta que vuelvan a entrar en la habitacin. Por ejemplo, vamos a quitar una de las camas de la habitacin, entrando en ella con la llave maestra:
... llaveHab2.camas( 2 );

Ahora cuando los inquilinos entren en la habitacin podrn comprobar el cambio realizado:
... llaveHab4.printData();

Referencias y arrays
Como en C y C++, Java dispone de arrays de tipos primitivos o de clases. Los arrays en C y C++ son bsicamente un acompaante para los punteros. En Java, sin embargo, son ciudadanos de primera clase. Vamos a expandir nuestro hotel creando todo un ala de habitaciones, Hotel2.java. Crearemos un juego de llaves maestras y luego construiremos las habitaciones:
public class Hotel2 { // Nmero de habitaciones por ala public static final int habPorAla = 12; public static void main( String args[] ) { Habitacion llavesMaestras[]; // paso 1 llavesMaestras = new Habitacion[ habPorAla ]; // pasos 2-5 // ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ // A B, C, D y E int numPiso = 1; for( int i=0; i < habPorAla; i++ ) // pasos 6-9 llavesMaestras[ i ] = new Habitacion( numPiso * 100 + i, ( 0 == (i%2)) ? 2 : 1 ); for( int i=0; i < habPorAla; i++ ) // pasos 10-11 llavesMaestras[i].printData();

Tutorial de Java

55

} }

Cada paso en el ejemplo es semejante al que ya vimos antes. El paso 1 especifica que el juego de llaves maestras es un grupo de llaves de habitaciones. Los pasos 2 a 5 son, en este caso, la parte principal. En lugar de crear una habitacin, el gerente ordena construir un grupo contiguo de habitaciones. El nmero de llaves se especifica entre corchetes y todas se crean en blanco. Los pasos 6 a 9 son idnticos a los pasos 2 a 5 del ejemplo anterior, excepto en que en este caso todas las llaves pasan a formar parte del juego maestro. Los nmeros de piso se dan en miles para que cuando se creen las habitaciones, todas tengan el mismo formato. Tambin todas las habitaciones de nmero par tienen una sola cama, mientras que las habitaciones impares tendrn dos camas. Los pasos 10 y 11 nos permiten obtener informacin de cada una de las habitaciones.

Referencias y listas
Hay gente que piensa que como Java no dispone de punteros, resulta demasiado complejo construir listas enlazadas, rboles binarios y grafos. Vamos a demostrar que quien as piense est bastante equivocado. Retomemos el ejemplo de los arrays, y en vez de stos vamos a usar una lista doblemente enlazada. El paquete de la lista simple se compone de dos clases. Cada elemento de la lista es un NodoListaEnlazada, NodoListaEnlazada.java:
public class NodoListaEnlazada { private NodoListaEnlazada siguiente; private NodoListaEnlazada anterior; private Object datos; // . . . }

Cada NodoListaEnlazada contiene una referencia a su nodo precedente en la lista y una referencia al nodo que le sigue. Tambin contiene una referencia genrica a cualquier clase que se use para proporcionar acceso a los datos que el usuario proporcione. La lista enlazada, ListaEnlazada.java, contiene un nodo principio-fin y un contador para el nmero de nodos en la lista:
public class ListaEnlazada { public NodoListaEnlazada PrincipioFin; private int numNodos; // . . . }

El nodo especial PrincipioFin es sencillo, para simplificar el cdigo. El contador se usa para optimizar los casos ms habituales. Revisemos pues el cdigo de nuestro Hotel, ahora Hotel3.java, que ser prcticamente el

56

Tutorial de Java

mismo que en el caso de los arrays:


public class Hotel3 { // Nmero de habitaciones por ala public static final int habPorAla = 12; public static void main( String args[] ) { ListaEnlazada llaveMaestra; llaveMaestra = new ListaEnlazada();

// paso 1 // pasos 2-5

int numPiso = 1; for( int i=0; i < habPorAla; i++ ) // pasos 6-9 llaveMaestra.insertAt( i, new Habitacion( numPiso * 100 + i, ( 0 == (i%2)) ? 2 : 1 ); for( int i=0; i < habPorAla; i++ ) // pasos 10-12 ( (Habitacion)llaveMaestra.getAt(i) ).printData(); } }

El paso 1 es la llave maestra de la lista. Est representada por una lista genrica; es decir, una lista de llaves que cumple la convencin que nosotros hemos establecido. Podramos acelerar el tiempo de compilacin metiendo la lista genrica ListaEnlazada dentro de una ListaEnlazadaHabitacion. Los pasos 2 a 5 son equivalentes a los del primer ejemplo. Construimos e inicializamos una nueva ListaEnlazada, que usaremos como juego de llaves maestras. Los pasos 6 a 9 son funcionalmente idnticos a los del ejemplo anterior con arrays, pero con diferente sintaxis. En Java, los arrays y el operador [] son internos del lenguaje. Como Java no soporta la sobrecarga de operadores por parte del usuario, tenemos que usarlo siempre en su forma normal. La ListaEnlazada proporciona el mtodo insertAt() que coge el ndice en la lista, donde el nuevo nodo ha de ser insertado, como primer argumento. El segundo argumento es el objeto que ser almacenado en la lista. Obsrvese que no es necesario colocar moldeo alguno para hacer algo a una clase descendiente que depende de uno de sus padres. Los pasos 10 a 12 provocan la misma salida que los pasos 10 y 11 del ejemplo con arrays. El paso 10 coge la llave del juego que se indica en el mtodo getAt(). En este momento, el sistema no sabe qu datos contiene la llave, porque el contenido de la habitacin es genrico. Pero nosotros s sabemos lo que hay en la lista, as que informamos al sistema haciendo un moldeado a la llave de la habitacin (este casting generar un chequeo en tiempo de ejecucin por el compilador, para asegurarse de que se trata de una Habitacion). El paso 12 usa la llave para imprimir la informacin.

Punteros C/C++ y referencias Java


Ahora que ya sabemos un poco ms sobre las referencias en Java, vamos a compararlas con los punteros de C y C++.

Tutorial de Java

57

Los punteros en C y C++ estn orientados hacia un modelo fsico de funcionamiento. Es decir, que el modelo de punteros se mapea directamente sobre el modelo hardware. Este modelo asume cosas como el no movimiento, lo que hace que mecanismos como la liberacin automtica resulten mucho menos eficientes o simplemente imposibles. Cosas como la distribucin en redes y la persistencia de objetos son muy difciles de conseguir en C y C++. Aunque no hay implementaciones en Java, por ahora, para la persistencia y la distribucin, la caracterstica opaca de las referencias en Java hace que el soporte para estas prestaciones sea mucho ms simple. C y C++ permiten el uso de punteros de tal forma que podemos corromper el sistema, cosa que no puede suceder con las referencias en Java. Cualquier intento de hacer esto sera abortado por el compilador o por el sistema en ejecucin (lanzando una excepcin). C y C++ dejan la proteccin de memoria al sistema operativo, que solamente tiene el recurso de generar un error del sistema cuando un puntero accede a una posicin no vlida. Por el contrario, con el uso de las referencias, Java nos protege contra nuestras propias tendencias autodestructivas.

58

Tutorial de Java

Captulo 4
Una mnima aplicacin en Java
// // Aplicacin HolaMundo de ejemplo // class HolaMundoApp { public static void main( String args[] ) { System.out.println( Hola Mundo! ) ; } }

Programas Bsicos en Java

Como cualquier otro lenguaje, Java se usa para crear aplicaciones. Pero, tambin Java tiene la particularidad especial de poder crear aplicaciones muy especiales, son los applets, que es una mini (let) aplicacin (app) diseada para ejecutarse en un navegador. Vamos a ver en detalle lo mnimo que podemos hacer en ambos casos.

La aplicacin ms pequea posible es la que simplemente imprimir un mensaje en la pantalla. Tradicionalmente, el mensaje suele ser Hola Mundo!. Esto es justamente lo que hace el siguiente fragmento de cdigo:

HolaMundo
Vamos ver en detalle la aplicacin anterior, lnea a lnea. Esas lneas de cdigo contienen los componenetes mnimos para imprimir Hola Mundo! en la pantalla.
// // Aplicacin HolaMundo de ejemplo //

Estas tres primera lneas son comentarios. Hay tres tipos de comentarios en Java, // es un comentario orientado a lnea.
class HolaMundoApp {

Esta lnea declara la clase HolaMundoApp. El nombre de la clase especificado en el fichero fuente se utiliza para crear un fichero nombredeclase.class en el directorio en el que se compila la aplicacin. En nuestro caso, el compilador crear un fichero llamado HolaMundoApp.class.
public static void main( String args[] ) {

Esta lnea especifica un mtodo que el intrprete Java busca para ejecutar en primer lugar. Igual que en otros lenguajes, Java utiliza una palabra clave main para especificar la primera

Tutorial de Java

59

funcin a ejecutar. En este ejemplo tan simple no se pasan argumentos.

public significa que el mtodo main puede ser llamado por cualquiera, incluyendo el intrprete Java. static es una palabra clave que le dice al compilador que main se refiere a la propia clase HolaMundoApp y no a ninguna instancia de la clase. De esta forma, si alguien intenta hacer otra instancia de la clase, el mtodo main no se instanciara. void indica que main no devuelve nada. Esto es importante ya que Java realiza una estricta comprobacin de tipos, incluyendo los tipos que se ha declarado que devuelven los mtodos. args[] es la declaracin de un array de Strings. Estos son los argumentos escritos tras el nombre de la clase en la lnea de comandos:
%java HolaMundoApp arg1 arg2 ... System.out.println( Hola Mundo! );

Esta es la funcionalidad de la aplicacin. Esta lnea muestra el uso de un nombre de clase y mtodo. Se usa el mtodo println() de la clase out que est en el paquete System. El mtodo println() toma una cadena como argumento y la escribe en el stream de salida estndar; en este caso, la ventana donde se lanza la aplicacin.
} }

Finalmente, se cierran las llaves que limitan el mtodo main() y la clase HolaMundoApp.

Compilacin y ejecucin de HolaMundo


Vamos a ver a continuacin como podemos ver el resultado de nuestra primera aplicacin Java en pantalla. Generaremos un fichero con el cdigo fuente de la aplicacin, lo compilaremos y utilizaremos el intrprete java para ejecutarlo.

Ficheros Fuente Java


Los ficheros fuente en Java terminan con la extensin .java. Crear un fichero utilizando cualquier editor de texto ascii que tenga como contenido el cdigo de las ocho lneas de nuestra mnima aplicacin, y salvarlo en un fichero con el nombre de HolaMundoApp.java. Para crear los ficheros con cdigo fuente Java no es necesario un procesador de textos, aunque puede utilizarse siempre que tenga salida a fichero de texto plano o ascii, sino que es suficiente con cualquier otro editor.

60

Tutorial de Java

Compilacin
El compilador javac se encuentra en el directorio bin por debajo del directorio java, donde se haya instalado el JDK. Este directorio bin, si se han seguido las instrucciones de instalacin, debera formar parte de la variable de entorno PATH del sistema. Si no es as, tendra que revisar la Instalacin del JDK. El compilador de Java traslada el cdigo fuente Java a bytecodes, que son los componentes que entiende la Mquina Virtual Java que est incluida en los navegadores con soporte Java y en appletviewer. Una vez creado el fichero fuente HolaMundoApp.java, se puede compilar con la lnea siguiente:
%javac HolaMundoApp.java

Si no se han cometido errores al teclear ni se han tenido problemas con el path al fichero fuente ni al compilador, no debera aparecer mensaje alguno en la pantalla, y cuando vuelva a aparecer el prompt del sistema, se debera ver un fichero HolaMundoApp.class nuevo en el directorio donde se encuentra el fichero fuente. Si ha habido algn problema, en Problemas de compilacin al final de esta seccin, hemos intentado reproducir los que ms frecuentemente se suelen dar, se pueden consultar por si pueden aportar un poco de luz al error que haya aparecido.

Ejecucin
Para ejecutar la aplicacin HolaMundoApp, hemos de recurrir al intrprete java, que tambin se encuentra en el directorio bin, bajo el directorio java. Se ejecutar la aplicacin con la lnea:
%java HolaMundoApp

y debera aparecer en pantalla la respuesta de Java:


%Hola Mundo!

El smbolo % representa al prompt del sistema, y lo utilizaremos para presentar las respuestas que nos ofrezca el sistema como resultado de la ejecucin de los comandos que se indiquen en pantalla o para indicar las lneas de comandos a introducir.

Problemas de compilacin
A continuacin presentamos una lista de los errores ms frecuentes que se presentan a la hora de compilar un fichero con cdigo fuente Java, nos basaremos en errores provocados sobre nuestra mnima aplicacin Java de la seccin anterior, pero podra generalizarse sin demasiados problemas.
%javac: Command not found

No se ha establecido correctamente la variable PATH del sistema para el compilador javac. El compilador javac se encuentra en el directorio bin, que cuelga del directorio java, que cuelga del directorio donde se haya instalado el JDK (Java Development

Tutorial de Java

61

Kit).
%HolaMundoApp.java:3: Method printl(java.lang.String) not found in class java.io.PrintStream. System.out.printl( HolaMundo!); ^

Error tipogrfico, el mtodo es println no printl.


%In class HolaMundoApp: main must be public and static

Error de ejecucin, se olvid colocar la palabra static en la declaracin del mtodo main de la aplicacin.
%Cant find class HolaMundoApp

Este es un error muy sutil. Generalmente significa que el nombre de la clase es distinto al del fichero que contiene el cdigo fuente, con lo cual el fichero nombre_fichero.class que se genera es diferente del que cabra esperar. Por ejemplo, si en nuestro fichero de cdigo fuente de nuestra aplicacin HolaMundoApp.java colocamos en vez de la declaracin actual de la clase HolaMundoApp, la lnea:
class HolaMundoapp {

se crear un fichero HolaMundoapp.class, que es diferente del HolaMundoApp.class, que es el nombre esperado de la clase; la diferencia se encuentra en la a minscula y mayscula.

El visor de Applets de Sun (appletviewer)


El visualizador de applets (appletviewer) es una aplicacin que permite ver en funcionamiento applets, sin necesidad de la utilizacin de un navegador World-Wide-Web como HotJava, Microsoft Explorer o Nescape. En adelante, recurriremos muchas veces a l, ya que el objetivo del tutorial es el lenguaje Java.

Applet
La definicin ms extendida de applet, muy bien resumida por Patrick Naughton, indica que un applet es una pequea aplicacin accesible en un servidor Internet, que se transporta por la red, se instala automticamente y se ejecuta in situ como parte de un documento web. Claro que as la definicin establece el entorno (Internet, Web, etc.). En realidad, un applet es una aplicacin pretendidamente corta (nada impide que ocupe ms de un gigabyte, a no ser el pensamiento de que se va a transportar por la red y una mente sensata) basada en un formato grfico sin representacin independiente: es decir, se trata de un elemento a embeber en otras aplicaciones; es un componente en su sentido estricto. Un ejemplo en otro mbito de cosas podra ser el siguiente: Imaginemos una empresa, que cansada de empezar siempre a codificar desde cero, disea un formulario con los datos bsicos de una persona (nombre, direccin, etc.). Tal formulario no es un dilogo por s mismo, pero se podra integrar en dilogos de clientes, proveedores, empleados, etc. El hecho de que se integre esttica (embebido en un ejecutable) o dinmicamente (intrpretes,

62

Tutorial de Java

DLLs, etc.) no afecta en absoluto a la esencia de su comportamiento como componente con que construir dilogos con sentido autnomo. Pues bien, as es un applet. Lo que ocurre es que, dado que no existe una base adecuada para soportar aplicaciones industriales Java en las que insertar nuestras miniaplicaciones (aunque todo se andar), los applets se han construido mayoritariamente, y con gran acierto comercial (parece), como pequeas aplicaciones interactivas, con movimiento, luces y sonido... en Internet.

Llamadas a Applets con appletviewer


Un applet es una mnima aplicacin Java diseada para ejecutarse en un navegador Web. Por tanto, no necesita preocuparse por un mtodo main() ni en dnde se realizan las llamadas. El applet asume que el cdigo se est ejecutando desde dentro de un navegador. El appletviewer se asemeja al mnimo navegador. Espera como argumento el nombre del fichero html que debe cargar, no se le puede pasar directamente un programa Java. Este fichero html debe contener una marca que especifica el cdigo que cargar el appletviewer:
<HTML> <APPLET CODE=HolaMundo.class WIDTH=300 HEIGHT=100> </APPLET> </HTML>

El appletviewer crear un espacio de navegacin, incluyendo un rea grfica, donde se ejecutar el applet, entonces llamar a la clase applet apropiada. En el ejemplo anterior, el appletviewer cargar una clase de nombre HolaMundo y le permitir trabajar en su espacio grfico.

Arquitectura de Appletviewer
El appletviewer representa el mnimo interfaz de navegacin. En la figura se muestran los pasos que seguira appletviewer para presentarnos el resultado de la ejecucin del cdigo de nuestra clase.

Tutorial de Java

63

Esta es una visin simplificada del appletviewer. La funcin principal de esta aplicacin es proporcionar al usuario un objeto de tipo Graphics sobre el que dibujar, y varias funciones para facilitar el uso del objeto Graphics.

Ciclo de vida de un Applet


Cuando un applet se carga en el appletviewer, comienza su ciclo de vida, que pasara por las siguientes fases: Se crea una instancia de la clase que controla el applet. En el ejemplo de la figura anterior, sera la clase HolaMundo. El applet se incializa. El applet comienza a ejecutarse. El applet empieza a recibir llamadas. Primero recibe una llamada init (inicializar), seguida de un mensaje start (empezar) y paint (pintar). Estas llamadas pueden ser recibidas asncronamente.

Mtodos de Appletviewer
Vamos a utilizar como excusa la funcin asociada al appletviewer de los siguientes mtodos para adentrarnos en su presentacin, aunque a lo largo de secciones posteriores, volveremos a referirnos a ellos, porque tambin son los mtodos propios de la clase Applet. init() El mtodo init() se llama cada vez que el appletviewer carga por primera vez la clase. Si el applet llamado no lo sobrecarga, init() no hace nada. Fundamentalmente en este mtodo se debe fijar el tamao del applet, aunque en el caso de Netscape el tamao que vale es el que se indique en la lnea del fichero html que cargue el applet. Tambin se deben realizar en este mtodo las cargas de imgenes y sonidos necesarios para la ejecucin del applet. Y, por supuesto, la asignacin de valores a las variables globales a la clase que se utilicen. En el caso de los applet, este mtodo nicamente es llamado por el sistema al cargar el applet. start()

start() es la llamada para arrancar el applet cada vez que es visitado. La clase Applet no hace nada en este mtodo. Las clases derivadas deben sobrecargarlo para comenzar la animacin, el sonido, etc. Esta funcin es llamada automticamente cada vez que la zona de visualizacin en que est ubicado el applet se expone a la visin, a fin de optimizar en uso de los recursos del sistema y no ejecutar algo que no puede ser apreciado (aunque el programador puede variar este comportamiento y hacer que un applet siga activo aun cuando est fuera del rea de visin). Esto es, imaginemos que cargamos un applet en un navegador minimizado; el sistema llamar al mtodo init(), pero no a start(), que s ser llamado cuando restauremos el navegador a un tamao que permita ver el applet. Naturalmente, start() se puede ejecutar varias 64 Tutorial de Java

veces: la primera tras init() y las siguientes (porque init() se ejecuta solamente una vez) tras haber aplicado el mtodo stop(). stop()

stop() es la llamada para detener la ejecucin del applet. Se llama cuando el applet desaparece de la pantalla. La clase Applet tampoco hace nada en este mtodo, que debera ser sobrecargado por las clases derivadas para detener la animacin, el sonido, etc. Esta funcin es llamada cuando el navegador no incluye en su campo de visin al applet; por ejemplo, cuando abandona la pgina en que est insertado, de forma que el programador puede paralizar los threads que no resulten necesarios respecto de un applet no visible, y luego recuperar su actividad mediante el mtodo start().
destroy() El mtodo destroy() se llama cuando ya no se va a utilizar ms el applet, cuando se necesita que sean liberados todos los recursos dispuestos por el applet, por ejemplo, cuando se cierra el navegador. La clase Applet no hace nada en este mtodo. Las clases derivadas deberan sobrecargarlo para hacer una limpieza final. Los applet multithread deberan utilizar destroy() para detener los threads que quedasen activos. El appletviewer tambin contiene la clase Component (componente), que usa dos mtodos para ayudas al applet a escribir en el espacio grfico que el appletviewer le proporciona para su ejecucin. paint( Graphics g ) Es la funcin llamada cada vez que el rea de dibujo del applet necesita ser refrescada. La clase Applet simplemente dibuja un rectngulo gris en el rea, es la clase derivada, obviamente, la que debera sobrecargar este mtodo para representar algo inteligente en la pantalla. Cada vez que la zona del applet es cubierta por otra ventana, se desplaza el applet fuera de la visin o el applet cambia de posicin debido a un redimensionamiento del navegador, el sistema llama automticamente a este mtodo, pasando como argumento un objeto de tipo Graphics que delimita la zona a ser pintada; en realidad se pasa una referencia al contexto grfico en uso, y que representa la ventana del applet en la pgina web. update( Graphics g ) Esta es la funcin que realmente se llama cuando se necesita una actualizacin de la pantalla. La clase Applet simplemente limpia el rea y llama al mtodo paint(). Esta funcionalidad es suficiente para la mayora de los casos; aunque, de cualquier forma, las clases derivadas pueden sustituir esta funcionalidad para sus propsitos especiales. Es decir, en las situaciones detalladas anteriormente que daan la zona de exposicin del applet, el sistema llama al mtodo paint(), pero en realidad la llamada se realiza al mtodo update() , cuyo comportamiento establecido en la clase Component es llamar al mtodo paint(), tras haber rellenado la zona del applet con

Tutorial de Java

65

su color de fondo por defecto. Pudiera parecer as que se trata de un mtodo de efecto neutro, pero si la funcin paint() cambiara el color del fondo, podramos percibir un flick de cambio de colores nada agradable. Por tanto, habr que cuidarse por lo comn, de eliminar este efecto de limpia primero, sobrecargando el mtodo update(), para que llame nicamente a paint(). Otra solucin sera insertar el cdigo de pintado en una sobrecarga del mtodo update() y escribir un mtodo paint() que slo llame a update(). La ltima solucin pasara por usar el mismo mtodo setBackground( Color ), en el mtodo init() para as evitar el efecto visual sin tener que sobrecargar el mtodo update(). Estas son las mismas razones que aconsejan usar el mtodo resize() inserto en init(), para evitar el mismo desagradable efecto. repaint Llamando a este mtodo se podr forzar la actualizacin de un applet, la llamada a update(). Pero hay que tener cierto cuidado, porque AWT posee cierta inteligencia (combinacin casi siempre nefasta), de forma que si se llama a update() mediante repaint() con una frecuencia muy corta, AWT ignorar las llamadas a update() que estime oportuno, pues considera a esta funcin como un bien escaso.

Sinopsis
La llamada a appletviewer es de la forma:
appletviewer [-debug] urls...

El appletviewer toma como parmetro de ejecucin, o bien el nombre del un fichero html conteniendo el tag (marca) <APPLET>, o bien un URL hacia un fichero HTML que contenga esa marca. Si el fichero html no contiene un tag <APPLET> vlido, el appletviewer no har nada. El appletviewer no muestra otras marcas html. La nica opcin vlida que admite la llamada a appletviewer es -debug, que arranca el applet en el depurador de Java, jdb. Para poder ver el cdigo fuente en el depurador, se tiene que compilar el fichero .java con la opcin -g.

Ejemplo de uso
En el ejemplo de llamada al appletviewer siguiente, hacemos que se ejecute el applet bsico que crearemos en la seccin siguiente y que lanzaremos desde un fichero html del mismo nombre que nuestro fichero de cdigo fuente Java.
%appletviewer HolaMundo.html

Esta llamada lanzara la ejecucin de HolaMundo.class en el appletviewer, abrindose en pantalla la ventana siguiente:

66

Tutorial de Java

Funciones de men de Appletviewer


El appletviewer tiene un nico men mostrado en la imagen siguiente y que vamos a explicar en cada una de sus opciones, ya que lo usaremos a menudo cuando vayamos avanzando en nuestros conocimientos de Java.

Restart

La funcin Restart llama al mtodo stop() y seguidamente llama de nuevo a start(), que es el mtodo que ha lanzado inicialmente la ejecucin del applet. Se puede utilizar Restart para simular el movimiento entre pginas en un documento html. Reload La funcin Reload llama al mtodo stop() y luego al mtodo destroy() en el applet actual. A continuacin carga una nueva copia del applet y la arranca llamando al mtodo start(). Clone La funcin Clone crea una copia del applet actual en una ventana de appletviewer nueva. En realidad es un appletviewer idntico con el mismo URL. Tag

Tutorial de Java

67

La funcin Tag muestra en una ventana hija del appletviewer el cdigo html cargado para su ejecucin. Es similar a la funcin View Source que figura en la mayora de los navegadores, Netscape, Internet Explorer y HotJava incluidos. Info La funcin Info lee los comentarios de documentacin contenidos en el fichero html y muestra la informacin de los parmetros (si la hay). Properties El appletviewer tiene las funciones bsicas de presentacin de un navegador y la funcin Properties (propiedades de la red) permite cambiar o establecer el modo de seguridad o fijar los servidores de proxy o firewall. Close La funcin Close llama al mtodo destroy() de la ventana actual del appletviewer, teminando su ejecucin. Quit La funcin Quit llama al mtodo destroy() de cada una de las copias del appletviewer que se encuentren lanzadas, concluyendo la ejecucin de todas ellas y terminando entonces el appletviewer.

Un applet bsico en Java


Vamos a comenzar la creacin del cdigo fuente del un applet que satisfaga nuestras necesidades. Recordamos que Java utiliza la extensin .java para designar los ficheros fuente.

HolaMundo
A continuacin est el cdigo fuente del applet HolaMundo, que es la versin applet de la mnima aplicacin Java que antes habamos escrito. Guardar este cdigo en un fichero fuente Java como HolaMundo.java.
// // Applet HolaMundo de ejemplo // import java.awt.Graphics; import java.applet.Applet; public class HolaMundo extends Applet { public void paint( Graphics g ) { g.drawString( Hola Mundo!,25,25 ) ; } }

68

Tutorial de Java

Componentes bsicos de un Applet


El lenguaje Java implementa un modelo de Programacin Orientada a Objetos. Los objetos sirven de bloques centrales de construccin de los programas Java. De la misma forma que otros lenguajes de programacin, Java tiene variables de estado y mtodos. Veamos como se descompone un applet en sus piezas/objetos:
/* Seccin de importaciones */ public class NombreDelNuevoApplet extends Applet { /* Aqu se declaran las variables de estado (public y private) */ /* Los mtodos para la interaccin con los objetos se declaran y definen aqu */ public void MetodoUno( parmetros ) { /* Aqu viene para cada mtodo, el cdigo Java que desempea la tarea. Qu cdigo se use depende del applet */ } }

Para HolaMundo, se importan las dos clases que necesita. No hay variables de estado, y slo se tiene que definir un mtodo para que el applet tenga el comportamiento esperado.

Clases incluidas
El comando import carga otras clases dentro de nuestro cdigo fuente. El importar una clase desde un paquete de Java hace que esa clase importada est disponible para todo el cdigo incluido en el fichero fuente Java que la importa. Por ejemplo, en el applet HolaMundo se importa la clase java.awt.Graphics, y podremos llamar a los mtodos de esta clase desde cualquier mtodo de nuestro programa que se encuentre en el fichero HolaMundo.java. Esta clase define una rea grfica y mtodos para poder dibujar dentro de ella. La funcin paint() declara a g como un objeto de tipo Graphics; luego, paint() usa el mtodo drawString() de la clase Graphics para generar su salida.

La clase Applet
Se puede crear una nueva clase, en este caso HolaMundo, extendiendo la clase bsica de Java: Applet. De esta forma, se hereda todo lo necesario para crear un applet. Modificando determinados mtodos del applet, podemos lograr que lleve a cabo las funciones que deseamos.
import java.applet.Applet; . . . public class HolaMundo extends Applet {

Tutorial de Java

69

Mtodos de Applet
La parte del applet a modificar es el mtodo paint(). En la clase Applet, se llama al mtodo paint() cada vez que el mtodo arranca o necesita ser refrescado, pero no hace nada. En nuestro caso, lo que hacemos es:
public void paint( Graphics g ) { g.drawString( Hola Mundo!,25,25 ); }

De acuerdo a las normas de sobrecarga, se ejecutar este ltimo paint() y no el paint() vaco de la clase Applet. Luego, aqu se ejecuta el mtodo drawString(), que le dice al applet cmo debe aparecer un texto en el rea de dibujo. Otros mtodos bsicos para dibujar son:
drawLine( int x1,int y1,int x2,int y2 ) drawRect( int x,int y,int ancho,int alto ) drawOval( int x,int y,int ancho,int alto )

Tanto para drawRect() como para drawOval(), las coordenadas (x,y) son la esquina superior izquierda del rectngulo (para drawOval, el valo es encajado en el rectngulo que lo circunscribe).

Compilacin de un applet
Ahora que tenemos el cdigo de nuestro applet bsico y el fichero fuente Java que lo contiene, necesitamos compilarlo y obtener un fichero .class ejecutable. Se utiliza el compilador Java, javac, para realizar la tarea. El comando de compilacin ser:
%javac HolaMundo.java

Eso es todo. El compilador javac generar un fichero HolaMundo.class que podr ser llamado desde cualquier navegador con soporte Java y, por tanto, capaz de ejecutar applets Java.

Llamada a Applets
Qu tienen de especial HotJava, Microsoft Explorer o Netscape con respecto a otros navegadores? Con ellos se puede ver html bsico y acceder a todo el texto, grfico, sonido e hipertexto que se pueda ver con cualquier otro navegador. Pero adems, pueden ejecutar applets, que no es html estndar. Ambos navegadores entienden cdigo html que lleve la marca <APPLET>:
<APPLET CODE=SuCodigo.class WIDTH=100 HEIGHT=50> </APPLET>

Esta marca html llama al applet SuCodigo.class y establece su ancho y alto inicial. Cuando se acceda a la pgina Web donde se encuentre incluida la marca, se ejecutar el byte-code contenido en SuCodigo.class, obtenindose el resultado de la ejecucin del applet en la ventana del navegador, con soporte Java, que estemos utilizando.

70

Tutorial de Java

Prueba de un Applet
71El JDK, Kit de Desarrollo de Java, incluye el visor de applets bsico, appletviewer, que puede utilizarse para la visualizacin rpida y prueba de nuestros applets, tal como se ha visto ya. La ejecucin de un applet sobre appletviewer se realiza a travs de la llamada:
%appletviewer fichero.html

En nuestro caso el fichero con el cdigo html que ejecutar nuestro applet HolaMundo es HolaMundo.html que generar la salida que se mostraba en la seccin sobre el Ejemplo de uso de appletviewer.

La marca APPLET de HTML


Dado que los applets estn mayormente destinados a ejecutarse en navegadores Web, haba que preparar el lenguaje HTML para soportar Java, o mejor, los applets. El esquema de marcas de HTML, y la evolucin del estndar marcado por Netscape hicieron fcil la adicin de una nueva marca que permitiera, una vez aadido el correspondiente cdigo gestor en los navegadores, la ejecucin de programas Java en ellos. La sintaxis de las etiquetas <APPLET> y <PARAM> es la que se muestra a continuacin y que iremos explicando en prrafos posteriores:
<APPLET CODE= WIDTH= HEIGTH= [CODEBASE=] [ALT=] [NAME=] [ALIGN=] [VSPACE=] [HSPACE=]> <PARAM NAME= VALUE= > </APPLET>

Atributos obligatorios:
CODE : Nombre de la clase principal WIDTH : Anchura inicial HEIGHT : Altura inicial

Atributos opcionales:
CODEBASE : URL base del applet ALT : Texto alternativo NAME : Nombre de la instancia ALIGN : Justificacin del applet VSPACE : Espaciado vertical HSPACE : Espaciado horizontal Los applets se incluyen en las pginas Web a travs de la marca <APPLET>, que para quien conozca html resultar muy similar a la marca <IMG>. Ambas necesitan la referencia

Tutorial de Java

71

a un fichero fuente que no forma parte de la pgina en que se encuentran embebidos. IMG hace esto a travs de SRC=parmetro y APPLET lo hace a travs CODE=parmetro. El parmetro de CODE indica al navegador dnde se encuentra el fichero con el cdigo Java compilado .class. Es una localizacin relativa al documento fuente. Por razones que no entiendo muy bien, pero posiblemente relacionadas con los packages y classpaths, si un applet reside en un directorio diferente del que contiene a la pgina en que se encuentra embebido, entonces no se indica un URL a esta localizacin, sino que se apunta al directorio del fichero .class utilizando el parmetro CODEBASE, aunque todava se puede usar CODE para proporcionar el nombre del fichero .class. Al igual que IMG, APPLET tiene una serie de parmetros que lo posicionan en la pgina. WIDTH y HEIGHT especifican el tamao del rectngulo que contendr al applet, se indican en pixels. ALIGN funciona igual que con IMG (en los navegadores que lo soportan), definiendo cmo se posiciona el rectngulo del applet con respecto a los otros elementos de la pgina. Los valores posibles a especificar son: LEFT, RIGHT, TOP, TEXTTOP, MIDDLE, ABSMIDDLE, BASELINE, BOTTOM y ABSBOTTOM. Y, finalmente, lo mismo que con IMG, se puede especificar un HSPACE y un VSPACE en pixels para indicar la cantidad de espacio vaco que habr de separacin entre el applet y el texto que le rodea. APPLET tiene una marca ALT. La utilizara un navegador que entendiese la marca APPLET, pero que por alguna razn, no pudiese ejecutarlo. Por ejemplo, si un applet necesita escribir en el disco duro de nuestro ordenador, pero en las caractersticas de seguridad tenemos bloqueada esa posibilidad, entonces el navegador presentara el texto asociado a ALT. ALT no es utilizado por los navegadores que no entienden la marca APPLET, por ello se ha definido la marca </APPLET>, que finaliza la descripcin del applet. Un navegador con soporte Java ignorar todo el texto que haya entre las dos marcas <APPLET> y </APPLET>, sin embargo, un navegador que no soporte Java ignorar las marcas y presentar el texto que se encuentre entre ellas.

Atributos de APPLET
Los atributos que acompaan a la etiqueta <APPLET>, algunos son obligatorios y otros son opcionales. Todos los atributos, siguiendo la sintaxis de html, se especifican de la forma: atributo=valor. Los atributos obligatorios son: CODE Indica el fichero de clase ejecutable, que tiene la extensin .class. No se permite un URL absoluto, como ya se ha dicho, aunque s puede ser relativo al atributo opcional CODEBASE. WIDTH Indica la anchura inicial que el navegador debe reservar para el applet en pixels. HEIGHT

72

Tutorial de Java

Indica la altura inicial en pixels. Un applet que disponga de una geometra fija no se ver redimensionado por estos atributos. Por ello, si los atributos definen una zona menor que la que el applet utiliza, nicamente se ver parte del mismo, como si se visualiza a travs de una ventana, eso s, sin ningn tipo de desplazamiento. Los atributos opcionales que pueden acompaar a la marca APPLET comprenden los que se indican a continuacin: CODEBASE Se emplea para utilizar el URL base del applet. En caso de no especificarse, se utilizar el mismo que tiene el documento. ALT Como ya se ha dicho, funciona exactamente igual que el ALT de la marca <IMG>, es decir, muestra un texto alternativo, en este caso al applet, en navegadores en modo texto o que entiendan la etiqueta APPLET pero no implementen una mquina virtual Java. NAME Otorga un nombre simblico a esta instancia del applet en la pgina que puede ser empleado por otros applets de la misma pgina para localizarlo. As, un applet puede ser cargado varias veces en la misma pgina tomando un nombre simblico distinto en cada momento. ALIGN Se emplea para alinear el applet permitiendo al texto fluir a su alrededor. Puede tomas los siguientes valores: LEFT, RIGHT, TOP, TEXTTOP, MIDDLE, ABSMIDDLE, BASELINE, BOTTOM y ABSBOTTOM. VSPACE Indica el espaciado vertical entre el applet y el texto, en pixels. Slo funciona cuando se ha indicado ALIGN = LEFT o RIGHT. HSPACE Funciona igual que el anterior pero indicando espaciamiento horizontal, en pixels. Slo funciona cuando se ha indicado ALIGN = LEFT o RIGHT. Es probable encontrar en algunas distribuciones otras etiquetas para la inclusin de applets, como <APP>. Esto se debe a que estamos ante la tercera revisin de la extensin de HTML para la incrustacin de applets y ha sido adoptada como la definitiva. Por ello, cualquier otro medio corresponde a implementaciones obsoletas que han quedado descartadas.

Tutorial de Java

73

Paso de parmetros a los applets


El espacio que queda entre las marcas de apertura y cierre de la definicin de un applet, se utiliza para el paso de parmetros al applet. Para ello se utiliza la marca PARAM en la pgina HTML para indicar los parmetros y el mtodo getParameter() de la clase java.applet.Applet para leerlos en el cdigo interno del applet. La construccin puede repetirse cuantas veces se quiera, una tras otra. Los atributos que acompaan a la marca PARAM son los siguientes: NAME Nombre del parmetro que se desea pasar al applet. VALUE Valor que se desea transmitir en el parmetro que se ha indicado antes. Texto HTML Texto HTML que ser interpretado por los navegadores que no entienden la marca APPLET en sustitucin del applet mismo. Para mostar esta posibilidad vamos a modificar nuestro applet bsico HolaMundo para que pueda saludar a cualquiera. Lo que haremos ser pasarle al applet el nombre de la persona a quien queremos saludar. Generamos el cdigo para ello y lo guardamos en el fichero HolaTal.java
import java.awt.Graphics; import java.applet.Applet; public class HolaTal extends Applet { String nombre; public void init() { nombre = getParameter( Nombre ); } public void paint( Graphics g ) { g.drawString( Hola +nombre+!,25,25 ); } }

Si compilamos el ejemplo obtendremos el fichero HolaTal.class que incluiremos en nuestra pgina Web. Vamos a generar el fichero HolaTal.html, en el que incluiremos nuestro applet, y que debera tener el siguiente contenido:
<HTML> <APPLET CODE=HolaTal.class WIDTH=300 HEIGHT=100> <PARAM NAME=Nombre VALUE=Agustin> </APPLET> </HTML>

Por supuesto, que puedes sustituir mi nombre por el tuyo. Este cambio no afectar al cdigo Java, no ser necesario recompilarlo para que te salude a ti el applet.

74

Tutorial de Java

Los parmetros no se limitan a uno solo. Se puede pasar al applet cualquier nmero de parmetros y siempre hay que indicar un nombre y un valor para cada uno de ellos. El mtodo getParameter() es fcil de entender. El nico argumento que necesita es el nombre del parmetro cuyo valor queremos recuperar. Todos los parmetros se pasan como Strings, en caso de necesitar pasarle al applet un valor entero, se ha de pasar como String, recuperarlo como tal y luego convertirlo al tipo que deseemos. Tanto el argumento de NAME como el de VALUE deben ir colocados entre dobles comillas () ya que son String. El hecho de que las marcas <APPLET> y <PARAM> sean ignoradas por los navegadores que no entienden Java, es inteligentemente aprovechado a la hora de definir un contenido alternativo a ser mostrado en este ltimo caso. As la etiqueta es doble:
<APPLET atributos> parmetros contenido alternativo </APPLET>

Nuestro fichero para mostrar el applet de ejemplo lo modificaremos para que pueda ser visualizado en cualquier navegador y en unos casos presente la informacin alternativa y en otros, ejcute nuestro applet:
<HTML> <APPLET CODE=HolaTal.class WIDTH=300 HEIGHT=100> <PARAM NAME=Nombre VALUE=Agustin> No vers lo bueno hasta que consigas un navegador <I>Java Compatible</I> </APPLET> </HTML>

Tokens en parmetros de llamada


Ya de forma un poco ms avanzada vamos a ver como tambin se pueden pasar varios parmetros en la llamada utilizando separadores, o lo que es lo mismo, separando mediante delimitadores los parmetros, es decir, tokenizando la cadena que contiene el valor del parmetro, por ejemplo:
<PARAM NAME=Nombre VALUE=Agustin|Antonio>

En este caso el separador es la barra vertical |, que delimita los dos tokens, pero tambin podemos redefinirlo y utilizar cualquier otro smbolo como separador:
<PARAM NAME=Separador VALUE=#> <PARAM NAME=Nombre VALUE=Agustin#Antonio>

Si ahora intentamos cambiar de color de fondo en que aparecen los textos en el applet, utilizando el mismo mtodo, podramos tener:
<PARAM NAME=Nombre VALUE=Agustin|Antonio> <PARAM NAME=Color VALUE=green|red>

Es ms, podramos hacer que parpadeasen los mensajes en diferentes colores, cambiando el color de fondo y el del texto:
<PARAM NAME=Nombre1 VALUE=Agustin|green|yellow> <PARAM NAME=Nombre2 VALUE=Antonio|red|white>

Tutorial de Java

75

Para recoger los parmetros pasados en este ltimo caso, bastara con hacer un pequeo bucle de lectura de los parmetros que deseamos:
for( int i=1; ; i++ ) p = getParameter( Nombre+i ); if( p == null ) break; ... }

incluso podramos utilizar un fichero para pasar parmetros al applet. La llamada sera del mismo tipo:
<PARAM NAME=Fichero VALUE=FicheroDatos>

y el FicheroDatos debera tener un contenido, en este caso, que sera el siguiente:


Agustin fondoColor=green textoColor=yellow fuente=Courier fuenteTam=14 Antonio fondoColor=red textocolor=white

E incluso ese FicheroDatos, podramos hacer que se encontrase en cualquier URL, de forma que utilizando el mtodo getContent() podramos recuperar el contenido del fichero que contiene los parmetros de funcionamiento del applet:
String getContent( String url ) { URL url = new URL( null,url ); return( (String).url.getContent() ); }

Para recuperar los parmetros que estn incluidos en la cadena que contiene el valor podemos utilizar dos mtodos:
StringTokenizer( string,delimitadores ) treamTokenizer( streamentrada )

As en la cadena Agustin|Antonio si utilizamos el mtodo:


StringTokenizer( cadena,| );

obtenemos el token Agustin, el delimitador | y el token Antonio. El cdigo del mtodo sera el que se muestra a continuacin:
// Capturamos el parmetro p = getParameter( p ); // Creamos el objeto StringTokenizer st = new StringTokenizer( p,| ); // Creamos el almacenamiento cads = new String[ st.countTokens() ]; // Separamos los tokens de la cadena del parmetro for( i=0; i < cads.length; i++ ) cadenas[i] = st.nextToken();

76

Tutorial de Java

En el caso de que utilicemos un fichero como verdadera entrada de parmetros al applet y el fichero se encuentre en una direccin URL, utilizamos el mtodo StreamTokenizer() para obtener los tokens que contiene ese fichero:
// Creamos el objeto URL para acceder a l url = new URL( http://www.prueba.es/Fichero ); // Creamos el canal de entrada ce = url.openStream(); // Creamos el objeto StreamTokenizer st = new StreamTokenizer( ce ); // Capturamos los tokens st.nextToken();

El parmetro ARCHIVE
Una de las cosas que se achacan a Java es la rapidez. El factor principal en la percepcin que tiene el usuario de la velocidad y valor de los applets es el tiempo que tardan en cargarse todas las clases que componen el applet. Algunas veces tenemos que estar esperando ms de un minuto para ver una triste animacin, ni siquiera buena. Y, desafortunadamente, esta percepcin de utilidad negativa puede recaer tambin sobre applets que realmente s son tiles. Para entender el porqu de la necesidad de un nuevo mtodo de carga para acelerarla, necesitamos comprender porqu el mtodo actual es lento. Normalmente un applet se compone de varias clases, es decir, varios ficheros .class. Por cada uno de estos ficheros .class, el cargador de clases debe abrir una conexin individual entre el navegador y el servidor donde reside el applet. As, si un applet se compone de 20 ficheros .class, el navegador necesitar abrir 20 sockets para transmitir cada uno de los ficheros. La sobrecarga que representa cada una de estas conexiones es relativamente significante. Por ejemplo, cada conexin necesita un nmero de paquetes adicionales que incrementan el trfico en la Red. Me imagino que ya el lector habr pensado la solucin al problema: poner todos los ficheros en uno solo, con lo cual solamente sera necesaria una conexin para descargar todo el cdigo del applet. Bien pensado. Esto es lo mismo que han pensado los dos grandes competidores en el terreno de los navegadores, Netscape y Microsoft. Desafortunadamente, las soluciones que han implementado ambas compaas no son directamente compatibles. Microsoft, en su afn de marcar diferencia, crea su propio formato de ficheros CAB. La solucin de Netscape es utilizar el archiconocido formato ZIP. Por suerte, nosotros podemos escribir nuestro cdigo HTML de forma que maneje ambos formatos, en caso necesario. Esto es as porque podemos especificar cada uno de estos formatos de ficheros especiales en extensiones separadas de la marca <APPLET>. No vamos a contar la creacin de ficheros CAB; quien est interesado puede consultar la documentacin de Java que proporciona Microsoft con su SDK para Java, que es bastante exhaustiva al respecto. Una vez que disponemos de este fichero, podemos aadir un

Tutorial de Java

77

parmetro CABBASE a la marca <APPLET>:


<APPLET NAME=Hola CODE=HolaMundo WIDTH=50 HEIGHT=50 > <PARAM NAME=CODEBASE VALUE=http://www.ejemplo.es/classes> <PARAM NAME=CABBASE VALUE=hola.cab> </APPLET>

El VALUE del parmetro CABBASE es el nombre del fichero CAB que contiene los ficheros .class que componen el conjunto de applet. Crear un archivo ZIP para utilizarlo con Netscape es muy fcil. Se deben agrupar todos los ficheros .class necesarios en un solo fichero .zip. Lo nico a tener en cuenta es que solamente hay que almacenar los ficheros .class en el archivo; es decir, no hay que comprimir. Si se est utilizando pkzip, se hara:
Pkzip -e0 archivo.zip listaFicherosClass

El parmetro de la lnea de comandos es el nmero cero, no la O mayscula. Para utilizar un fichero .zip hay que indicarlo en la marca ARCHIVE de la seccin <APPLET>:
<APPLET NAME=Hola CODE=HolaMundo WIDTH=50 HEIGHT=50 CODEBASE VALUE=http://www.ejemplo.es/classes ARCHIVE=hola.zip> </APPLET>

Pero hay ms. Podemos crear ambos tipos de ficheros y hacer que tanto los usuarios de Netscape Navigator como los de Microsoft Internet Explorer puedan realizar descargas rpidas del cdigo del applet. No hay que tener en cuenta los usuarios de otros navegadores, o de versiones antiguas de estos dos navegadores, porque ellos todava podrn seguir cargando los ficheros a travs del mtodo lento habitual. Para compatibilizarlo todo, ponemos las piezas anteriores juntas:
<APPLET NAME=Hola CODE=HolaMundo WIDTH=50 HEIGHT=50 CODEBASE VALUE=http://www.ejemplo.es/classes ARCHIVE=hola.zip> <PARAM NAME=CABBASE VALUE=hola.cab> <PARAM NAME=CODEBASE VALUE=http://www.ejemplo.es/classes> <PARAM NAME=CABBASE VALUE=hola.cab> </APPLET>

Ahora que se puede hacer esto con ficheros .cab y .zip, JavaSoft ha definido un nuevo formato de ficheros, que incorporar en del JDK 1.1, para incluir juntos todos los ficheros de imgenes, sonido y class. JavaSoft llama a esto formato JAR (Java Archive). La marca <APPLET> de HTML se modificar para manejar este nuevo formato JAR a travs del parmetro ARCHIVES. Y dejamos al lector el trabajo de poner los tres formatos juntos bajo el mismo paraguas de la marca <APPLET>.

Depuracin general
Compilar y ejecutar el programa HolaMundo.java a travs del fichero HolaMundo.html no debera suponer ningn problema, pero alguna vez nos encontraremos frente a programas

78

Tutorial de Java

ms difciles y se necesitar el truco de depuracin al que se recurre en el desarrollo de programas en cualquier lenguaje.

System.out.println
Una de las herramientas de depuracin ms efectivas en cualquier lenguaje de programacin es simplemente la salida de informacin por pantalla. El comando System.out.println imprime la cadena que se le especifique en la ventana de texto en la que se invoc al navegador. La forma de usarlo se muestra a continuacin:
public void paint( Graphics g ) { g.drawString( Hola Mundo!,25,25 ); System.out.println( Estamos en paint() ); }

Ciclo de vida de un applet


Para seguir el ciclo de vida de un applet, supondremos que estamos ejecutando en nuestro navegador el applet bsico HolaMundo, a travs de la pgina HTML que lo carga y corre. Lo primero que aparece son los mensajes initializing... starting..., como resultado de la carga del applet en el navegador. Una vez cargado, lo que sucede es:

Se crea una instancia de la clase que controla al applet El applet se inicializa a si mismo Comienza la ejecucin del applet
Cuando se abandona la pgina, por ejemplo, para ir a la siguiente, el applet detiene la ejecucin. Cuando se regresa a la pgina que contiene el applet, se reanuda la ejecucin. Si se utiliza la opcin del navegador de Reload, es decir, volver a cargar la pgina, el applet es descargado y vuelto a cargar. El applet libera todos los recursos que hubiese acaparado, detiene su ejecucin y ejecuta su finalizador para realizar un proceso de limpieza final de sus trazas. Despus de esto, el applet se descarga de la memoria y vuelve a cargarse volviendo a comenzar su inicializacin. Finalmente, cuando se concluye la ejecucin del navegador, o de la aplicacin que est visualizando el applet, se detiene la ejecucin del applet y se libera toda la memoria y recursos ocupados por el applet antes de salir del navegador.

Proteccin de applets
Como curiosidad, ms que como algo verdaderamente til, podemos proteger nuestros applets de forma muy sencilla, o por lo menos evitar que nadie pueda ocultar en sus pginas HTML que nosotros somos los legales autores de un applet.

Tutorial de Java

79

El mtodo es muy sencillo y se basa en la utilizacin de un parmetro del cual comprobamos su existencia, por ejemplo:
<PARAM NAME=copyright VALUE=Applet de Prueba, A.Froufe (C)1996, Todos los derechos reservados>

y en el cdigo Java de nuestro applet, comprobaramos que efectivamente el parmetro copyright existe y ese es su contenido:
if( !getParameter( copyright).equals( ... ) throw( new Exception( Violacion del Copyright ) );

donde ... es el texto completo del valor del parmetro. Pero tambin podemos hacerlo de forma ms elegante:
copyright = getParameter( copyright ); // System.out.println( copyright.hashCode() ); if( copyright != -913936799 ) throw( new Exception( Violacion del Copyright ) );

en donde la sentencia comentada nos proporciona el valor del copyright para poder introducirlo en la comparacin de la presencia o no del parmetro en la llamada al applet. Habra que declarar y definir correctamente tipos y variables, pero la idea bsica es la que expuesta.

Escribir applets en Java


Para escribir applets Java, hay que utilizar una serie de mtodos, algunos de los cuales ya se hay sumariado al hablar de los mtodos del appletviewer, que es el visualizador de applets de Sun. Incluso para el applet ms sencillo necesitaremos varios mtodos. Son los que se usan para arrancar (start) y detener (stop) la ejecucin del applet, para pintar (paint) y actualizar (update) la pantalla y para capturar la informacin que se pasa al applet desde el fichero HTML a travs de la marca APPLET.

init()
Esta funcin miembro es llamada al crearse el applet. Es llamada slo una vez. La clase Applet no hace nada en init(). Las clases derivadas deben sobrecargar este mtodo para cambiar el tamao durante su inicializacin, y cualquier otra inicializacin de los datos que solamente deba realizarse una vez. Deberan realizarse al menos las siguientes acciones: Por ejemplo:
public void init() { if( width < 200 || height < 200 ) resize( 200,200 );

Carga de imgenes y sonido El resize del applet para que tenga su tamao correcto Asignacin de valores a las variables globales

80

Tutorial de Java

valor_global1 = 0; valor_global2 = 100; // cargaremos imgenes en memoria sin mostrarlas // cargaremos msica de fondo en memoria sin reproducirla }

destroy()
Esta funcin miembro es llamada cuando el applet no se va a usar ms. La clase Applet no hace nada en este mtodo. Las clases derivadas deberan sobrecargarlo para hacer una limpieza final. Los applet multithread debern usar destroy() para matar cuanquier thread del applet que quedase activo.

start()
Llamada para activar el applet. Esta funcin miembro es llamada cuando se visita el applet. La clase Applet no hace nada en este mtodo. Las clases derivadas deberan sobrecargarlo para comenzar una animacin, sonido, etc.
public void start() { estaDetenido = false; // comenzar la reproduccin de la msica musicClip.play(); }

Tambin se puede utilizar start() para eliminar cualquier thread que se necesite.

stop()
Llamada para detener el applet. Se llama cuando el applet desaparece de la pantalla. La clase Applet no hace nada en este mtodo. Las clases derivadas deberan sobrecargarlo para detener la animacin, el sonido, etc.
public void stop() { estaDetenido = true; if( /* se est reproduciendo msica? */ ) musicClip.stop(); }

resize( int width,int height )


El mtodo init() debera llamar a esta funcin miembro para establecer el tamao del applet. Puede utilizar las variables ancho y alto, pero no es necesario. Cambiar el tamao en otro sitio que no sea init() produce un reformateo de todo el documento y no se recomienda.

Tutorial de Java

81

En el navegador Netscape, el tamao del applet es el que se indica en la marca APPLET del HTML, no hace caso a lo que se indique desde el cdigo Java del applet.

width
Variable entera, su valor es el ancho definido en el parmetro WIDTH de la marca HTML del APPLET. Por defecto es el ancho del icono.

height
Variable entera, su valor es la altura definida en el parmetro HEIGHT de la marca HTML del APPLET. Por defecto es la altura del icono. Tanto width como height estn siempre disponibles para que se puede chequear el tamao del applet. Podemos retomar el ejemplo de init():
public void init() { if( width < 200 || height < 200 ) resize( 200,200 ); ...

paint( Graphics g )
Se llama cada vez que se necesita refrescar el rea de dibujo del applet. La clase Applet simplemente dibuja una caja con sombreado de tres dimensiones en el rea. Obviamente, la clase derivada debera sobrecargar este mtodo para representar algo inteligente en la pantalla. Para repintar toda la pantalla cuando llega un evento Paint, se pide el rectngulo sobre el que se va a aplicar paint() y si es ms pequeo que el tamao real del applet se invoca a repaint(), que como va a hacer un update(), se actualizar toda la pantalla. Podemos utilizar paint() para imprimir nuestro mensaje de bienvenida:
void public paint( Graphics g ) { g.drawString( Hola Java!,25,25 ); // Dibujaremos la imgenes que necesitemos }

update( Graphics g )
Esta es la funcin que se llama realmente cuando se necesita actualizar la pantalla. La clase Applet simplemente limpia el rea y llama al mtodo paint(). Esta funcionalidad es suficiente en la mayora de los casos. De cualquier forma, las clases derivadas pueden sustituir esta funcionalidad para sus propsitos.

82

Tutorial de Java

Podemos, por ejemplo, utilizar update() para modificar selectivamente partes del rea grfica sin tener que pintar el rea completa:
public void update( Graphics g ) { if( estaActualizado ) { g.clear(); // garantiza la pantalla limpia repaint(); // podemos usar el mtodo padre: super.update() } else // Informacin adicional g.drawString( Otra informacin,25,50 ); }

repaint()
A esta funcin se la debera llamar cuando el applet necesite ser repintado. No debera sobrecargarse, sino dejar que Java repinte completamente el contenido del applet. Al llamar a repaint(), sin parmetros, internamente se llama a update() que borrar el rectngulo sobre el que se redibujar y luego se llama a paint(). Como a repaint() se le pueden pasar parmetros, se puede modificar el rectngulo a repintar.

getParameter( String attr )


Este mtodo carga los valores parados al applet va la marca APPLET de HTML. El argumento String es el nombre del parmetro que se quiere obtener. Devuelve el valor que se le haya asignado al parmetro; en caso de que no se le haya asignado ninguno, devolver null. Para usar getParameter(), se define una cadena genrica. Una vez que se ha capturado el parmetro, se utilizan mtodos de cadena o de nmeros para convertir el valor obtenido al tipo adecuado.
public void init() { String pv; pv = getParameter( velocidad ); if( pv == null ) velocidad = 10; else velocidad = Integer.parseInt( pv ); }

getDocumentBase()
Indica la ruta http, o el directorio del disco, de donde se ha recogido la pgina HTML que contiene el applet, es decir, el lugar donde est la hoja en todo Internet o en el disco.

Tutorial de Java

83

getCodeBase()
Indica la ruta http, o el directorio del disco, de donde se ha cargado el cdigo bytecode que forma el applet, es decir, el lugar donde est el fichero .class en todo Internet o en el disco.

print( Graphics g )
Para imprimir en impresora, al igual que paint() se puede utilizar print(), que pintar en la impresora el mapa de bits del dibujo.

La aplicacin Fecha (Aproximacin a OOP)


Veamos ahora una aplicacin un poco ms til que HolaMundo, presentaremos en pantalla la fecha y hora del sistema. Aprovecharemos tambin para realizar un introduccin muy sencilla a los conceptos fundamentales de la programacin orientada a objetos, clases y objetos, a travs de esta simple aplicacin.
import java.util.Date; class FechaApp { public static void main( String args[] ) { Date hoy = new Date(); System.out.println( hoy ); } }

Esta aplicacin es una versin modificada de HolaMundoApp de la que difiere porque se importa la clase Date, la aplicacin se llama ahora FechaApp en vez de HolaMundoApp, se crea un objeto Date y el mensaje de salida a pantalla es diferente. Almacenaremos esta nueva aplicacin en el fichero FechaApp.java. La lnea de cdigo:
class FechaApp {

es el inicio del bloque de la declaracin de nuestra clase. Ya hemos dicho que todas las funciones y variables en Java, existen dentro de una clase o un objeto, Java no soporta funciones o variables globales. Por tanto, la declaracin de la clase se convierte en el esqueleto de cualquier aplicacin Java. La clase, el bloque bsico de un lenguaje orientado a objetos como Java, es la plantilla que usamos para describir los datos y el entorno asociado a las instancias de esa clase. Cuando se instancia una clase, se crea un objeto del tipo definido por la clase y exactamente igual que cualquier otra instancia realizada de la misma clase. Los datos asociados a la clase u objeto son las variables y el entorno asociado con la clase u objeto son los mtodos. Un ejemplo de clase es la clase que representa un rectngulo. Esta clase contiene las variables que indican las coordenadas del origen del rectngulo y su ancho y alto. La clase

84

Tutorial de Java

puede contener un mtodo para calcular el rea de ese rectngulo. Ahora podemos instanciarlo para muy diferentes propsitos, es decir, podemos tener objetos rectngulo especficos, as podremos obtener informacin de las dimensiones de nuestro dormitorio o de las dimensiones de la ventana en donde se est visualizando esta pgina.
class NombreDeLaClase { . . . }

Esta es la forma general de definicin de una clase en Java, donde la palabra clave class inicia la definicin de la clase NombreDeLaClase. Las variables y mtodos de la clase han de ir colocados entre las llaves que delimitan el bloque de definicin de la clase. FechaApp no tiene variables y solamente tiene un mtodo llamado main(). Este mtodo, main(), es el cerebro de cualquier aplicacin Java. Cuando se ejecuta una aplicacin Java utilizando el intrprete Java, se debe especificar el nombre de la clase que se desea ejecutar. El intrprete entonces, invoca al mtodo main() definido dentro de esa clase, que debe controlar el flujo del programa, pedir al sistema los recursos que necesite y ejecutar cualquier otro mtodo necesario para completar la funcionalidad de la aplicacin. La definicin del mtodo main() debe estar precedida por tres modificadores: public indica que el mtodo main() puede ser llamado por cualquier objeto static indica que el mtodo main() es un mtodo esttico, es decir, un mtodo propio de la clase void indica que el mtodo main() no devolver ningn valor El mtodo main() en Java es similar a la funcin main() de C y C++. Cuando se ejecuta un programa escrito en C o C++, arranca llamando en primer lugar a la funcin main(), que llamar a las dems funciones necesarias en la ejecucin del programa. De forma similar, en el lenguaje Java, cuando se ejecuta una clase con el intrprete Java, el sistema comienza llamando al mtodo main() de la clase, que llamar a los dems mtodos necesarios para completar la ejecucin de la aplicacin. Si se intenta ejecutar una clase con el intrprete Java que no contenga el mtodo main(), el intrprete generar un mensaje de error. El mtodo main() acepta como argumento un array de Strings:
public static void main( Strings args[] ) {

Este array de Strings es el mecanismo a travs del cual el sistema puede pasar informacin a la aplicacin. Cada una de las cadenas String es un argumento de lnea de comandos. Permiten pasar informacin a la aplicacin, para variar su ejecucin, sin necesidad de recompilarla. Por ejemplo, si desarrollamos una aplicacin de ordenacin, podramos permitir al usuario seleccionar el mtodo, ascendente o descendente, en la lnea de comandos de ejecucin de la aplicacin.
-descendente

Nuestra aplicacin FechaApp ignora los argumentos de la lnea de comandos, as que no nos extendemos ms, pero volveremos sobre ello ms adelante. No obstante, los programadores de C y C++ deben tener en cuenta que en Java el nmero y tipo de argumentos de la lnea de comandos es diferente a los que se pasan a la funcin main() en C y C++. La aplicacin FechaApp es el programa ms simple que podemos hacer que realice algo

Tutorial de Java

85

interesante, pero por su misma sencillez no necesita ninguna clase adicional. Sin embargo, la mayora de los programas que escribamos sern ms complejos y necesitarn que escribamos otras clases y utilizar las que nos proporciona Java como soporte. Nuestra aplicacin FechaApp utiliza dos clases, la clase System y la clase Date, que nos proporciona el entorno de desarrollo de Java. La clase System proporciona un acceso al sistema independiente del hardware sobre el que estemos ejecutando la aplicacin y la clase Date proporciona un acceso a las funciones de Fecha independientemente del sistema en que estemos ejecutando la aplicacin.

86

Tutorial de Java

Captulo 5
Depurar HolaMundo

El depurador de Java - JDB

El depurador de Java, jdb es un depurador de lnea de comandos, similar al que Sun proporciona en sus Sistemas, dbx. Es complicado de utilizar y un tanto crptico, por lo que, en principio, tiene escasa practicidad y es necesaria una verdadera emergencia para tener que recurrir a l. Trataremos por encima los comandos que proporciona el jdb, pero sin entrar en detalles de su funcionamiento, porque no merece la pena. Casi es mejor esperar a disponer de herramientas visuales para poder depurar con cierta comodidad nuestro cdigo Java. Para poder utilizar el depurador, las aplicaciones Java deben estar compiladas con la opcin de depuracin activada, -g. Posteriormente se puede lanzar appletviewer con la opcin de depuracin, debug, y habremos puesto en marcha jdb.

Hemos modificado nuestro applet de ejemplo para utilizarlo en nuestra sesin de ejemplo con el depurador. Se compilara con el comando:
%javac -g hm.java

y el contenido de nuestro applet HolaMundo modificado y guardado en el fichero hm.java sera el siguiente:
// // Applet HolaMundo de ejemplo, para depurar // import java.awt.Graphics; import java.applet.Applet; public class hm extends Applet { int i; public void paint( Graphics g ) { i = 10; g.drawString( Hola Mundo!,25,25 ); } }

Una vez compilado, iniciamos la sesin lanzando el visor de applets de Sun con la opcin de depuracin, utilizando el comando:

Tutorial de Java

87

%appletviewer -debug hm.html

El fichero hm.html contiene las lneas mnimas para poder activar el applet, estas lneas son las que reproducimos:
<html> <applet code=hm.class width=100 height=100> </applet> </html>

Se inicia pues la sesin con el depurador y vamos a ir reproduciendo lo que aparece en la pantalla a medida que vamos introduciendo comandos:
%appletviewer -debug hm.html Loading jdb... 0xee301bf0:class(sun.applet.AppletViewer) >

Comando help
El comando help proporciona una lista de los comandos que estn disponibles en la sesin de jdb. Esta lista es la que sigue, en donde hemos aprovechado la presencia de todos los comandos para comentar la accin que cada uno de ellos lleva a cabo.
>help ** command list ** threads [threadgroup] lista threads thread <thread id> establece el thread por defecto suspend [thread id(s)] suspende threads (por defecto, todos) resume [thread id(s)] contina threads (por defecto, todos) where [thread id]|all muestra la pila de un thread threadgroups lista los grupos de threads threadgroup <name> establece el grupo de thread actual print <id> [id(s)] imprime un objeto o campo dump <id> [id(s)] imprime toda la informacin locals

del objeto

imprime las variables locales de la pila actual

classes lista las clases conocidas methods <class id> lista los mtodos

de una clase

stop in <class id>.<method> fija un punto de ruptura en un mtodo stop at <class id>:<line> establece un punto de ruptura en una lnea up [n frames] ascender en la pila de threads down [n frames] descender en la pila de threads clear <class id>:<line> eliminar un punto de ruptura step ejecutar la lnea actual cont continuar la ejecucin desde el punto de ruptura

88

Tutorial de Java

catch <class id> parar por la excepcin especificada ignore <class id> ignorar la excepcin especificada list [line number] imprimir cdigo fuente use [source file path] ver o cambiar la ruta del fichero fuente memory informe del uso de la memoria load <classname> - carga la clase Java a ser depurada run <args> - comienza la ejecucin de la clase cargada !! - repite el ltimo comando help (or ?) - lista los comandos exit (or quit) - salir del depurador >

Comando threadgroups
El comando threadgroups permite ver la lista de threads que se estn ejecutando. Los grupos system y main deberan estar siempre corriendo.
>threadgroups 1.(java.lang.ThreadGroup)0xee300068 system 2.(java.lang.ThreadGroup)0xee300a98 main >

Comando threads
El comando threads se utiliza para ver la lista completa de los threads que se estn ejecutando actualmente.
>threads Group system: 1.(java.lang.Thread)0xee300098 clock handler cond 2.(java.lang.Thread)0xee300558 Idle thread run 3.(java.lang.Thread)0xee3005d0 sync Garbage Collector cond 4.(java.lang.Thread)0xee300620 Finalizer thread cond 5.(java.lang.Thread)0xee300a20 Debugger agent run 6.(java.tools.debug.BreakpointHandler)0xee300b58) Breakpoint handler cond Group main: 7.(java.lang.Thread)0xee300048 main suspended >

Comando run
El comando run es el que se utiliza para arrancar el appletviewer en la sesin de depuracin. Lo teclearemos y luego volveremos a listar los threads que hay en ejecucin.
>run run sun.applet.AppletViewer hm.html running...

Tutorial de Java

89

main[1]threads threads Group sun.applet.AppletViewer.main: 1.(java.lang.Thread)0xee3000c0 AWT-Motif running 2.(sun.awt.ScreenUpdater)0xee302ed0 ScreenUpdater cond. Waiting Group applet-hm.class: 3.(java.lang.Thread)0xee302f38 Thread-6 cond. Waiting main[1]

El visor de applets de Sun aparecer en la pantalla y mostrar el conocido mensaje de saludo al Mundo. Ahora vamos a rearrancar el appletviewer con un punto de ruptura, para detener la ejecucin del applet, y podamos seguir mostrando los comandos disponibles en el jdb.
main[1]exit %appletviewer -debug hm.html Loading jdb... 0xee3009c8:class(sun.applet.AppletViewer) >stop in hm.paint Breakpoint set in hm.paint >run run sun.applet.AppletViewer hm.html running... Breakpoint hit: hm.paint(hm.java:9) AWT-Motif[1]

Comando where
El comando where mostrar la pila de ejecucin del applet.
AWT-Motif[1]where [1]hm.paint(hm.java:9) [2]sun.awt.motif.MComponentPeer.paint(MComponenetPeer.java:109) [3]sun.awt.motif.MComponentPeer.handleExpose(MComponenetPeer.java:170) AWT-Motif[1]

Comando use
El comando use nos informa del camino donde jdb va a buscar los ficheros fuentes que contienen el cdigo Java de las clases que se estn depurando. Por defecto, utilizar el camino que se especifique en la variable de entorno CLASSPATH.
AWT-Motif[1]use /usr/local/java/classes: AWT-Motif[1]

Comando list
El comando list mostrar el cdigo fuente actual al comienzo del punto de ruptura que hayamos fijado.
AWT-Motif[1]list

90

Tutorial de Java

9 public void paint( Graphics g ) { 10 => i = 10; 11 g.drawString( Hola Mundo!,25,25 ) ; 12 } 13 } AWT-Motif[1]

Comando dump
El comando dump nos permitir ahora ver el valor del objeto g pasado desde el appletviewer.
AWT-Motif[1]dump g g = (sun.awt.motif.X11Graphics)0xee303df8 { int pData = 1342480 Color foreground = (java.awt.Color)0xee302378 Font font = (java.awt.Font)0xee302138 int originX = 0 int originY = 0 float scaleX = 1 float scaleY = 1 Image image = null } AWT-Motif[1]

Comando step
El comando step nos porporciona el mtodo para ejecutar la lnea actual, que estar siendo apuntada por el indicador si hemos utilizado el comando list. AWT-Motif[1]step Breakpoint hit: hm.paint(hm.java:11) AWT-Motif[1]list 9 public void paint( Graphics g ) { 10 i = 10; 11 => g.drawString( Hola Mundo!,25,25 ); 12 } 13 } AWT-Motif[1]

Tutorial de Java

91

92

Tutorial de Java

Captulo 6
La clase Math

Clases Java

En cualquier lenguaje orientado a objetos, las clases definen cualquier objeto que se pueda manipular. Java tiene muchas clases tiles, no solamente aquellas que se utilizan para grficos y sonido, usadas en la construccin de applets mucho ms complejos.

La clase Math representa la librera matemtica de Java. Las funciones que contiene son las de todos los lenguajes, parece que se han metido en una clase solamente a propsito de agrupacin, por eso se encapsulan en Math, y lo mismo sucede con las dems clases que corresponden a objetos que tienen un tipo equivalente (Character, Float, etc.). El constructor de la clase es privado, por los que no se pueden crear instancias de la clase. Sin embargo, Math es public para que se pueda llamar desde cualquier sitio y static para que no haya que inicializarla.

Funciones matemticas
Si se importa la clase, se tiene acceso al conjunto de funciones matemticas estndar:
Math.abs( x ) para int, long, Math.sin( double ) Math.cos( double ) Math.tan( double ) Math.asin( double ) Math.acos( double ) Math.atan( double ) Math.atan2( double,double ) Math.exp( double ) Math.log( double ) Math.sqrt( double ) Math.ceil( double ) Math.floor( double ) Math.rint( double ) Math.pow( a,b ) Math.round( x ) para double

float y double

y float

Tutorial de Java

93

Math.random() devuelve un double Math.max( a,b ) para int, long, float y double Math.min( a,b ) para int, long, float y double Math.E para la base exponencial Math.PI para PI

He aqu un ejemplo, Mates.java, de uso de algunas funciones de la clase Math:


class Mates { public static void main( String args[] ) { int x; double rand,y,z; float max; rand = Math.random(); x = Math.abs( -123 ); y = Math.round( 123.567 ); z = Math.pow( 2,4 ); max = Math.max( (float)1e10,(float)3e9 ); System.out.println( rand ); System.out.println( x ); System.out.println( y ); System.out.println( z ); System.out.println( max ); } }

La clase Character
Al trabajar con caracteres se necesitan muchas funciones de comprobacin y traslacin. Estas funciones estn empleadas en la clase Character. De esta clase s que se pueden crear instancias, al contrario que sucede con la clase Math.

Declaraciones
La primera sentencia crear una variable carcter y la segunda un objeto Character:
char c; Character C;

Comprobaciones booleanas
Character.isLowerCase( c ) Character.isUpperCase( c ) Character.isDigit( c ) Character.isSpace( c )

En este caso, si tuvisemos un objeto Character C, no se podra hacer C.isLowerCase, porque no se ha hecho un new de Character. Estas funciones son estticas y no conocen al objeto, por eso hay que crealo antes.

94

Tutorial de Java

Traslaciones de caracteres
char c2 = Character.toLowerCase( c ); char c2 = Character.toUpperCase( c );

Traslaciones de carcter/dgito
int i = Character.digit( c,base ); char c = Character.forDigit( i,base );

Mtodos de la clase Character


C = new Character( J ); char c = C.charValue(); String s = C.toString();

La clase Float
Cada tipo numrico tiene su propia clase de objetos. As el tipo float tiene el objeto Float. De la misma forma que con la clase Character, se han codificado muchas funciones tiles dentro de los mtodos de la clase Float.

Declaraciones
La primera sentencia crear una variable float y la segunda un objeto Float:
float f; Float F;

Valores de Float
Float.POSITIVE_INFINITY Float.NEGATIVE_INFINITY Float.NaN Float.MAX_VALUE Float.MIN_VALUE

Conversiones de Clase/Cadena
String s = Float.toString( f ); f = Float.valueOf( 3.14 );

Tutorial de Java

95

Comprobaciones
boolean b = Float.isNaN( f ); boolean b = Float.isInfinite( f );

La funcin isNaN() comprueba si f es un No-Nmero. Un ejemplo de no-nmero es raiz cuadrada de -2.

Conversiones de Objetos
Float F = new Float( Float.PI ); String s = F.toString(); int i = F.intValue(); long l = F.longValue(); float F = F.floatValue(); double d = F.doubleValue();

Otros Mtodos
int i = F.hashCode(); boolean b = F.equals( Object obj ); int i = Float.floatToIntBits( f ); float f = Float.intBitsToFloat( i );

La clase Double
Cada tipo numrico tiene su propia clase de objetos. As el tipo double tiene el objeto Double. De la misma forma que con la clase Character, se han codificado muchas funciones tiles dentro de los mtodos de la clase Double.

Declaraciones
La primera sentencia crear una variable double y la segunda un objeto Double:
double d; Double D;

Valores de Double
Double.POSITIVE_INFINITY Double.NEGATIVE_INFINITY Double.NaN Double.MAX_VALUE Double.MIN_VALUE

96

Tutorial de Java

Mtodos de Double
D.isNaN(); Double.isNaN( d ); D.isInfinite(); Double.isInfinite( d ); boolean D.equals(); String D.toString(); int D.intValue(); long D.longValue(); float D.floatValue(); double D.doubleValue(); int i = D.hashCode(); Double V.valueOf( String s ); long l = Double.doubleToLongBits( d ); double d = Double.longBitsToDouble( l );

La clase Integer
Cada tipo numrico tiene su propia clase de objetos. As el tipo int tiene el objeto Integer. De la misma forma que con la clase Character, se han codificado muchas funciones tiles dentro de los mtodos de la clase Integer.

Declaraciones
La primera sentencia crear una variable int y la segunda un objeto Integer:
int i; Integer I;

Valores de Integer
Integer.MIN_VALUE; Integer.MAX_VALUE;

Mtodos de Integer
String Integer.toString( int i,int base ); String Integer.toString( int i ); int I.parseInt( String s,int base ); int I.parseInt( String s ); Integer Integer.valueOf( String s,int base ); Integer Integer.valueOf( String s ); int I.intValue(); long I.longValue(); float I.floatValue(); double I.doubleValue(); String I.toString(); int I.hashCode();

Tutorial de Java

97

boolean I.equals( Object obj );

En los mtodos toString(), parseInt() y valueOf() que no se especifica la base sobre la que se trabaja, se asume que es base 10.

La clase Long
Cada tipo numrico tiene su propia clase de objetos. As el tipo long tiene el objeto Long. De la misma forma que con la clase Character, se han codificado muchas funciones tiles dentro de los mtodos de la clase Long.

Declaraciones
La primera sentencia crear una variable long y la segunda un objeto Long:
long l; Long L;

Valores de Long
Long.MIN_VALUE; Long.MAX_VALUE;

Mtodos de Long
String Long.toString( long l,int base ); String Long.toString( long l ); long L.parseLong( String s,int base ); long L.parseLong( String s ); Long Long.valueOf( String s,int base ); Long Long.valueOf( String s ); int L.intValue(); long L.longValue(); float L.floatValue(); double L.doubleValue(); String L.toString(); int L.hashCode(); boolean L.equals( Object obj );

En los mtodos toString(), parseInt() y valueOf() que no se especifica la base sobre la que se trabaja, se asume que es base 10.

La clase Boolean
Los valores boolean tambin tienen su tipo asociado Boolean, aunque en este caso hay menos mtodos implementados que para el resto de las clases numricas.

98

Tutorial de Java

Declaraciones
La primera sentencia crear una variable boolean y la segunda un objeto Boolean:
boolean b; Boolean B;

Valores de Boolean
Boolean.TRUE; Boolean.FALSE;

Mtodos de Boolean
boolean B.booleanValue(); String B.toString(); boolean B.equals( Object obj );

La clase String
Java posee gran capacidad para el manejo de cadenas dentro de sus clases String y StringBuffer. Un objeto String representa una cadena alfanumrica de un valor constante que no puede ser cambiada despus de haber sido creada. Un objeto StringBuffer representa una cadena cuyo tamao puede variar. Los Strings son objetos constantes y por lo tanto muy baratos para el sistema. La mayora de las funciones relacionadas con cadenas esperan valores String como argumentos y devuelven valores String. Hay que tener en cuenta que las funciones estticas no consumen memoria del objeto, con lo cual es ms conveniente usar Character que char. No obstante, char se usa, por ejemplo, para leer ficheros que estn escritos desde otro lenguaje. Existen muchos constructores para crear nuevas cadenas:
String(); String( String str ); String( char val[] ); String( char val[],int offset,int count ); String( byte val[],int hibyte ); String( byte val[],int hibyte,int offset,int count );

Tal como uno puede imaginarse, las cadenas pueden ser muy complejas, existiendo muchas funciones muy tiles para trabajar con ellas y, afortunadamente, la mayora estn codificadas en la clase String.

Tutorial de Java

99

Funciones Bsicas
La primera devuelve la longitud de la cadena y la segunda devuelve el carcter que se encuentra en la posicin que se indica en indice:
int length(); char charAt( int indice );

Funciones de Comparacin de Strings

boolean equals( Object obj ); boolean equalsIgnoreCase( Object obj );

Lo mismo que equals() pero no tiene en cuenta maysculas o minsculas.


int compareTo( String str2 );

Devuelve un entero menor que cero si la cadena es lxicamente menor que str2. Devuelve cero si las dos cadenas son lxicamente iguales y un entero mayor que cero si la cadena es lxicamente mayor que str2.

Funciones de Comparacin de Subcadenas


boolean regionMatch( int thisoffset,String s2,int s2offset,int len ); boolean regionMatch( boolean ignoreCase,int thisoffset,String s2, int s2offset,int 1 );

Comprueba si una regin de esta cadena es igual a una regin de otra cadena.
boolean startsWith( String prefix ); boolean startsWith( String prefix,int offset ); boolean endsWith( String suffix );

Devuelve si esta cadena comienza o termina con un cierto prefijo o sufijo comenzando en un determinado desplazamiento.
int indexOf( int ch ); int indexOf( int ch,int fromindex ); int lastIndexOf( int ch ); int lastIndexOf( int ch,int fromindex ); int indexOf( String str ); int indexOf( String str,int fromindex ); int lastIndexOf( String str ); int lastIndexOf( String str,int fromindex );

Devuelve el primer/ltimo ndice de un carcter/cadena empezando la bsqueda a partir de un determinado desplazamiento.


String substring( int beginindex ); String substring( int beginindex,int endindex ); String concat( String str ); String replace( char oldchar,char newchar );

100

Tutorial de Java

String toLowerCase(); String toUpperCase(); String trim();

Ajusta los espacios en blanco al comienzo y al final de la cadena.


void getChars( int srcBegin,int srcEnd,char dst[],int dstBegin ); void getBytes( int srcBegin,int srcEnd,byte dst[],int dstBegin ); String toString(); char toCharArray(); int hashCode();

Funciones ValueOf
La clase String posee numerosas funciones para transformar valores de otros tipos de datos a su representacin como cadena. Todas estas funciones tienen el nombre de valueOf, estando el mtodo sobrecargado para todos los tipos de datos bsicos. Veamos un ejemplo de su utilizacin:
String Uno = new String( Hola Mundo ); float f = 3.141592; String PI = Uno.valueOf( f ); String PI = String.valueOf( f );

// Mucho ms correcto

Funciones de Conversin

String valueOf( boolean b ); String valueOf( int i ); String valueOf( long l ); String valueOf( float f ); String valueOf( double d ); String valueOf( Object obj ); String valueOf( char data[] ); String valueOf( char data[],int offset,int count ); Usa arrays de caracteres para la cadena. String copyValueOf( char data[] ); String copyValueOf( char data[],int offset,int count ); Crea un nuevo array equivalente para la cadena.

La clase StringBuffer
Java posee gran capacidad para el manejo de cadenas dentro de sus clases String y StringBuffer. Un objeto String representa una cadena alfanumrica de un valor constante que no puede ser cambiada despus de haber sido creada. Un objeto StringBuffer representa una cadena cuyo tamao puede variar. La clase StringBuffer dispone de muchos mtodos para modificar el contenido de los obje-

Tutorial de Java

101

tos StringBuffer. Si el contenido de una cadena va a ser modificado en un programa, habr que sacrificar el uso de objetos String en beneficio de StringBuffer, que aunque consumen ms recursos del sistema, permiten ese tipo de manipulaciones. Al estar la mayora de las caractersticas de los StringBuffers basadas en su tamao variable, se necesita un nuevo mtodo de creacin:
StringBuffer(); StringBuffer( int len ); StringBuffer( String str );

Se puede crear un StringBuffer vaco de cualquier longitud y tambin se puede utilizar un String como punto de partida para un StringBuffer.
StringBuffer Dos = new StringBuffer( 20 ); StringBuffer Uno = new StringBuffer( Hola Mundo );

Cambio de Tamao
El cambio de tamao de un StringBuffer necesita varias funciones especficas para manipular el tamao de las cadenas:
int length(); char charAt( int index ); void getChars( int srcBegin,int srcEnd,char dst[],int dstBegin ); String toString(); void setLength( int newlength ); void setCharAt( int index,char ch ); int capacity(); void ensureCapacity( int minimum ); void copyWhenShared();

Obervar que una de las funciones devuelve una cadena constante normal de tipo String. Este objeto se puede usar con cualquier funcin String, como por ejemplo, en las funciones de comparacin.

Modificacin del Contenido


Para cambiar el contenido de un StringBuffer, se pueden utilizar dos mtodos: append() e insert(). En el ejemplo CadAppend.java, vemos el uso de estos dos mtodos:
class CadAppend { public static void main( String args[] ) { StringBuffer str = new StringBuffer( Hola ); str.append( Mundo ); System.out.println( str ); } }

En este otro ejemplo, CadInversa.java, mostramos un mtodo muy simple que le da la vuelta a una cadena:

102

Tutorial de Java

class CadInversa { public static String cadenaInversa( String fuente ) { int longitud = fuente.length(); StringBuffer destino = new StringBuffer( longitud ); for( int i=(longitud-1); i >= 0; i ) destino.append( fuente.charAt( i ) ); return( destino.toString() ); } public static void main( String args[] ) { System.out.println( cadenaInversa( Hola Mundo ) ); } }

Las funciones que cambian el tamao son pues:


StringBuffer append( Object obj ); StringBuffer append( String str ); StringBuffer append( char str[] ); StringBuffer append( char str[],int offset,int len ); StringBuffer append( boolean b ); StringBuffer append( int i ); StringBuffer append( long l ); StringBuffer append( float f ); StringBuffer append( double d ); StringBuffer append( char ch ); StringBuffer insert( int offset,Object obj ); StringBuffer insert( int offset,String str ); StringBuffer insert( int offset,char str[] ); StringBuffer insert( int offset,boolean b ); StringBuffer insert( int offset,int i ); StringBuffer insert( int offset,long l ); StringBuffer insert( int offset,float f ); StringBuffer insert( int offset,double d ); StringBuffer insert( int offset,char ch );

Operadores de Concatenacin
Hay que recordar que los operadores + y += tambin se pueden aplicar a cadenas. Ambos realizan una concatenacin y estn implementados con objetos StringBuffer. Por ejemplo, la sentencia:
String s = Qu + tal ?;

es interpretada por el compilador como:


String s = new StringBuffer().append( Qu ).append( tal ? ).toString();

y se marcara el StringBuffer para borrarlo ya que el contenido pasa al objeto String. Tambin, la sentencia:
s += por ah!;

sera interpretada por el sistema como:

Tutorial de Java

103

String s = new StringBuffer().append( s ).append( por ah! ).toString();

y volvera a marcar para borrar el nuevo StringBuffer.

Uso de conversiones
Veamos un ejemplo de utilidad de estas funciones. En el applet Conversion.java, que se muestra en el cdigo que sigue, se usan estas funciones para producir una salida til en un programa, presentando las coordenadas en las que se ha hecho click con el botn del ratn.
public class Conversion extends Applet { int RatonX = 25; int RatonY = 25; String Status = Haz click con el ratn; public void paint( Graphics g ) { g.drawString( Status,RatonX,RatonY ); } public boolean mouseDown( Event evt,int x,int y ) { Integer X = new Integer( x ); Integer Y = new Integer( y ); RatonX = x; RatonY = y; Status = X.toString()+,+Y.toString(); repaint(); return true; } }

104

Tutorial de Java

Captulo 7
Introduccin al AWT

Abstract Window Toolkit (AWT)

AWT es el acrnimo del X Window Toolkit para Java, donde X puede ser cualquier cosa: Abstract, Alternative, Awkward, Another o Asqueroso; aunque parece que Sun se decanta por Abstracto, seriedad por encima de todo. Se trata de una biblioteca de clases Java para el desarrollo de Interfaces de Usuario Grficas. La versin del AWT que Sun proporciona con el JDK se desarroll en slo dos meses y es la parte ms dbil de todo lo que representa Java como lenguaje. El entorno que ofrece es demasiado simple, no se han tenido en cuenta las ideas de entornos grficos novedosos, sino que se ha ahondado en estructuras orientadas a eventos, llenas de callbacks y sin soporte alguno del entorno para la construccin grfica; veremos que la simple accin de colocar un dibujo sobre un botn se vuelve una tarea harto complicada. Quiz la presin de tener que lanzar algo al mercado haya tenido mucho que ver en la pobreza de AWT.
JavaSoft, asegura que esto slo era el principio y que AWT ser multi-idioma, tendr herramientas visuales, etc. En fin, al igual que dicen los astrlogos, el futuro nos deparar muchas sorpresas. La estructura bsica del AWT se basa en Componentes y Contenedores. Estos ltimos contienen Componentes posicionados a su respecto y son Componentes a su vez, de forma que los eventos pueden tratarse tanto en Contenedores como en Componentes, corriendo por cuenta del programador (todava no hay herramientas de composicin visual) el encaje de todas las piezas, as como la seguridad de tratamiento de los eventos adecuados. Nada trivial. No obstante y pese a ello, vamos a abordar en este momento la programacin con el AWT para tener la base suficiente y poder seguir profundizando en las dems caractersticas del lenguaje Java, porque como vamos a ir presentando ejemplos grficos es imprescindible el conocimiento del AWT. Mientras tanto, esperemos que JavaSoft sea fiel a sus predicciones y lo que ahora veamos nos sirva de base para migrar a un nuevo y maravilloso AWT.

Interface de usuario
La interface de usuario es la parte del programa que permite a ste interactuar con el usuario. Las interfaces de usuario pueden adoptar muchas formas, que van desde la simple lnea de comandos hasta las interfaces grficas que proporcionan las aplicaciones ms modernas.

Tutorial de Java

105

La interface de usuario es el aspecto ms importante de cualquier aplicacin. Una aplicacin sin un interfaz fcil, impide que los usuarios saquen el mximo rendimiento del programa. Java proporciona los elementos bsicos para construir decentes interfaces de usuario a travs del AWT. Al nivel ms bajo, el sistema operativo transmite informacin desde el ratn y el teclado como dispositivos de entrada al programa. El AWT fue diseado pensando en que el programador no tuviese que preocuparse de detalles como controlar el movimiento del ratn o leer el teclado, ni tampoco atender a detalles como la escritura en pantalla. El AWT constituye una librera de clases orientada a objeto para cubrir estos recursos y servicios de bajo nivel. Debido a que el lenguaje de programacin Java es independiente de la plataforma en que se ejecuten sus aplicaciones, el AWT tambin es independiente de la plataforma en que se ejecute. El AWT proporciona un conjunto de herramientas para la construccin de interfaces grficas que tienen una apariencia y se comportan de forma semejante en todas las plataformas en que se ejecute. Los elementos de interface proporcionados por el AWT estn implementados utilizando toolkits nativos de las plataformas, preservando una apariencia semejante a todas las aplicaciones que se creen para esa plataforma. Este es un punto fuerte del AWT, pero tambin tiene la desventaja de que una interface grfica diseada para una plataforma, puede no visualizarse correctamente en otra diferente.

Estructura del AWT


La estructura de la versin actual del AWT podemos resumirla en los puntos que exponemos a continuacin: Los Contenedores contienen Componentes, que son los controles bsicos No se usan posiciones fijas de los Componentes, sino que estn situados a travs de una disposicin controlada (layouts) El comn denominador de ms bajo nivel se acerca al teclado, ratn y manejo de eventos Alto nivel de abstraccin respecto al entorno de ventanas en que se ejecute la aplicacin (no hay reas cliente, ni llamadas a X, ni hWnds, etc.) La arquitectura de la aplicacin es dependiente del entorno de ventanas, en vez de tener un tamao fijo Es bastante dependiente de la mquina en que se ejecuta la aplicacin (no puede asumir que un dilogo tendr el mismo tamao en cada mquina) Carece de un formato de recursos. No se puede separar el cdigo de lo que es propiamente interface. No hay ningn diseador de interfaces (todava)

Componentes y contenedores
Una interface grfica est construida en base a elementos grficos bsicos, los Componentes. Tpicos ejemplos de estos Componentes son los botones, barras de desplazamiento, etiquetas, listas, cajas de seleccin o campos de texto. Los Componentes permiten al usuario interactuar con la aplicacin y proporcionar informacin desde el programa al usuario

106

Tutorial de Java

sobre el estado del programa. En el AWT, todos los Componentes de la interface de usuario son instancias de la clase Component o uno de sus subtipos. Los Componentes no se encuentran aislados, sino agrupados dentro de Contenedores. Los Contenedores contienen y organizan la situacin de los Componentes; adems, los Contenedores son en s mismos Componentes y como tales pueden ser situados dentro de otros Contenedores. Tambin contienen el cdigo necesario para el control de eventos, cambiar la forma del cursor o modificar el icono de la aplicacin. En el AWT, todos los Contenedores son instancias de la clase Container o uno de sus subtipos. En la imagen siguiente presentamos una interface de usuario muy simple, con la apariencia que presenta cuando se visualiza bajo Windows 95.

Los Componentes deben circunscribirse dentro del Contenedor que los contiene. Esto hace que el anidamiento de Componentes (incluyendo Contenedores) en Contenedores crean rboles de elementos, comenzando con un Contenedor en la raiz del rbol y expandindolo en sus ramas. A continuacin presentamos el rbol que representa la interface que corresponde con la aplicacin grfica generada anteriormente.

Tutorial de Java

107

Tipos de componentes
En el rbol siguiente mostramos la relacin que existe entre todas las clases que proporciona AWT para la creacin de interfaces de usuario, presentando la jerarqua de Clases e Interfaces:

Clases:
BorderLayout CardLayout CheckboxGroup Color Component Button Canvas Checkbox Choice Container Panel Window Dialog Frame Label List Scrollbar TextComponent TextArea TextField Dimension Event FileDialog FlowLayout Font FontMetrics Graphics GridLayout GridBagConstraints GridBagLayout Image Insets MediaTracker MenuComponent MenuBar MenuItem CheckboxMenuItem Menu Point

108

Tutorial de Java

Polygon Rectangle Toolkit LayoutManager MenuContainer

Interfaces:

En la figura siguiente reproducimos la ventana generada por el cdigo de la aplicacin ComponentesAWT.java que muestra todos los Componentes que proporciona el AWT. Vamos a ver en detalle estos Componentes, pero aqu podemos observar ya la esttica que presentan en su conjunto. La ventana es necesaria porque el programa incluye un men, y los mens solamente pueden utilizarse en ventanas. El cdigo contiene un mtodo main() para poder ejecutarlo como una aplicacin independiente.

Componentes
Component es una clase abstracta que representa todo lo que tiene una posicin, un tamao, puede ser pintado en pantalla y puede recibir eventos. Los Objetos derivados de la clase Component que se incluyen en el Abstract Window Toolkit son los que aparecen a continuacin: Button Canvas Checkbox Choice Container Panel Window Dialog Frame Label List Scrollbar TextComponent

Tutorial de Java

109

TextArea TextField

Vamos a ver un poco ms en detalle los Componentes que nos proporciona el AWT para incorporar a la creacin de la interface con el usuario.

Botones
Veremos ejemplos de cmo se aaden botones a un panel para la interaccin del usuario con la aplicacin, pero antes vamos a ver la creacin de botones como objetos. Se pueden crear objetos Button con el operador new:
Button boton; boton = new Button( Botn);

La cadena utilizada en la creacin del botn aparecer en el botn cuando se visualice en pantalla. Esta cadena tambin se devolver para utilizarla como identificacin del botn cuando ocurra un evento.

Eventos Button
Cada vez que el usuario pulsa un botn, se produce un evento, de la misma forma que se produce un evento cuando se aprieta el botn del ratn. Los eventos de pulsacin de un botn se pueden capturar sobrecargando el mtodo action():
public boolean action( Event evt,Object obj ) { if( evt.target instanceof Button ) System.out.println( (String)obj ); else System.out.println( Evento No-Button ); }

La distincin entre todos los botones existentes se puede hacer utilizando el objeto destino pasado por el objeto Event y comparndolo con los objetos botn que hemos dispuesto en nuestro interface:
import java.awt.*; import java.applet.Applet; public class Botones extends Applet { Button b1,b2,b3; public void init() { b1 = new Button( Botn B1 ); b2 = new Button( Botn B2 ); b3 = new Button( Botn B3 ); this.add( b1 ); this.add( b2 ); this.add( b3 );

110

Tutorial de Java

} public boolean action( Event evt,Object obj ) { if( evt.target.equals( b1 ) ) System.out.println( Se ha pulsado el boton B1 ); if( evt.target.equals( b2 ) ) System.out.println( Se ha pulsado el boton B2 ); if( evt.target.equals( b3 ) ) System.out.println( Se ha pulsado el boton B3 ); return true; } }

En el applet anterior, Botones.java, observamos que se imprime el texto asociado al botn que hayamos pulsado.

Botones de Pulsacin
Los botones presentados en el applet son los botones de pulsacin estndar; no obstante, para variar la representacin en pantalla y para conseguir una interfaz ms limpia, AWT ofrece a los programadores otros tipos de botones.

Botones de Lista
Los botones de seleccin en una lista (Choice) permiten el rpido acceso a una lista de elementos. Por ejemplo, podramos implementar una seleccin de colores y mantenerla en un botn Choice:

import java.awt.*; import java.applet.Applet; public class BotonSeleccion extends Applet { Choice Selector; public void init() { Selector = new Choice(); Selector.addItem( Rojo ); Selector.addItem( Verde ); Selector.addItem( Azul );

Tutorial de Java

111

add( Selector ); } public boolean action( Event evt,Object obj ) { if( evt.target instanceof Choice ) { String color = (String)obj; System.out.println( El color elegido es el + color ); } return true; } }

En este ejemplo, BotonSeleccion.java, la cadena proporcionada al mtodo addItem() ser devuelta en el argumento Object de un evento Choice, por ello en el manejador del botn de seleccin, comprobamos en primer lugar que se trate efectivamente de un evento generado en un botn de tipo Choice.

Botones de Marcacin
Los botones de comprobacin (Checkbox) se utilizan frecuentemente como botones de estado. Proporcionan informacin del tipo S o No (true o false). El estado del botn se devuelve en el argumento Object de los eventos Checkbox; el argumento es de tipo booleano: verdadero (true) si la caja se ha seleccionado y falso (false) en otro caso. Tanto el nombre como el estado se devuelven en el argumento del evento, aunque se pueden obtener a travs de los mtodos getLabel() y getState() del objeto Checkbox.

import java.awt.*; import java.applet.Applet; public class BotonComprobacion extends Applet { Checkbox Relleno; public void init() { Relleno = new Checkbox( Relleno); add( Relleno ); } public boolean action( Event evt,Object obj ) { if( evt.target instanceof Checkbox ) System.out.println( CheckBox: + evt.arg.toString() );

112

Tutorial de Java

return true; } }

El sencillo ejemplo anterior, BotonComprobacion.java, muestra los cambios que se producen en el estado del botn cuando la caja est o no seleccionada.

Botones de Seleccin
Los botones de comprobacin se pueden agrupar para formar una interfaz de botn de radio (CheckboxGroup), que son agrupaciones de botones Checkbox en las que siempre hay un nico botn activo.

import java.awt.*; import java.applet.Applet; public class BotonRadio extends Applet { CheckboxGroup Radio; public void init() { Radio = new CheckboxGroup(); add( new Checkbox( Primero,Radio,true) ); add( new Checkbox( Segundo,Radio,false) ); add( new Checkbox( Tercero,Radio,false) ); } }

En el ejemplo anterior, BotonRadio.java, observamos que siempre hay un botn activo entre los que conforman el interfaz de comprobacin que se ha implementado.

Botones Autocontenidos
La naturaleza orientada a objetos de Java nos da la posibilidad de crear botones completamente autocontenidos. En este tipo de botn, se construye el manejador de eventos dentro de la propia clase extendida de botn. Se pueden aadir estos botones a la aplicacin, sin tener que preocuparse de los eventos que pudieran generar. En el ejemplo siguiente, BotonAuto.java, creamos el botn que muestra la figura, un botn que genera el texto Boton Aceptar por la salida estndar:

Tutorial de Java

113

import java.awt.*; import java.applet.Applet; class BotonAceptar extends Button { public BotonAceptar() { setLabel( Aceptar ); } public boolean action( Event evt,Object obj ) { System.out.println( Boton Aceptar ); return true; } } public class BotonAuto extends Applet { BotonAceptar boton; public void init() { boton = new BotonAceptar(); add( boton ); } }

Es de hacer notar que no hay un mtodo action() en la clase applet BotonAuto, la clase BotonAceptar manejar sus eventos. Si se hubiesen colocado otros objetos en el applet, se podra haber usado un mtodo action() para tratar los eventos de esos objetos.

Etiquetas
Las etiquetas (Label) proporcionan una forma de colocar texto esttico en un panel, para mostrar informacin que no vara, normalmente, al usuario. El applet Etiqueta.java presenta dos textos en pantalla, tal como aparece en la figura siguiente:

114

Tutorial de Java

import java.awt.*; import java.applet.Applet; public class Etiqueta extends Applet { public void init() { Label etiq1 = new Label( Hola Java! ); Label etiq2 = new Label( Otra Etiqueta ); setLayout( new FlowLayout( FlowLayout.CENTER,10,10) ); add( etiq1 ); add( etiq2 ); } }

Listas
Las listas (List) aparecen en los interfaces de usuario para facilitar a los operadores la manipulacin de muchos elementos. Se crean utilizando mtodos similares a los de los botones Choice. La lista es visible todo el tiempo, utilizndose una barra de desplazamiento para visualizar los elementos que no caben en el rea que aparece en la pantalla. El ejemplo siguiente, Lista.java, crea una lista que muestra cuatro lneas a la vez y no permite seleccin mltiple.

import java.awt.*; import java.applet.Applet; public class Lista extends Applet { public void init() { List l = new List( 4,false );

Tutorial de Java

115

l.addItem( Mercurio ); l.addItem( Venus ); l.addItem( Tierra ); l.addItem( Marte ); l.addItem( Jupiter ); l.addItem( Saturno ); l.addItem( Neptuno ); l.addItem( Urano ); l.addItem( Pluton ); add( l ); } public boolean action( Event evt,Object obj ) { if( evt.target instanceof List ) System.out.println( Entrada de la Lista: + obj ); return true; } }

Para acceder a los elementos seleccionados se utilizan los mtodos getSelectedItem() o getSelectedItems(). Para listas de seleccin simple, cualquier seleccin con doble-click en la lista disparar el mtodo action() de la misma forma que con los eventos de seleccin en mens. En el applet siguiente, ListaMult.java, se permite al usuario seleccionar varios elementos de los que constituyen la lista. En la figura se muestra la apariencia de una seleccin mltiple en este applet.

import java.awt.*; import java.applet.Applet; public class ListaMult extends Applet { List lm = new List( 6,true ); public void init() { Button boton = new Button( Aceptar ); lm.addItem( Mercurio ); lm.addItem( Venus ); lm.addItem( Tierra );

116

Tutorial de Java

lm.addItem( Marte ); lm.addItem( Jupiter ); lm.addItem( Saturno ); lm.addItem( Neptuno ); lm.addItem( Urano ); lm.addItem( Pluton ); add( lm ); add( boton ); } public boolean action( Event evt,Object obj ) { if( evt.target instanceof Button ) { if( Aceptar.equals( obj ) ) { String seleccion[]; seleccion = lm.getSelectedItems(); for( int i=0; i < seleccion.length; i++ ) System.out.println( seleccion[i] ); } } return true; } }

En este caso de la seleccin mltiple en una lista, utilizamos un evento externo para disparar las acciones asociadas a la lista. En el ejemplo, hemos incluido un botn para generar el evento que hace que el applet recoja los elementos que hay seleccionados en la lista.

Campos de texto
Para la entrada directa de datos se suelen utilizar los campos de texto, que aparecen en pantalla como pequeas cajas que permiten al usuario la entrada por teclado. Los campos de texto (TextField) se pueden crear vacos, vacos con una longitud determinada, rellenos con texto predefinido y rellenos con texto predefinido y una longitud determinada. El applet siguiente, CampoTexto.java, genera cuatro campos de texto con cada una de las caractersticas anteriores. La imagen muestra los distintos tipos de campos.

Tutorial de Java

117

import java.awt.*; import java.applet.Applet; public class CampoTexto extends Applet { TextField tf1,tf2,tf3,tf4; public void init() { // Campo de texto vaco tf1 = new TextField(); // Campo de texto vaco con 20 columnas tf2 = new TextField( 20 ); // Texto predefinido tf3 = new TextField( Hola ); // Texto predefinido en 30 columnas tf4 = new TextField( Hola,30 ); add( tf1 ); add( tf2 ); add( tf3 ); add( tf4 ); } public boolean action( Event evt,Object obj ) { if( evt.target instanceof TextField ) { if( evt.target.equals( tf1 ) ) System.out.println( Campo de Texto 1: + evt.arg.toString() ); if( evt.target.equals( tf1 ) ) System.out.println( Campo de Texto 2: + evt.arg.toString() ); if( evt.target.equals( tf1 ) ) System.out.println( Campo de Texto 3: + evt.arg.toString() ); if( evt.target.equals( tf1 ) ) System.out.println( Campo de Texto 4: + evt.arg.toString() ); } return true; } }

Cuando el usuario teclea un retorno de carro en un campo de texto, se genera un evento

118

Tutorial de Java

TextField, que al igual que con los otros Componentes del AWT podemos capturar con el mtodo action(), tal como se demuestra en el ejemplo.

Areas de texto
Java, a travs del AWT, permite incorporar texto multilnea dentro de zonas de texto (TextArea). Los objetos TextArea se utilizan para elementos de texto que ocupan ms de una lnea, como puede ser la presentacin tanto de texto editable como de slo lectura. Para crear una rea de texto se pueden utilizar cuatro formas anlogas a las que se han descrito en la creacin de Campos de Texto. Pero adems, para las reas de texto hay que especificar el nmero de columnas. Se puede permitir que el usuario edite el texto con el mtodo setEditable() de la misma forma que se haca en el TextField. En la figura aparece la representacin del applet AreaTexto.java, que presenta dos reas de texto, una vaca y editable y otra con un texto predefinido y no editable.

import java.awt.*; import java.applet.Applet; public class AreaTexto extends Applet { TextArea t1,t2;

Tutorial de Java

119

public void init() { Button boton = new Button( Aceptar ); t1 = new TextArea(); t2 = new TextArea( Tutorial de Java,5,40 ); t2.setEditable( false ); add( t1 ); add( t2 ); add( boton ); } public boolean action( Event evt,Object obj ) { if( evt.target instanceof Button ) { if( Aceptar.equals( obj ) ) { String texto = t1.getText(); System.out.println( texto ); } } return true; } }

Para acceder al texto actual de una zona de texto se utiliza el mtodo getText(), tal como muestra el cdigo anterior. Las reas de texto no generan eventos por s solas, por lo que hay que utilizar eventos externos, para saber cuando tenemos que acceder a la informacin contenida en el TextArea. En este caso hemos utilizado un botn que generar un evento al pulsarlo que har que se recoja el texto que haya escrito en el rea de texto que constituye el applet.

Canvas
Si tenemos un applet que trabaja con imgenes directamente, ya sea un applet grfico o de dibujo, los lienzos o zonas de dibujo (Canvas) resultan muy tiles. Los Canvas son un Componente bsico que captura eventos de exposicin (expose), de ratn y dems eventos relacionados. La clase base Canvas no responde a estos eventos, pero se puede extender esa clase base creando subclases en las que controlemos eseos eventos. Al permitir saltarse el manejo normal de eventos, y junto con los mtodos de representacin grfica, los canvas simplifican la produccin de applets que necesitan una nica funcionalidad para distintas reas. Por ejemplo, el applet Lienzo.java, contiene un manejador de eventos que controla el evento mouseDown en el canvas. Si el evento no se genera en el canvas, se le pasa al applet que lo tratar como un evento de ratn normal.
import java.awt.*;

120

Tutorial de Java

import java.applet.Applet; public class Lienzo extends Applet { Button boton; public void init() { setLayout( new BorderLayout( 15,15 ) ); boton = new Button( Test ); MiCanvas canv = new MiCanvas( 100,100 ); add( Center,canv ); add( South,boton ); } public boolean action( Event evt,Object obj ) { System.out.println( Evento: + obj ); return true; } public boolean mouseDown( Event evt,int x, int y ) { System.out.println( Raton: (+x+,+y+) ); return true; } } class MiCanvas extends Canvas { private int ancho; private int alto; public MiCanvas( int anc,int alt ) { ancho = anc; alto = alt; reshape( 0,0,anc,alt ); } public void paint( Graphics g ) { g.setColor( Color.blue ); g.fillRect( 0,0,ancho,alto ); } public boolean mouseDown( Event evt,int x, int y ) { if( x < ancho && y < alto ) { System.out.println( Raton en Canvas: (+x+,+y+) ); return true; } return false; } }

Tutorial de Java

121

Barras de desplazamiento
En determinados applets que necesiten realizar el ajuste de valores lineales en pantalla, resulta til el uso de barras de desplazamiento (Scrollbar). Las barras de desplazamiento proporcionan una forma de trabajar con rangos de valores o de reas como el Componente TextArea, que proporciona dos barras de desplazamiento automticamente. Si queremos implementar un selector de color, como en el applet Slider.java, podemos utilizar una barra de desplazamiento para cada uno de los colores primarios.

import java.awt.*; import java.applet.Applet; public class Slider extends Applet { Scrollbar rojo,verde,azul; public void init() { rojo = new Scrollbar( Scrollbar.VERTICAL,0,1,0,255 ); verde = new Scrollbar( Scrollbar.VERTICAL,0,1,0,255 ); azul = new Scrollbar( Scrollbar.VERTICAL,0,1,0,255 ); add( rojo ); add( verde ); add( azul ); } }

Este tipo de interfaz proporciona al usuario un punto de referencia visual de un rango y al mismo tiempo la forma de cambiar los valores. Por ello, las barras de desplazamiento son Componentes un poco ms complejos que los dems, reflejndose esta complejidad en sus constructores. Al crearlos hay que indicar su orientacin, su valor inicial, los valores mnimo y mximo que puede alcanzar y el porcentaje de rango que estar visible. Tambin podramos utilizar una barra de desplazamiento para un rango de valores de color, tal como se muestra en el ejemplo Ranger.java.

import java.awt.*; import java.applet.Applet; public class Ranger extends Applet { Scrollbar rango; public void init() { rango = new Scrollbar( Scrollbar.HORIZONTAL,0,64,0,255 );

122

Tutorial de Java

add( rango ); } }

Como se puede ver, el ancho de la barra es mayor, en relacin al Scrollbar. En este caso, maxValue representa el valor mximo para el lado izquierdo de la barra. Si se quieren representar 64 colores simultneamente, es decir [0-63] a [192-255], maxValue debera ser 192. Igual que otros Componentes, las barras de desplazamiento generan eventos; pero al contrario que en el resto, se tiene que utilizar el mtodo handleEvent() directamente, en lugar del mtodo action(). El destino del evento ser un objeto de la clase Scrollbar, a partir de ste se obtiene la posicin de la barra de desplazamiento. Como se habr podido observar en los applets anteriores, las barras de desplazamiento no disponen de un display o zona donde se muestren directamente los valores asociados a los desplazamientos. Al contrario, si se desea eso, es necesario aadir explcitamente una caja de texto, tal como se muestra en el ejemplo RangoRojo.java.

import java.awt.*; import java.applet.Applet; public class RangoRojo extends Applet { Scrollbar rango; TextField valor; Label etiqueta; public void init() { rango = new Scrollbar( Scrollbar.HORIZONTAL,0,1,0,255 ); valor = new TextField( 0,5 ); etiqueta = new Label( Rojo (0-255) ); setLayout( new GridLayout( 1,3 ) ); valor.setEditable( false ); add( etiqueta ); add( rango ); add( valor ); } public boolean handleEvent( Event evt ) { if( evt.target instanceof Scrollbar ) { valor.setText( Integer.toString( ((Scrollbar)evt.target).getValue() ) ); return true; }

Tutorial de Java

123

return super.handleEvent( evt ); } }

Ese era el cdigo del applet que construye la ventana de la figura y actualiza el campo de texto asociado. No implementa ninguna otra accin o evento.

Diseo de componentes propios


Tambin podemos atrevernos en el diseo de Componentes propios. Debern ser una subclase de Canvas o Panel. Para mostrarlos en pantalla deberemos sobreescribir los mtodos paint(), update(), minimumSize() y preferredSize() y para controlar los eventos tendremos que sobreescribir el mtodo handleEvent(). Para que podamos reusarlos, tenemos que poner cuidado en el diseo, implementando mtodos get y set, lanzando excepciones cuando proceda y permitiendo el acceso a los campos de que disponga nuestro Componente. Veamos un ejemplo, Separador.java, en que a partir de la clase Canvas, creamos un separador que ser una lnea vertical u horizontal simulando tres dimensiones.
import java.awt.*; public class Separador extends Canvas { public final static int HORIZONTAL = 0; public final static int VERTICAL = 1; int orientacion; Dimension sepTama,sepDim; public Separador( int lon,int thick,int orient ) { orientacion = orient; if( orient == HORIZONTAL ) sepTama = new Dimension( lon,thick ); else sepTama = new Dimension( thick,lon ); } public int getOrientacion() { return orientacion; } public void setOrientacion( int orient) { if( orient > VERTICAL || orient < HORIZONTAL ) throw new IllegalArgumentException( Orientacin ilegal ); if( orientacion != orient ) { orientacion = orient; sepDim = new Dimension( sepDim.height,sepDim.width ); invalidate(); } } public Dimension preferredSize() {

124

Tutorial de Java

return sepDim; } public Dimension minimumSize() { return sepDim; } public void paint( Graphics g ) { int x1,y1,x2,y2; Rectangle bbox = bounds(); Color c = getBackground(); Color brillo = c.brighter(); Color oscuro = c.darker(); if( orientacion == HORIZONTAL ) { x1 = 0; x2 = bbox.width - 1; y1 = y2 = bbox.height/2 - 1; } else { x1 = x2 = bbox.width/2 - 1; y1 = 0; y2 = bbox.height - 1; } g.setColor( oscuro ); g.drawLine( x1,y1,x2,y2 ); g.setColor( brillo ); if( orientacion == HORIZONTAL ) g.drawLine( x1,y1+1,x2,y2+1 ); else g.drawLine( x1+1,y1,x2+1,y2 ); } }

El cdigo que mostramos a continuacin EjSeparador.java, muestra un ejemplo de uso de nuestro separador recin creado:
import java.awt.*; import java.applet.Applet; public class EjSeparador extends Applet { public final static int HORIZONTAL = 0; public final static int VERTICAL = 1; public void init() { setLayout( new BorderLayout() ); TextField texto1 = new TextField( Hola,20 ); TextField texto2 = new TextField( que tal,20 ); Separador raya = new Separador( 8,2,HORIZONTAL ); add( North,texto1 ); add( Center,raya );

Tutorial de Java

125

add( South,texto2 ); } }

Y ahora podemos complicar nuestro Componente, o utilizarlo como base para el desarrollo de otros ms complejos. Por ejemplo, vamos a implementar un Componente de Grupo, que extenderemos de la clase Panel y que la utilizaremos para enmarcar a otros Componentes, tal como se utiliza en otros entornos de ventanas. En el cdigo fuente de la implementacin del Componente, Grupo.java, podemos observar que las acciones seguidas son exactamente las mismas que en el caso anterior: sobreescribir los mtodos que necesitamos y punto.
import java.awt.*; public class Grupo extends Panel { String Rotulo; int altoTexto; int longTexto; int offset; static Font defFuente; FontMetrics metFuente; public Grupo( String titulo ) { altoTexto = 20; longTexto = 20; offset = 10; Rotulo = titulo; setLayout( null ); metFuente = getFontMetrics( defFuente ); } public void paint( Graphics g ) { Dimension d = size(); Font fuente = getFont(); if( fuente != null ) metFuente = getFontMetrics( fuente ); longTexto = metFuente.stringWidth( Rotulo ); altoTexto = metFuente.getHeight(); g.setColor( Color.gray ); g.drawRect( 0, altoTexto/2,d.width-3,d.height-altoTexto/2-3 ); g.setColor( Color.white ); g.drawRect( 1,altoTexto/2+1,d.width-3,d.height-altoTexto/2-3 ); g.setColor( getBackground() ); g.drawLine( offset,altoTexto/2,offset+longTexto+12,altoTexto/2 ); g.drawLine( offset,altoTexto/2+1,offset+longTexto+12,altoTexto/2+1 ); g.setColor( getForeground() ); g.drawString( Rotulo,offset+6,altoTexto-3 ); } public boolean handleEvent( Event evt ) { return( super.handleEvent( evt ) ); } public Dimension preferredSize() { return( minimumSize() );

126

Tutorial de Java

} public Dimension minimumSize() { return( new Dimension( 100,100 ) ); } static { defFuente = new Font( Dialog,0,12 ); } }

Un ejemplo de uso, lo podremos observar compilando y ejecutando el cdigo que se puestra a continuacin, EjGrupo.java:
import java.awt.*; import java.applet.Applet public class EjGrupo extends Applet { Label etiq; Button izqda; Button dcha; Button todo; public void init() { setBackground( Color.lightGray ); setLayout( null ); Grupo g = new Grupo( Etiqueta ); add( g ); g.setFont( new Font(Dialog,0,14 ) ); g.reshape( insets().left + 10,insets().top + 20,290,40 ); g.setLayout( new FlowLayout( FlowLayout.CENTER,10,10 ) ); etiq = new Label( Hola Java! ); g.add( etiq ); Grupo g2 = new Grupo( Botones ); add( g2 ); g2.reshape( insets().left+10,80,290,60 ); izqda = new Button( Botn 1 ); g2.add( izqda ); izqda.reshape( 20,20,70,30 ); dcha = new Button( Botn 2 ); g2.add( dcha ); dcha.reshape( 110,20,70,30 ); todo = new Button( Botn 3 ); g2.add( todo ); todo.reshape( 200,20,70,30 ); } }

Contenedores
Container es una clase abstracta derivada de Component, que representa a cualquier componente que pueda contener otros componentes. Se trata, en esencia, de aadir a la

Tutorial de Java

127

clase Component la funcionalidad de adicin, sustraccin, recuperacin, control y organizacin de otros componentes. El AWT proporciona cuatro clases de Contenedores: Window Frame Dialog Panel

Adems de estos Contenedores, la clase Applet tambin es un Contenedor, es un subtipo de la clase Panel y puede tener Componentes.

Window
Es una superficie de pantalla de alto nivel (una ventana). Una instancia de la clase Window no puede estar enlazada o embebida en otro Contenedor. Una instancia de esta clase no tiene ni ttulo ni borde.

Frame
Es una superficie de pantalla de alto nivel (una ventana) con borde y ttulo. Una instancia de la clase Frame puede tener una barra de men. Una instancia de esta clase es mucho ms aparente y ms semejante a lo que nosotros entendemos por ventana.

Dialog
Es una superficie de pantalla de alto nivel (una ventana) con borde y ttulo. Una instancia de la clase Dialog no puede existir sin una instancia asociada de la clase Frame.

Panel
Es un Contenedor genrico de Componentes. Una instancia de la clase Panel, simplemente proporciona un Contenedor al que ir aadiendo Componentes.

Crear un Contenedor
Antes de poder incorporar Componentes a la interface de usuario que se desea implementar, el programador debe crear un Contenedor. Cuando se construye una aplicacin, el programador debe crear en primer lugar una instancia de la clase Window o de la clase Frame. Cuando lo que se construye es un applet, ya existe un Frame (la ventana del navegador). Debido a que la clase Applet est derivada de la clase Panel, el programador puede ir

128

Tutorial de Java

aadiendo Componentes directamente a la instancia que se crea de la clase Applet. En el siguiente ejemplo se crea un Frame vaco. El ttulo del Frame, que corresponder al ttulo de la ventana, se fija en la llamada al constructor. Un Frame inicialmente est invisible, para poder verlo es necesario invocar al mtodo show():
import java.awt.*; public class Ejemplo1 { public static void main( String args[] ) { Frame f = new Frame( Ejemplo 1 ); f.show(); } }

En el cdigo de ejemplo que sigue, extendemos el cdigo anterior para que la nueva clase sea una subclase de la clase Panel. En el mtodo main() de esta nueva clase se crea una instancia de ella y se le incorpora un objeto Frame llamando al mtodo add(). El resultado de ambos ejemplos es idntico a efectos de apariencia en pantalla:
import java.awt.*; public class Ejemplo2 extends Panel { public static void main( String args[] ) { Frame f = new Frame( Ejemplo 2 ); Ejemplo2 ej = new Ejemplo2(); f.add( Center,ej ); f.pack(); f.show(); } }

Derivando la nueva clase directamente de la clase Applet en vez de Panel, nuestro ejemplo puede ahora ejecutarse tanto como una aplicacin solitaria como dentro de una pgina HTML en un navegador. El cdigo siguiente muestra esta circunstancia:
import java.awt.*; public class Ejemplo3 extends java.applet.Applet { public static void main( String args[] ) { Frame f = new Frame( Ejemplo 3 ); Ejemplo3 ej = new Ejemplo3(); f.add( Center,ej ); f.pack(); f.show(); } }

Un objeto Window, y en algunos casos incluso un objeto Dialog, pueden reemplazar al objeto Frame. Son Contenedores vlidos y los Componentes se aaden en ellos del mismo modo que se hara sobre un Frame.

Tutorial de Java

129

Aadir Componentes a un Contenedor


Para que la interface sea til, no debe estar compuesta solamente por Contenedores, stos deben tener Componentes en su interior. Los Componentes se aaden al Contenedor invocando al mtodo add() del Contenedor. Este mtodo tiene tres formas de llamada que dependen del manejador de composicin o layout manager que se vaya a utilizar sobre el Contenedor. En el cdigo siguiente, incorporamos dos botones al cdigo del ltimo ejemplo. La creacin se realiza en el mtodo init() porque ste siempre es llamado automticamente al inicializarse el applet. De todos modos, al inciarse la ejecucin se crean los botones, ya que el mtodo init() es llamado tanto por el navegador como por el mtodo main():
import java.awt.*; public class Ejemplo4 extends java.applet.Applet { public void init() { add( new Button( Uno ) ); add( new Button( Dos ) ); } public static void main( String args[] ) { Frame f = new Frame( Ejemplo 4 ); Ejemplo4 ej = new Ejemplo4(); ej.init(); f.add( Center,ej ); f.pack(); f.show(); } }

Creacin de aplicaciones con AWT


Para crear una aplicacin utilizando AWT, vamos a ver en principio cmo podemos generar el interface de esa aplicacin, mostrando los distintos elementos del AWT, posteriormente volveremos hacia la implementacin de la aplicacin. Interface Crear el Marco de la aplicacin (Frame) Inicializar Fuentes, Colores, Layouts y dems recursos Crear mens y Barras de Mens Crear los controles, dilogos, ventanas, etc.

Implementacin Incorporar controladores de eventos Incorporar funcionalidad (threads, red, etc.) Incorporar un controlador de errores (excepciones)

130

Tutorial de Java

Crear el marco de la aplicacin


El Contenedor de los Componentes es el Frame. Tambin es la ventana principal de la aplicacin, lo que hace que para cambiar el icono o el cursor de la aplicacin no sea necesario crear mtodos nativos; al ser la ventana un Frame, ya contiene el entorno bsico para la funcionalidad de la ventana principal. Vamos a empezar a crear una aplicacin bsica, a la que iremos incorporando Componentes. Quizs vayamos un poco de prisa en las explicaciones que siguen; no preocuparse, ya que lo que no quede claro ahora, lo estar ms tarde. El problema es que para poder profundizar sobre algunos aspectos de Java, necesitamos conocer otros previos, as que proporcionaremos un ligero conocimiento sobre algunas caractersticas de Java y del AWT, para que nos permitan entrar a fondo en otras; y ya conociendo estas ltimas, volveremos sobre las primeras. Un poco lioso, pero imprescindible. En el archivo AplicacionAWT.java, se encuentra el cdigo completo de la aplicacin que vamos ir construyendo a lo largo de este repaso general por las caractersticas de que dispone el AWT. Comenzaremos el desarrollo de nuestra aplicacin bsica con AWT a partir del cdigo que mostramos a continuacin:
import java.awt.*; public class AplicacionAWT extends Frame { static final int HOR_TAMANO = 300; static final int VER_TAMANO = 200; public AplicacionAWT() { super( Aplicacin Java con AWT ); pack(); resize( HOR_TAMANO,VER_TAMANO ); show(); } public static void main( String args[] ) { new AplicacionAWT(); } }

La clase anterior es un Frame, ya que extiende esa clase y hereda todas sus caractersticas. Tiene un mtodo, el constructor, que no admite parmetros. Adems, se hace una llamada explcita al constructor super, es decir, al constructor de la clase padre, para pasarle como parmetro la cadena que figurar como el ttulo de la ventana. La llamada a show() es necesaria, ya que por defecto, los Contenedores del AWT se crean ocultos y hay que mostrarlos explcitamente. La llamada a pack() hace que los Componentes se ajusten a sus tamaos correctos en funcin del Contenedor en que se encuentren

Tutorial de Java

131

situados. La ejecucin de la aplicacin mostrar la siguiente ventana en pantalla:

Los atributos fundamentales de la ventana anterior son: Marco de 300x200 pixels No tiene barra de men No tiene ningn Componente Ttulo Aplicacin Java con AWT Color de fondo por defecto Layout por defecto Fondo de la ventana vaco

Inicializar fuentes, colores y recursos


Vamos a ir alterando los recursos de la ventana de la aplicacin Java que estamos desarrollando con el AWT, para ir incorporando y visualizando los distintos Componentes que proporciona AWT. Insertemos algunas lneas de cdigo en el constructor para inicializar la aplicacin: Cambiemos el font de caracteres a Times Roman de 12 puntos
setFont( new Font( TimesRoman,Font.PLAIN,12 ) );

Fijemos los colores de la ventana para que el fondo sea Blanco y el texto resalte en Negro
setBackground( Color.white ); setForeground( Color.black );

Seleccionemos como disposicin de los Componentes el BorderLayout para este Contenedor


setLayout( new BorderLayout() );

Incorporemos grficos. Usamos mtodos definidos en la clase Graphics; por ejemplo, reproduzcamos el ttulo de la ventana en medio con una fuente Time Roman de 24 puntos en color Azul. Es necesario utilizar new con Font ya que en Java, todo son objetos y no podramos utilizar un nuevo font de caracteres sin antes haberlo

132

Tutorial de Java

creado
public void paint( Graphics g ) { g.setFont( new Font( TimesRoman,Font.BOLD,24 ) ); g.setColor( Color.blue ); g.drawString( getTitle(),30,50 ); }

lar

Incorporemos en la parte inferior de la ventana dos botones: Aceptar y Cance-

Panel p = new Panel(); p.add( new Button( Aceptar ) ); p.add( new Button( Cancelar ) ); add( South,p );

Los Componentes se incorporan al Contenedor a travs de los dos mtodos add() que hay definidos:
add( Component c ); add( String s,Component c );

Los Componentes tambin se podan haber insertado en el Frame, organizndolos en una cierta forma, teniendo en cuenta que su manejador de composicin es un BorderLayout. Por ejemplo:
add( South,new Button( Aceptar ) ); add( South,new Button( Cancelar ) );

Hemos utilizado un Panel y no el segundo mtodo, porque es ms til el organizar los Componentes en pequeas secciones. As, con nuestro cdigo podemos considerar al Panel como una entidad separada del Frame, lo cual permitira cambiar el fondo, layout, fuente, etc. del Panel sin necesidad de tocar el Frame. Si ejecutamos de nuevo la aplicacin con los cambios que hemos introducido, aparecer ante nosotros la ventana que se muestra a continuacin.

Si intentsemos en esta aplicacin cerrar la ventana, no sucede nada. Cuando se intenta cerrar la ventana, el sistema enva un evento que no se est tratando. Incorporemos pues un controlador de eventos y empecemos tratando el evento WINDOW_DESTROY, generado cuando se intenta cerrar la ventana:
public boolean handleEvent( Event evt ) { switch( evt.id ) {

Tutorial de Java

133

case Event.WINDOW_DESTROY: { System.exit( 0 ); return true; } default: return false; } }

Si ahora ejecutamos de nuevo la aplicacin y cerramos la ventana... Efectivamente se cierra, tal como se supona.

Crear menus y barra de menus


En la actual versin del AWT que se proporciona con el JDK, slo se permite crear mens a travs de cdigo, ya que Java todava no dispone de un formato de recursos y tampoco hay un diseador como pueden ser AppStudio, Delphi o X-Designer; aunque terminar habiendo uno, con seguridad. No hay ningn mtodo para disear una buena interface, todo depende del programador. Los mens son el centro de la aplicacin. La diferencia entre una aplicacin til y otra que es totalmente frustrante radica en la organizacin de los mens, pero eso, las reglas del diseo de un buen rbol de mens, no estn claras. Hay un montn de libros acerca de la ergonoma y de cmo se debe implementar la interaccin con el usuario. Lo cierto es que por cada uno que defienda una idea, seguro que hay otro que defiende la contraria. Todava no hay un acuerdo para crear un estndar, con cada Window Manager se publica una gua de estilo diferente. As que, vamos a explicar lo bsico, sin que se deba tomar como dogma de fe, para que luego cada uno haga lo que mejor le parezca. La interface MenuContainer solamente se puede implementar sobre un Frame. Un applet que desee tener un men, debe crear un Frame en primer lugar. El cdigo de la funcin que vamos a ver, crea una barra de mens y se llama desde el constructor del Frame. La funcin es private porque no queremos que se pueda llamar desde ninguna otra clase.
private void InicializaMenus() { mbarra = new MenuBar(); Menu m = new Menu( Archivo ); m.add( new MenuItem( Nuevo) ); m.add( new MenuItem( Abrir) ); m.add( new MenuItem( Guardar) ); m.add( new MenuItem( Guardar como) ); m.add( new MenuItem( Imprimir) ); m.addSeparator(); m.add( new MenuItem( Salir) ); mbarra.add( m ); m = new Menu( Ayuda ); m.add( new MenuItem( Ayuda! ) ); m.addSeparator(); m.add( new MenuItem( Acerca de... ) );

134

Tutorial de Java

mbarra.add( m ); setMenuBar( mbarra ); }

El men que crea esta funcin tendr la apariencia que muestra la figura siguiente:

Los eventos generados por las opciones de un men se manejan del mismo modo que los Botones, Listas, etc. En el caso de mens, es el evento ACTION_EVENT de la clase java.awt.MenuItem el que se genera y en evt.target se nos indica la opcin seleccionada.
case Event.ACTION_EVENT: { if( evt.target instanceof MenuItem ) { if( Nuevo.equals( evt.arg ) ) AplicacionAWT aAwt = new AplicacionAWT();; if( Abrir.equals( evt.arg ) ) System.out.println( Opcion -Abrir- ); if( Guardar.equals( evt.arg ) ) System.out.println( Opcion -Guardar- ); if( Guardar como.equals( evt.arg ) ) System.out.println( Opcion -Guardar como- ); if( Imprimir.equals( evt.arg ) ) System.out.println( Opcion -Imprimir- ); if( Salir.equals( evt.arg ) ) System.exit( 0 ); if( Ayuda!.equals( evt.arg ) ) System.out.println( No hay ayuda ); if( Acerca de.equals( evt.arg ) ) System.out.println( Opcion -Acerca de- ); } }

En el cdigo anterior hemos tratado los eventos del men. Para ms seguridad, aunque no sea estrictamente necesario, lo primero que hacemos es asegurarnos de que el objeto evt.target es realmente un objeto MenuItem, es decir, procede de la barra de mens; y despus comprobamos la opcin que se ha seleccionado. Como todo, tambin se puede rizar el rizo y conseguir reproducir los sistemas de mens que estamos acostumbrados a ver en las aplicaciones que manejamos habitualmente. Un ejemplo de ello son los mens en cascada, semejantes al que muestra la figura y que ha sido generado mediante la aplicacin Cascada.java.

Tutorial de Java

135

Bsicamente se utiliza la tcnica ya descrita, pero en vez de crear un nuevo MenuItem se crea un nuevo Menu, lo que origina el men en cascada. No obstante, y volviendo al diseo de interfaces, no debe abusarse de este tipo de mens, porque pueden crear mucha ms confusin al usuario. Siempre se debe tener en mente que los usuarios tienen que navegar habitualmente por una gran cantidad de mens en las ms diversas aplicaciones, por lo que no debemos esconderles demasiado las opciones que les pueden interesar.

Dialogos y Ventanas
Una Ventana genrica, Window, puede utilizarse simplemente para que sea la clase padre de otras clases y se puede intercambiar por un Dilogo, Dialog, sin prdida de funcionalidad. No se puede decir lo mismo de un Frame. Se podra crear un men pop-up con una Ventana, pero lo cierto es que en esta versin del JDK hay un montn de bugs y no merece la pena el enfrascarse en el intento. No obstante, hay ciertos mtodos que estn en la clase Window y que no estn presentes en otras clases que pueden resultar interesantes y necesitar una Ventana si queremos emplearlos. Son:

getToolkit() getWarningString() pack() toBack() toFront()

Un Dilogo es una subclase de Window, que puede tener un borde y ser modal, es decir, no permite hacer nada al usuario hasta que responda al dilogo. Esto es lo que se usa en las cajas de dilogo Acerca de..., en la seleccin en listas, cuando se pide una entrada numrica, etc. El cdigo Java que se expone a continuacin, implementa el dilogo Acerca de para la aplicacin. Esta clase se crea oculta y necesitaremos llamar al mtodo show() de la propia clase para hacerla visible.
class AboutDialog extends Dialog { static int HOR_TAMANO = 300; static int VER_TAMANO = 150;

136

Tutorial de Java

public AboutDialog( Frame parent ) { super( parent,Acerca de...,true ); this.setResizable( false ); setBackground( Color.gray ); setLayout( new BorderLayout() ); Panel p = new Panel(); p.add( new Button( Aceptar ) ); add( South,p ); resize( HOR_TAMANO,VER_TAMANO ); } public void paint( Graphics g ) { g.setColor( Color.white ); g.drawString( Aplicacin Java con AWT, HOR_TAMANO/4,VER_TAMANO/3 ); g.drawString( Versin 1.00, HOR_TAMANO/3+15,VER_TAMANO/3+20 ); } public boolean handleEvent( Event evt ) { switch( evt.id ) { case Event.ACTION_EVENT: { if( Aceptar.equals( evt.arg ) ) { hide(); return true; } } default: return false; } } }

La ventana que aparece en pantalla generada por la clase anterior es la que muestra la figura:

Las aplicaciones independientes deberan heredar tomando como padre la ventana principal de esa aplicacin. As pueden implementar la interface MenuContainer y proporcionar mens.

Tutorial de Java

137

No hay razn aparente para que sea una subclase de la clase Frame, pero si se quiere proporcionar funcionalidad extra, s debera serlo, en vez de serlo de su padre: Window. Esto es as porque Frame implementa la interface MenuContainer, con lo cual tiene la posibilidad de proporcionar mens y cambiar el cursor, el icono de la aplicacin, etc. Un ejemplo ms complicado de aplicacin grfica basada en el AWT es el convertidor de decimal a binario/octal/hexadecimal/base36, Convertidor.java, cuya presentacin en pantalla es la que muestra la figura siguiente.

En la construccin de esta nueva aplicacin se utilizan elementos que se presentan en profundidad en secciones posteriores de este Tutorial.

Paneles
La clase Panel es el ms simple de los Contenedores de Componentes grficos. En realidad, se trataba de crear una clase no-abstracta (Container s lo es) que sirviera de base a los applet y a otras pequeas aplicaciones. La clase Panel consta de dos mtodos propios: el constructor, cuyo fin es crear un nuevo Panel con un LayoutManager de tipo FlowLayout (el de defecto), y el mtodo addNotify() que, sobrecargando la funcin del mismo nombre en la clase Container, llama al mtodo createPanel() del Toolkit adecuado, creando as un PanelPeer. El AWT enviar as al Panel (y por tanto al applet) todos los eventos que sobre l ocurran. Esto que puede parecer un poco rebuscado, obedece al esquema arquitectnico del AWT; se trata del bien conocido esquema de separacin interface/implementacin que establece por un lado una clase de interface y por otro distintas clases de implementacin para cada una de las plataformas elegidas. El uso de Paneles permite que las aplicaciones puedan utilizar mltiples layouts, es decir, que la disposicin de los componentes sobre la ventana de visualizacin pueda modificarse con mucha flexibilidad. Permite que cada Contenedor pueda tener su propio esquema de fuentes de caracteres, color de fondo, zona de dilogo, etc. Podemos, por ejemplo, crear una barra de herramientas para la zona superior de la ventana de la aplicacin o incorporarle una zona de estado en la zona inferior de la ventana para mostrar informacin til al usuario. Para ello vamos a implementar dos Paneles:
class BarraHerram extends Panel {

138

Tutorial de Java

public BarraHerram() { setLayout( new FlowLayout() ); add( new Button( Abrir ) ); add( new Button( Guardar ) ); add( new Button( Cerrar ) ); Choice c = new Choice(); c.addItem( Times Roman ); c.addItem( Helvetica ); c.addItem( System ); add( c ); add( new Button( Ayuda ) ); } } class BarraEstado extends Panel { Label texto; Label mas_texto; public BarraEstado() { setLayout( new FlowLayout() ); add( texto = new Label( Creada la barra de estado ) ); add( mas_texto = new Label( Informacin adicional ) ); } public void verEstado( String informacion ) { texto.setText( informacion ); } }

Ahora, para dar funcionalidad, debemos crear los objetos correspondientes a la barra de herramientas y a la barra de estado con new; al contrario que en C++, en Java todos los objetos deben ser creados con el operador new:
add( North,tb = new ToolBar() ); add( South,sb = new StatusBar() );

Tambin vamos a incorporar un nuevo evento a nuestro controlador, para que maneje los eventos de tipo ACTION_EVENT que le llegarn cuando se pulsen los botones de la barra de herramientas o se realice alguna seleccin, etc.
case Event.ACTION_EVENT: { be.verEstado( evt.arg.toString() ); return true; }

Cuando la aplicacin reciba este tipo de evento, alterar el contenido de la barra de estado para mostrar la informacin de la seleccin realizada o el botn pulsado.

Tutorial de Java

139

Al final, la apariencia de la aplicacin en pantalla es la que presenta la figura anterior.

Layouts
Los layout managers o manejadores de composicin, en traduccin literal, ayudan a adaptar los diversos Componentes que se desean incorporar a un Panel, es decir, especifican la apariencia que tendrn los Componentes a la hora de colocarlos sobre un Contenedor. Java dispone de varios, en la actual versin, tal como se muestra en la imagen:

Por qu Java proporciona estos esquemas predefinidos de disposicin de componentes? La razn es simple: imaginemos que deseamos agrupar objetos de distinto tamao en celdas de una rejilla virtual: si confiados en nuestro conocimiento de un sistema grfico determinado, codificamos a mano tal disposicin, deberemos preveer el redimensionamiento del applet, su repintado cuando sea cubierto por otra ventana, etc., adems de todas las cuestiones relacionadas con un posible cambio de plataforma (uno nunca sabe a donde van a ir a parar los propios hijos, o los applets). Sigamos imaginando, ahora, que un hbil equipo de desarrollo ha previsto las disposiciones grficas ms usadas y ha creado un gestor para cada una de tales configuraciones, que se

140

Tutorial de Java

ocupar, de forma transparente para nosotros, de todas esas cuitas de formatos. Bien, pues estos gestores son instancias de las distintas clases derivadas de Layout Manager y que se utilizan en el applet que genera la figura siguiente, donde se muestran los diferentes tipos de layouts que proporciona el AWT. En el applet que genera la figura siguiente, se utilizan los diferentes tipos de layouts que proporciona el AWT.

El ejemplo AwtGui.java, ilustra el uso de paneles, listas, barras de desplazamiento, botones, selectores, campos de texto, reas de texto y varios tipos de layouts. En el tratamiento de los Layouts se utiliza un mtodo de validacin, de forma que los Componentes son marcados como no vlidos cuando un cambio de estado afecta a la geometra o cuando el Contenedor tiene un hijo incorporado o eliminado. La validacin se realiza automticamente cuando se llama a pack() o show(). Los Componentes visibles marcados como no vlidos no se validan automticamente. En el ejemplo de control de eventos que se muestra a continuacin, la llamada a validate() hace que se realicen en un bloque, en un solo paso, todos los clculos necesarios para la validacin del layout.
public boolean action( Event evt,Object obj ) { if( obj.equals( Cambia Font ) ) { boton1.setFont( nuevoFont ); boton2.setFont( nuevoFont ); etiqueta.setFont( nuevoFont ); campoTexto.setFont( nuevoFont ); validate(); return true; } }

Tutorial de Java

141

FlowLayout
Es el ms simple y el que se utiliza por defecto en todos los Paneles si no se fuerza el uso de alguno de los otros. Los Componentes aadidos a un Panel con FlowLayout se encadenan en forma de lista. La cadena es horizontal, de izquierda a derecha, y se puede seleccionar el espaciado entre cada Componente. Por ejemplo, podemos poner un grupo de botones con la composicin por defecto que proporciona FlowLayout:

import java.awt.*; import java.applet.Applet; public class AwtFlow extends Applet { Button boton1,boton2,boton3; public void init() { boton1 = new Button( Aceptar ); boton2 = new Button( Abrir ); boton3 = new Button( Cerrar ); add( boton1 ); add( boton2 ); add( boton3 ); } }

Este cdigo, AwtFlow.java, construye tres botones con un pequeo espacio de separacin entre ellos.

BorderLayout
La composicin BorderLayout (de borde) proporciona un esquema ms complejo de colocacin de los Componentes en un panel. La composicin utiliza cinco zonas para colocar los Componentes sobre ellas: Norte, Sur, Este, Oeste y Centro. Es el layout o composicin que se utilizan por defecto Frame y Dialog. El Norte ocupa la parte superior del panel, el Este ocupa el lado derecho, Sur la zona inferior y Oeste el lado izquierdo. Centro representa el resto que queda, una vez que se hayan rellenado las otras cuatro partes.

142

Tutorial de Java

Con BorderLayout se podran representar botones de direccin:

import java.awt.*; import java.applet.Applet; public class AwtBord extends Applet { Button botonN,botonS,botonE,botonO,botonC; public void init() { setLayout( new BorderLayout() ); botonN = new Button( Norte ); botonS = new Button( Sur ); botonE = new Button( Este ); botonO = new Button( Oeste ); botonC = new Button( Centro ); add( North,botonN ); add( South,botonS ); add( East,botonE ); add( West,botonO ); add( Center,botonC ); } }

Este es el cdigo, AwtBord.java, que genera el applet de botones de direccin:

GridLayout
La composicin GridLayout proporciona gran flexibilidad para situar Componentes. El layout se crea con un nmero de filas y columnas y los Componentes van dentro de las celdas de la tabla as definida. En la figura siguiente se muestra un panel que usa este tipo de composicin para posicionar seis botones en su interior, con tres filas y dos columnas que crearn las seis celdas necesarias para albergar los botones:

Tutorial de Java

143

import java.awt.*; import java.applet.Applet; public class AwtGrid extends Applet { Button boton1,boton2,boton3,boton4,boton5,boton6; public void init() { setLayout( new GridLayout( 3,2 ) ); boton1 = new Button( 1 ); boton2 = new Button( 2 ); boton3 = new Button( 3 ); boton4 = new Button( 4 ); boton5 = new Button( 5 ); boton6 = new Button( 6 ); add( boton1 ); add( boton2 ); add( boton3 ); add( boton4 ); add( boton5 ); add( boton6 ); } }

Este es el cdigo, AwtGrid.java, que genera la imagen del ejemplo.

GridBagLayout
Es igual que la composicin de GridLayout, con la diferencia que los Componentes no necesitan tener el mismo tamao. Es quiz el layout ms sofisticado y verstil de los que actualmente soporta AWT. En la figura siguiente vemos la imagen generada por un panel que usa el GridBagLayout para soportar diez botones en su interior:

144

Tutorial de Java

import java.awt.*; import java.applet.Applet; public class AwtGBag extends Applet { public void init() { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints(); setLayout( gridbag ); gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 1.0; Button boton0 = new Button( Botn 0 ); gridbag.setConstraints( boton0,gbc ); add( boton0 ); Button boton1 = new Button( Botn 1 ); gridbag.setConstraints( boton1,gbc ); add( boton1 ); Button boton2 = new Button( Botn 2 ); gridbag.setConstraints( boton2,gbc ); add( boton2 ); gbc.gridwidth = GridBagConstraints.REMAINDER; Button boton3 = new Button( Botn 3 ); gridbag.setConstraints( boton3,gbc ); add( boton3 ); gbc.weightx = 0.0; Button boton4 = new Button( Botn 4 ); gridbag.setConstraints( boton4,gbc ); add( boton4 ); gbc.gridwidth = GridBagConstraints.RELATIVE; Button boton5 = new Button( Botn 5 ); gridbag.setConstraints( boton5,gbc ); add( boton5 ); gbc.gridwidth = GridBagConstraints.REMAINDER; Button boton6 = new Button( Botn 6 ); gridbag.setConstraints( boton6,gbc ); add( boton6 ); gbc.gridwidth = 1;

Tutorial de Java

145

gbc.gridheight = 2; gbc.weighty = 1.0; Button boton7 = new Button( Botn 7 ); gridbag.setConstraints( boton7,gbc ); add( boton7 ); gbc.weighty = 0.0; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.gridheight = 1; Button boton8 = new Button( Botn 8 ); gridbag.setConstraints( boton8,gbc ); add( boton8 ); Button boton9 = new Button( Botn 9 ); gridbag.setConstraints( boton9,gbc ); add( boton9 ); } }

Este es el cdigo, AwtGBag.java, que utilizamos para generar la imagen del ejemplo. Para aprovechar de verdad todas las posibilidades que ofrece este layout, hay que pintar antes en papel como van a estar posicionados los Componentes; utilizar gridx, gridy, gridwidth y gridheight en vez de GridBagConstraints.RELATIVE, porque en el proceso de validacin del layout pueden quedar todos los Componentes en posicin indeseable. Adems, se deberan crear mtodos de conveniencia para hacer ms fcil el posicionamiento de los Componentes. En el ejemplo siguiente, AwtGBagConv.java, creamos el mtodo de conveniencia addComponente() para la incorporacin de nuevos Componentes al layout, lo que hace ms sencillo el manejo de los Constraints:
import java.awt.*; import java.applet.Applet; public class AwtGBagConv extends Applet { GridBagLayout gridbag = new GridBagLayout(); void addComponente( Component comp,int gridx,int gridy, int gridw,int gridh ) { GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = gridx; gbc.gridy = gridy; gbc.gridwidth = gridw; gbc.gridheight = gridh; gridbag.setConstraints( comp,gbc ); add( comp ); } public void init() { setLayout( gridbag ); addComponente( new Label( Nombre: ),0,0,1,1 ); addComponente( new TextField( 12 ),1,0,2,1 ); addComponente( new TextArea( 5,20 ),0,1,2,2 ); addComponente( new Checkbox( S? ),2,1,1,1 ); addComponente( new List(),2,2,1,1 ); } }

146

Tutorial de Java

CardLayout
Este es el tipo de composicin que se utiliza cuando se necesita una zona de la ventana que permita colocar distintos Componentes en esa misma zona. Este layout suele ir asociado con botones de lista (Choice), de tal modo que cada seleccin determina el panel (grupo de componentes) que se presentarn. En la figura siguiente mostramos el efecto de la seleccin sobre la apriencia de la ventana que contiene el panel con la composicin CardLayout:

import java.awt.*; import java.applet.Applet; public class AwtCard extends Applet { Panel card; final static String PanelBoton = Panel con Botones; final static String PanelTexto = Panel con Campo de Texto; public void init() { setLayout( new BorderLayout() ); Panel ac = new Panel(); Choice c = new Choice(); c.addItem( PanelBoton ); c.addItem( PanelTexto ); ac.add( c ); add( North,ac ); card = new Panel(); card.setLayout( new CardLayout() ); Panel p1 = new Panel(); p1.add( new Button( Botn 1 ) ); p1.add( new Button( Botn 2 ) ); p1.add( new Button( Botn 3 ) ); Panel p2 = new Panel(); p2.add( new TextField( Texto,20 ) ); card.add( PanelBoton,p1 ); card.add( PanelTexto,p2 );

Tutorial de Java

147

add( Center,card ); } public boolean action( Event evt,Object arg ) { if( evt.target instanceof Choice ) { ( (CardLayout)card.getLayout() ).show( card,(String)arg ); return true; } return false; } }

Este es el cdigo, AwtCard.java, que hemos utilizado para generar las dos imgenes que muestran el funcionamiento de la composicin CardLayout.

Crear un Layout propio


Se puede crear un Layout personalizado en base a la interface LayoutManager. Hay que redefinir los cinco mtodos que utiliza este interface, lo cual puede no resultar sencillo, as que en lo posible se deben utilizar los mtodos de colocacin de componentes que proporciona AWT, fundamentalmente en el momento en que parezca que ninguno de los Layouts que hemos visto satisface nuestras exigencias, deberamos volver a comprobar que el GridBagLayout, que es el ms flexible, de verdad no cumple nuestros requerimientos. No obstante, vamos a implementar un layout propio, MiLayout.java, para poder colocar los Componentes en posiciones absolutas del panel que contenga a este layout. Derivamos nuestro nuevo layout de LayoutManager y redefinimos los cinco mtodos de la clase para que podamos posicionar los Componentes.
import java.awt.*; public class MiLayout implements LayoutManager { public MiLayout() { } public void addLayoutComponent( String name,Component comp ) { } public void removeLayoutComponent( Component comp ) { } public Dimension preferredLayoutSize( Container parent ) { Insets insets = parent.insets(); int numero = parent.countComponents(); int ancho = 0; int alto = 0; for( int i=0; i < numero; i++ ) { Component comp = parent.getComponent( i ); Dimension d = comp.preferredSize();

148

Tutorial de Java

Point p = comp.location(); if( ( p.x + d.width ) > ancho ) ancho = p.x + d.width; if( ( p.y + d.height ) > alto ) alto = p.y + d.height; } return new Dimension( insets.left + insets.right + ancho, insets.top + insets.bottom + alto ); } public Dimension minimumLayoutSize( Container parent ) { Insets insets = parent.insets(); int numero = parent.countComponents(); int ancho = 0; int alto = 0; for( int i=0; i < numero; i++ ) { Component comp = parent.getComponent( i ); Dimension d = comp.preferredSize(); Point p = comp.location(); if( ( p.x + d.width ) > ancho ) ancho = p.x + d.width; if( ( p.y + d.height ) > alto ) alto = p.y + d.height; } return new Dimension( insets.left + insets.right + ancho, insets.top + insets.bottom + alto ); } public void layoutContainer( Container parent ) { int numero = parent.countComponents(); for( int i=0; i < numero; i++ ) { Component comp = parent.getComponent( i ); Dimension d = comp.preferredSize(); comp.resize( d.width,d.height ); } } }

Y ahora vamos a ver un ejemplo en que utilicemos nuestro Layout. Posicionaremos tres botones en el panel y un campo de texto con una etiqueta precedindolo. La apriencia que tendr en pantalla ser la que se muestra en la figura:

Tutorial de Java

149

import java.awt.*; import java.applet.Applet; public class AwtLibre extends Applet { Button boton1,boton2,boton3; Label etiqueta; TextField texto; public void init() { setLayout( new MiLayout() ); boton1 = new Button( Aceptar ); boton2 = new Button( Abrir ); boton3 = new Button( Cerrar ); etiqueta = new Label( Texto ); texto = new TextField( ,20 ); add( boton1 ); add( boton2 ); add( boton3 ); add( etiqueta ); add( texto ); boton1.move( 0,10 ); boton2.move( 70,10 ); boton3.move( 30,40 ); etiqueta.move( 75,70 ); texto.move( 120,70 ); } }

Este cdigo, AwtLibre.java, es el que genera la imagen anterior creando un applet que utiliza nuestro recin creado layout.

Control de eventos
En el pasado, un programa que quisiera saber lo que estaba haciendo el usuario, deba recoger la informacin l mismo. En la prctica, esto significaba que una vez inicializado, el programa entraba en un gran bucle en el que continuamente se bloqueaba para comprobar que el usuario estuviese haciendo algo interesante (por ejemplo, pulsar un botn, pulsar una tecla, mover una barra o mover el ratn) y tomar las acciones oportunas. Esta tcnica se conoce como polling.

150

Tutorial de Java

El polling funciona, pero se vuelve demasiado difcil de manejar con las aplicaciones modernas por dos razones fundamentales: Primero, el uso de polling tiende a colocar todo el cdigo de control de eventos en una nica localizacin (dentro de un gran bucle); segundo, las interacciones dentro del gran bucle tienden a ser muy complejas. Adems, el polling necesita que el programa est ejecutando el bucle, consumiendo tiempo de CPU, mientras est esperando a que el usuario se decida a hacer algo, lo que supone un gran despilfarro de recursos. El AWT resuelve estos problemas abrazando un paradigma diferente, en el que estn basados todos los sistemas modernos de ventanas: la orientacin a eventos. Dentro del AWT, todas las acciones que pueda realizar el usuario caen dentro de un gran saco que son los eventos. Un evento describe, con suficiente detalle, una accin particular del usuario. En lugar de que el programa activamente recoja todos los eventos generados por el usuario, el sistema Java avisa al programa cuando se produce un evento interesante.

La clase Event
Un contenedor soltado en un entorno grfico se convierte en rpido receptor de eventos de todo tipo, singularmente de los relacionados con el movimiento del ratn, pulsaciones de teclas, creacin/movimiento/destruccin de partes grficas y, por ltimo, los referidos a acciones del usuario respecto de componentes (eleccin de un men, pulsacin de un botn, etc.). La clase Event es el jugador principal en el juego de los eventos. Intenta capturar las caractersticas fundamentales de todos los eventos que genera el usuario. Los datos miembro de la clase Event son los que se indican a continuacin: id - El tipo de evento que se ha producido target - Componente sobre el que se ha producido el evento x, y - Las coordenadas en donde se ha producido el evento relativas al Componente que actualmente est procesando ese evento. El origen se toma en la esquina superior izquierda del Componente key - Para eventos de teclado, es la tecla que se ha pulsado. Su valor ser el valor Unicode del carcter que representa la tecla. Otros valores que puede tomas son los de las teclas especiales como INICIO, FIN, F1, F2, etc. when - Instante en que se ha producido el evento modifiers - La combinacin aritmtica del estado en que se encuentran las teclas modificadoras Mays, Alt, Ctrl. clickCount - El nmero de clicks de ratn consecutivos. Slo tiene importancia en los eventos MOUSE_DOWN arg - Es un argumento dependiente del evento. Para objetos Button, este objeto arg es un

Tutorial de Java

151

objeto String que contiene la etiqueta de texto del botn evt - El siguiente evento en una lista encadenada de eventos Una instancia de la clase Event ser creada por el sistema Java cada vez que se genere un evento. Es posible, sin embargo, que un programa cree y enve eventos a los Componentes a travs de su mtodo postEvent().

Tipos de eventos
Los eventos se catalogan por su naturaleza, que se indicar en el miembro id de su estructura. Los grandes grupos de eventos son:

Eventos de Ventana
Son los que se generan en respuesta a los cambios de una ventana un frame o un dialogo. WINDOW_DESTROY WINDOW_EXPOSE WINDOW_ICONIFY WINDOW_DEICONIFY WINDOW_MOVED

Eventos de Teclado
Son generados en respuesta a cuando el usuario pulsa y suelta una tecla mientras un Componente tiene el foco de entrada. KEY_PRESS KEY_RELEASE KEY_ACTION KEY_ACTION_RELEASE

Eventos de Ratn
Son los eventos generados por acciones sobre el ratn dentro de los lmites de un Componente. MOUSE_DOWN MOUSE_UP MOUSE_MOVE MOUSE_ENTER MOUSE_EXIT MOUSE_DRAG

152

Tutorial de Java

Eventos de Barras
Son los eventos generados como respuesta a la manipulacin de barras de desplazamiento (scrollbars). SCROLL_LINE_UP SCROLL_LINE_DOWN SCROLL_PAGE_UP SCROLL_PAGE_DOWN SCROLL_ABSOLUTE

Eventos de Lista
Son los eventos generados al seleccionar elementos de una lista. LIST_SELECT LIST_DESELECT

Eventos Varios
Son los eventos generados en funcin de diversas acciones. ACTION_EVENT LOAD_FILE SAVE_FILE GOT_FOCUS LOST_FOCUS

El applet EventosPrnt.java est diseado para observar los eventos que se producen sobre l. Cada vez que se genera un evento, el applet responde imprimiendo el evento que ha capturado en la lnea de comandos desde donde se ha lanzado el applet. Una vez que se haya compilado el cdigo y cargado el applet en el appletviewer o en un navegador con soporte Java, jugar un poco con el applet. Mover el cursor dentro del applet, picar con el ratn, picar y arrastrar, teclear algo, cambiar el tamao de la ventana y taparla y destaparla con otra. Las acciones anteriores harn que en la ventana en donde se haya lanzado el appletviewer, o en la consola Java en caso de Netscape, vayan apareciendo los textos que indican los eventos que est recibiendo el applet. Lo cierto es que el uso de System.out.println() en un applet es algo que no debera utilizarse, e incluso puede llegar a no funcionar en algunos sistemas, pero tena la ventaja de ser la forma ms fcil de ver los eventos. No obstante, vamos a reescribir el cdigo del applet utilizando una Lista. Una Lista es una lista de cadenas o Strings definidas en java.awt.List. Crearemos una lista de 25 lneas y no permitiremos seleccin mltiple, que son los dos parmetros que necesita el constructor del objeto List. El cdigo EventosList.java que se muestra a continuacin corresponde al anterior ejemplo un poco modificado.

Tutorial de Java

153

import java.awt.*; import java.applet.Applet; public class EventosList extends Applet { List lista; public void init() { lista = new List( 25,false ); add( lista ); lista.addItem( Evento init ); } public void start() { lista.addItem( Evento start ); } public void destroy() { lista.addItem( Evento destroy ); } public void paint( Graphics g ) { lista.addItem( Evento paint ); } public void update( Graphics g ) { lista.addItem( Evento update ); } public boolean mouseUp( Event evt,int x, int y ) { lista.addItem( Evento mouseUp en (+x+,+y+) ); return false; } public boolean mouseDown( Event evt,int x, int y ) { lista.addItem( Evento mouseDown en (+x+,+y+) ); return false; } public boolean mouseDrag( Event evt,int x, int y ) { lista.addItem( Evento mouseDrag en (+x+,+y+) ); return false; } public boolean mouseMove( Event evt,int x, int y ) { lista.addItem( Evento mouseMove en (+x+,+y+) ); return false; } public boolean mouseEnter( Event evt,int x, int y ) { lista.addItem( Evento mouseEnter en (+x+,+y+) ); return false; } public boolean mouseExit( Event evt,int x, int y ) { lista.addItem( Evento mouseExit ); return false;

154

Tutorial de Java

} public boolean keyDown( Event evt,int x ) { lista.addItem( Evento keyDown,carcter +(char)x ); return true; } public void getFocus() { lista.addItem( Evento getFocus ); } public void gotFocus() { lista.addItem( Evento gotFocus ); } public void lostFocus() { lista.addItem( Evento lostFocus ); } }

El applet VisorEventos.java muestra los datos que componen cada evento que se produce. Hemos incorporado un botn y una lista, tal como se puede apreciar en la figura, para poder generar diferentes eventos.

Moviendo el ratn o actuando sobre los dos Componentes, botn y lista, podemos observar los datos que el sistema Java enva en la recoleccin de esos eventos.

Generacin y propagacin de eventos


Tomemos el applet, EventosPro.java, que aparece en la figura siguiente. Consta de dos instancias de la clase Button, embebidas dentro de una instancia de la clase Panel. Esta

Tutorial de Java

155

instancia est a su vez embebida dentro de otra instancia de la clase Panel. Esta ltima instancia de la clase Panel est situada junto a una instancia de la clase TextArea, y ambas estn embebidas dentro de una instancia de la clase Applet.

La figura siguiente presenta los elementos que conforman este applet en forma de rbol, con el TextArea y Button como hojas y la instancia de Applet como raiz.

Cuando un usuario interacta con el applet, el sistema Java crea una instancia de la clase Event y rellena sus datos miembro con la informacin necesaria para describir la accin. Es en ese momento cuando el sistema Java permite al applet controlar el evento. Este control comienza por el Componente que recibe inicialmente el evento (por ejemplo, el botn que ha sido pulsado) y se desplaza hacia arriba en el rbol de Componentes, componente a componente, hasta que alcanza al Contenedor de la raz del rbol. Durante este camino, cada Componente tiene oportunidad de ignorar el evento o reaccionar ante l en una (o ms) de las forma siguientes: Modificar los datos miembros de la instancia de Event Entrar en accin y realizar clculos basados en la informacin contenida en el

156

Tutorial de Java

evento Indicar al sistema Java que el evento no debera propagarse ms arriba en el rbol El sistema Java pasa informacin del evento a un Componente a travs del mtodo handleEvent() del Componente. Todos los mtodos handleEvent() deben ser de la forma:
public boolean handleEvent( Event evt )

Un controlador de eventos solamente necesita una informacin: una referencia a la instancia de la clase Event que contiene la informacin del evento que se ha producido. El valor devuelto por el mtodo handleEvent() es importante. Indica al sistema Java si el evento ha sido o no completamente controlado por el controlador. Un valor true indica que el evento ha sido controlado y que su propagacin debe detenerse. Un valor false indica que el evento ha sido ignorado, o que no ha sido controlado en su totalidad y debe continuar su propagacin hacia arriba en el rbol de Componentes. Veamos la descripcin de una accin con el applet de la figura anterior. El usuario pulsa el botn Uno. El sistema run-time del lenguaje Java capturar la informacin sobre el evento (el nmero de clicks, la localizacin del click, la hora en que se ha producido la pulsacin y el Componente que ha recibido el click) y empaqueta todos esos datos en una instancia de la clase Event. El sistema Java comienza entonces por el Componente que ha sido pulsado (en este caso, el botn Uno) y, a travs de una llamada al mtodo handleEvent() del Componente, ofrece a ste la posibilidad de reaccionar ante el evento. Si el Componente no controla el evento, o no lo hace completamente (indicado por un valor de retorno false), el sistema Java presentar la instancia de Event al siguiente Componente por encima en el rbol (en este caso, una instancia de la clase Panel). El sistema Java contina de este mismo modo hasta que el evento es controlado en su totalidad o ya no hay Componentes a los que informar. En la figura siguiente mostramos el camino recorrido por el evento en su intento de que algn Componente lo controle.

Cada Componente del applet aade una lnea al objeto TextArea indicando que ha recibido un evento. Luego permite que el evento se propague al siguiente Componente. El cdigo del controlador de eventos usado en el ejemplo es el que muestran las siguientes lneas:

Tutorial de Java

157

public boolean handleEvent( Event evt) { if( evt.id == Event.ACTION_EVENT ) ta.appendText( Panel + str + recibe action...\n ); else if( evt.id == Event.MOUSE_DOWN ) ta.appendText( Panel + str + recibe mouse_down...\n ); return super.handleEvent( evt ); }

Mtodos de control de eventos


El mtodo handleEvent() es un lugar para que el programador pueda insertar cdigo para controlar los eventos. A veces, sin embargo, un Componente solamente estar interesado en eventos de un cierto tipo (por ejemplo, eventos del ratn). En estos casos, el programador puede colocar el cdigo en un mtodo de ayuda, en lugar de colocarlo en el mtodo handleEvent(). No hay mtodos de ayuda para ciertos tipos de eventos, aqu est la lista de los que estn disponibles para los programadores:
action( Event evt,Object obj ) gotFocus( Event evt,Object obj ) lostFocus( Event evt,Object obj ) mouseEnter( Event evt,int x,int y ) mouseExit( Event evt,int x,int y ) mouseMove( Event evt,int x,int y ) mouseUp( Event evt,int x,int y ) mouseDown( Event evt,int x,int y ) mouseDrag( Event evt,int x,int y ) keyDown( Event evt,int key ) keyUp( Event evt,int key )

false indicar que el mtodo de ayuda no maneja el evento.


La implementacin del mtodo handleEvent() proporcionada por la clase Component invoca a cada mtodo de ayuda. Por esta razn, es importante que las implementaciones redefinidas del mtodo handleEvent() en clases derivadas, siempre finalicen con la sentencia:
return( super.handleEvent( evt ) );

El siguiente trozo de cdigo ilustra esta regla.


public boolean handleEvent( Event evt ) { if( evt.target instanceof MiBoton ) { // Hace algo... return true; } return( super.handleEvent( evt ) ); }

No seguir esta regla tan simple har que no se invoquen adecuadamente los mtodos de ayuda. El applet EventosRaton.java, que controla los eventos de ratn exclusivamente a

158

Tutorial de Java

travs de cdigo insertado en sus mtodos de ayuda; va dibujando una lnea (rubber band) entre el ltimo punto donde se ha producido un click de ratn y la posicin actual del cursor.

ACTION_EVENT
Algunos de los eventos que ms frecuentemente tendremos que controlar son los siguientes: ACTION_EVENT MOUSE_DOWN KEY_PRESS WINDOW_DESTROY En la documentacin de la clase Event se encuentra toda la lista de eventos que cualquier aplicacin puede necesitar manejar y su documentacin; como ejemplo de uso vamos a detenernos en el primero de ellos, ACTION_EVENT. Como ejemplo del manejo de eventos vamos a ver este evento que se provoca al pulsar un botn, seleccionar un men, etc. Para su control podemos manejarlo en el mtodo handleEvent() o en el mtodo action(). Los dos mtodos anteriores pertenecen a la clase Component por lo que todas las clases derivadas de sta contendrn estos dos mtodos y se pueden sobrecargar para que se ajuste su funcionamiento a lo que requiere nuestra aplicacin. Veamos el siguiente ejemplo, en que se controla este evento a travs del mtodo handleEvent(), que es el mtodo general de manejo de eventos:
public boolean handleEvent( Event evt ) { switch( evt.id ) { case Event.ACTION_EVENT: // evt.arg contiene la etiqueta del botn pulsado // o el item del men que se ha seleccionado if( ( Pulsado +n+ veces ).equals( evt.arg ) ) return( true ); default: return( false ); } }

Pero en este caso, cuando se produce este evento se llama al mtodo action(), que sera:
public boolean action( Event evt,Object arg ) { if( ( Pulsado +n+ veces ).equals( arg ) ) return( true ); return( false ); }

Como se puede comprobar, incluso si las etiquetas cambian se puede recibir el evento. Los ejemplos anteriores corresponden al control de un evento producido por un botn que cambia su etiqueta cada vez que se pulsa. Aunque esta no es la nica forma de manejar eventos; de hecho se puede hacer:

Tutorial de Java

159

if( evt.target == miBoton )

en donde se comparan objetos en lugar de etiquetas.

Mejorar el diseo de Interfaces


La interface de usuario es el aspecto ms importante de una aplicacin, tal como ya hemos repetido. Un diseo pobre de la interface es un grave problema para que el usuario pueda obtener todo el partido posible de la aplicacin. Para ser efectivos, no debemos limitarnos a colocar una serie de botones, etiquetas y barras de desplazamiento sobre la pantalla. Desafortunadamente, nadie ha determinado una reglas correctas para del diseo de una buena interface. Los diseadores del AWT parece que se pusieron como meta principal que las clases del AWT funcionasen correctamente, dejando un poco de lado su apariencia. Sin embargo, han proporcionado suficientes mecanismos para poder alterar la apariencia de los Componentes del AWT, muchas veces no de forma sencilla, pero ah estn para que los programadores podamos alterar la visualizacin de los Componentes sobre nuestro interface. Vamos a ver unas cuantas formas sencillas de alterar y dar un mejor aspecto a nuestros diseos. Cambio de Font de Caracteres Colores de Fondo y Texto Fijar el Tamao Preferido Uso de Insets Habilitar y Deshabilitar Componentes

Cambio de fuentes de caracteres


El font de caracteres con el que se presenta un texto en pantalla influye mucho en el impacto de una interface. Una interface efectiva no debera utilizar una maraa de fuentes, pero s que debera utilizar dos o tres diferentes para aumentar el atractivo y la efectividad de los textos. El applet Fuentes.java, tal como se muestra en la figura, ilustra este extremo.

Utiliza tres tipos de fonts de caracteres (en diferente estilo y diferente tamao) para llamar la atencin del usuario sobre las tres zonas de la interface. La fuente por defecto para todos los Componentes es la fuente Dialog. Java proporciona otras fuentes con propsitos ms

160

Tutorial de Java

especializados, el nmero exacto de fuentes depende de la plataforma, por ello, se puede utilizar el applet ListaFuentes.java para obtener una lista de las fuentes de caracteres disponibles en el sistema.

Su navegador no entiende la marca &ltAPPLET>, la siguiente imagen muestrala lista de fuentes disponible en Windows 95 Cuando un programador necesita presentar un Componente en pantalla, como un objeto TextArea, en una fuente de caracteres distinta a la de defecto, la nueva fuente debe seleccionarse mediante el mtodo setFont():
public void setFont( Font f )

El mtodo setFont() espera como parmetro una fuente. En el siguiente trozo de cdigo vemos cmo se usa:
TextArea ta = new TextArea(); Font f = new Font( Helvetica,Font.ITALIC,12 ); ta.setFont( f );

Este cdigo con ligeras modificaciones funcionar para cualquier Componente. Si se cambia la fuente de un Contenedor, todos los Componentes colocados dentro del Contenedor automticamente adoptarn la nueva fuente de caracteres. El siguiente cdigo, CambioFuentes.java, muestra esta circunstancia:
import java.awt.*; import java.applet.Applet; public class CambioFuentes extends Applet { public static void main( String args[] ) { Frame fr = new Frame( Cambio de Fuentes ); CambioFuentes cf = new CambioFuentes(); Font f = new Font( Helvetica,Font.ITALIC,12 ); fr.setFont( f ); fr.setLayout( new FlowLayout() ); Button b = new Button( Hola ); fr.add( b ); Checkbox cb = new Checkbox( Plsame ); fr.add( cb ); TextArea ta = new TextArea(); fr.add( ta ); fr.pack(); fr.show(); } }

La fuente de caracteres solamente se indica para el objeto Frame, el botn, la caja y el rea de texto tambin utilizarn esta fuente.

Tutorial de Java

161

Colores de fondo y de texto


El impacto visual del color nunca debe ser desestimado cuando se ataca el diseo de una interface de usuario. El color tiende a atraer la visin y puede utilizarse para llamr la atencin sobre una parte importante del interface. En el ejemplo siguiente, el color rojo alrededor del botn hace que la vista se fije inmediatamente en l.

La clase Component proporciona dos mtodos para modificar el color de un Componente. A travs de los mtodos setBackground() y setForeground(), se pueden indicar los colores del fondo y del texto, respectivamente:
public void setBackground( Color c ) public void setForeground( Color c )

Ambos mtodos solamente necesitan un parmetro, un objeto Color. A continuacin mostramos un ejemplo de su uso:
TextArea ta = new TextArea(); ta.setBackground( Color.blue ); ta.setForeground( Color.red );

Este cdigo funcionar con ligeras modificaciones para casi todos los Componentes del AWT. Si se cambia el Color de un Contenedor, todos los Componentes colocados dentro de ese Contenedor, automticamente adoptan el nuevo color. El applet, CambioColor.java, ilustra este punto. El Color solamente se fija para el color de fondo del objeto Frame; el botn, la caja y el rea de texto usarn ese mismo color de fondo.
import java.awt.*; import java.applet.Applet; public class CambioColor extends Applet { public static void main( String args[] ) { Frame fr = new Frame( Cambio de Color ); CambioColor cc = new CambioColor(); fr.setBackground( Color.red ); fr.setLayout( new FlowLayout() ); Button b = new Button( Hola ); fr.add( b ); Checkbox cb = new Checkbox( Plsame ); fr.add( cb ); TextArea ta = new TextArea(); fr.add( ta ); fr.pack(); fr.show(); } }

162

Tutorial de Java

La calidad de soporte del color vara mucho de una plataforma a otra. Bajo Windows 95, la clase Button ignora totalmente los comandos de color y se empea en permanecer bajo un patrn de grises. Por otro lado, el fondo de la clase Label parece ser transparente. Algunos Componentes no se presentan en pantalla con un mismo color para un mismo objeto Color. Bajo Windows 95, un fondo de color naranja aparece como naranja en muchos Componentes (excepto en los botones), pero se presenta como amarillo cuando se trata de objetos TextArea o TextField. El soporte del color en Solaris parece ser mucho ms consistente.

Fijar el tamao preferido


Otro ingrediente importante de una buena interface es el tamao con el cual aparecer un Componente o Contenedor en la pantalla. En el corazn del control de la composicin del interface est el layout manager, que es capaz de fijar el tamao y la posicin de los Componentes que se vayan incorporando al layout que est manejando. Esto, indirectamente, tambin influye en el tamao del Contenedor. En este caso, en lugar de llamar a un mtodo para indicar cul debe ser el tamao de un Componente, hay que derivar una nueva clase del Componente y redefinir el mtodo preferredSize() que devolver el tamao preferido. El layout manager llama al mtodo preferredSize() para determinar cul debe ser el tamao preferido para cada Componente. Hay que redefinir el mtodo:
public Dimension preferredSize()

Uno puede estar tentado a utilizar el mtodo resize() o el mtodo reshape() para especificar un tamao, pero no debe hacerse. Ambos mtodos son usados directamente por el layout manager, y los ajustes de tamao se reclacularn la prxima vez que el layout manager tenga que recomponer la posicin de los Componentes sobre el Contenedor. Las siguientes lneas de cdigo demuestran el uso del mtodo preferredSize(), convenientemente redefinido en una clase derivada. Este mtodo crea un nuevo objeto Dimension con la altura y anchura especificadas y se lo devuelve a quien lo ha llamado (normalmente el layout manager).
public Dimension preferredSize() { return new Dimension( 200,100 ); }

Desde luego, no hay nada para evitar que el tamao preferido de un botn vare dinmicamente, como ocurre en el applet CambioBoton.java, mostrado en la figura:

Tutorial de Java

163

Este applet contiene dos botones. Pulsando sobre uno de ellos se provoca el cambio de tamao en el otro. El mtodo preferredSize() del applet de la figura anterior utiliza la variable dim, que es una variable privada:
public Dimension preferredSize() { return new Dimension( dim ); }

La variable dim puede ser fijada o cambiada va una funcin miembro como la siguiente:
public void newPreferredSize( Dimension dim ) { this.dim = new Dimension( dim ); resize( dim ); }

El Contenedor debe llamar al mtodo anterior antes de su llamada al mtodo layout() para resituar los Componentes despus de que haya cambiado el tamao de uno de ellos.

Uso de Insets
Insets, o borde interior, al igual que el tamao preferido, se puede utilizar para proporcionar a la interface de usuario una mayor ordenacin espacial. El insets de un Contendor, especifica la cantidad de espacio alrededor del borde interior del Contenedor donde no se situar ningn Componente, estar vaco.
El borde interior se especifica para un Contenedor, redefiniendo su mtodo insets():
public Insets insets()

El mtodo insets() no necesita ningn parmetro y devuelve una instancia del objeto Insets. La clase Insets tiene cuatro campos, que especifican el nmero de pixels que constituirn el borde interior desde arriba, izquierda, abajo y derecha, respectivamente. A continuacin mostramos el uso tpico de insets(). El cdigo de este ejemplo indica que los Componentes contenidos en el layout debern estar situados a 5 unidades desde cualquiera de los bordes del Contenedor.
public Insets insets() { return new Insets( 5,5,5,5 ); }

En la figura siguiente se muestra el efecto que provoca en la apariencia del interface el uso de insets().

164

Tutorial de Java

Habilitar y deshabilitar componentes


Los Componentes de una interface de usuario que no estn actualmente disponibles pero pueden estarlo, en funcin de alguna accin del usuario, se dice que estn deshabilitados. Se presentan con su rtulo en gris y no responden a las acciones del usuario. El deshabilitar Componentes es mucho mejor que ocultarlos, porque el usuario puede ver las operaciones que puede realizar, aunque en ese momento no las tenga disponibles. La clase Component proporciona tres mtodos para llevar a cabo la habilitacin y deshabilitacin de Componentes:
public void enable() public void disable() public void enable( boolean condicionBooleana )

Los primeros dos mtodos habilitan y deshabilitan un Componente. El tercer mtodo tambin hace eso dependiendo del valor de un parmetro booleano. Veamos un ejemplo:
Button b = new Button( Plsame ); b.enable(); // o, b.disable(); // o, b.enable( true );

Este cdigo funcionar, con muy pocas modificaciones, sobre cualquier Componente. Para un uso efectivo de estos mtodos, un programa debe monitorizar el estado de la interface de usuario. Como el usuario interacta con la interface, su estado interno cambia. Esto hay que reflejarlo en el estado de los Componentes que pasarn de habilitados a deshabilitados, o viceversa, en funcin de las circunstancias. Tomemos como ejemplo ahora el applet que aparece en la figura siguiente, obtenido de la ejecucin de Habilitar.java:

En este applet, los botones Aadir y Borrar estn deshabilitados hasta que el usuario haya entrado en el campo de texto. Este previene una activacin inadvertida de los dos botones en el caso de que no haya texto en el campo. Tan pronto como se teclee el primer carcter en el campo de texto, se habilitan los dos botones, cada cual asumiendo su propio rol. Si, en cualquier momento, el campo de texto se volviese a quedar vaco, los dos botones volveran a estar deshabilitados Adems de que los Componentes deshabilitados tienen una apariencia visual diferente, tampoco reciben eventos desde el sistema Java; los eventos son inmediatamente propagados al Contenedor en que est situado el Componente deshabilitado.

Tutorial de Java

165

Botn grfico
AWT adolece de herramientas ya desarrolladas para implementar interfaces al uso, con alta proliferacin de imgenes y facilidades para que el programador utilice directamente Componentes. Sin embargo, s proporciona las herramientas suficientes como para que se puedan implementar cualquier tipo de Componentes, o modificar al gusto los ya existentes. Para mostrar un ejemplo, vamos a implementar un botn grfico, BotonGrafico.java Partimos de un botn normal al cual aplicaremos tres imgenes diferentes, para cada uno de los tres estados en que puede encontrarse: pulsado, liberado e inhabilitado. En el fichero fuente, podemos comprobar que las tres imgenes se pueden pasar cmo parmetro en la llamada APPLET, por ejemplo:
<APPLET CODE=BotonGrafico.class WIDTH=100 HEIGHT=50> <PARAM NAME=IMAGEN0 VALUE=boton0.gif> <PARAM NAME=IMAGEN1 VALUE=botonup.gif> <PARAM NAME=IMAGEN2 VALUE=botondn.gif> </APPLET>

Observando el cdigo, se puede comprobar que una vez cargadas la imgenes, solamente se deben controlar los eventos del ratn, para que automticamente se presenten las imgenes del botn adecuadas y responda correctamente.
import java.awt.*; import java.applet.Applet; public class BotonGrafico extends Applet { private MediaTracker tracker; private Image imagen[] = new Image[3]; private boolean BotActivo = false; private boolean BotPulsado = false; private boolean tresImg = false; private int Estado = 0; public void init() { String istr; tracker = new MediaTracker( this ); // Recogemos las tres imgenes de los parmentros de llamada // al applet for( int i=0; i < 3; i++ ) { istr = getParameter( IMAGEN + i ); if( istr == null ) tresImg = false; else { // Registramos las imgenes con el Media Tracker imagen[i] = getImage( getCodeBase(),istr ); tracker.addImage( imagen[i],0 ); try { tracker.waitForAll(); } catch( InterruptedException e ) { System.out.println( Error cargando imagen + i ); }

166

Tutorial de Java

} } } public void start() { repaint(); } public void stop(){ } // Controlamos la pulsacin del ratn public boolean mouseDown( Event evt,int x,int y ) { BotPulsado = true; repaint(); return( true ); } // Controlamos cuando el usuario suelta el botn del ratn public boolean mouseUp( Event evt,int x,int y ) { if( BotPulsado && BotActivo ) { BotPulsado = true; repaint(); } else { BotPulsado = false; repaint(); } return( true ); } // Controlamos cuando el cursor del ratn entra en el // campo de accin del applet // Presentamos un mensaje en la lnea de estado public boolean mouseEnter( Event evt,int x,int y ) { BotActivo = true; showStatus( Tutorial de Java, Boton Grafico ); repaint(); return( true ); } // Controlamos cuando el cursor del ratn abandona el // lugar ocupado por el applet public boolean mouseExit( Event evt,int x,int y ) { BotActivo = false; showStatus( ); repaint(); return( true ); } public void update( Graphics g ) { // Controlamos el estado en que se queda el botn // tras la accin que se haya hecho con el ratn if( !BotActivo )

Tutorial de Java

167

Estado = 0; else if( BotActivo && !BotPulsado ) Estado = 1; else Estado = 2; paint( g ); } public void paint( Graphics g ) { g.drawImage( imagen[Estado],0,0,this ); } }

168

Tutorial de Java

Captulo 8
Objetos Grficos

Grficos

En pginas anteriores ya se ha mostrado cmo escribir applets, cmo lanzarlos y los fundamentos bsicos de la presentacin de informacin sobre ellos. Ahora, pues, querremos hacer cosas ms interesantes que mostrar texto; ya que cualquier pgina HTML puede mostrar texto. Para ello, Java proporciona la clase Graphics, que permite mostrar texto a travs del mtodo drawString(), pero tambin tiene muchos otros mtodos de dibujo. Para cualquier programador, es esencial el entendimiento de la clase Graphics, antes de adentrarse en el dibujo de cualquier cosa en Java. Esta clase proporciona el entorno de trabajo para cualquier operacin grfica que se realice dentro del AWT. Juega dos importantes papeles: por un lado, es el contexto grfico, es decir, contiene la informacin que va a afectar a todas las operaciones grficas, incluyendo los colores de fondo y texto, la fuente de caracteres, la localizacin y dimensiones del rectngulo en que se va a pintar, e incluso dispone de informacin sobre el eventual destino de las operaciones grficas (pantalla o imagen). Por otro lado, la clase Graphics proporciona mtodos que permiten el dibujo de primitivas, figuras y la manipulacin de fonts de caracteres y colores. Tambin hay clases para la manipulacin de imgenes, doble-buffering, etc. Para poder pintar, un programa necesita un contexto grfico vlido, representado por una instancia de la clase Graphics. Pero, como esta clase es abstracta, no se puede instanciar directamente; as que debemos crear un componente y pasarlo al programa como un argumento a los mtodos paint() o update(). Los dos mtodos anteriores, paint() y update(), junto con el mtodo repaint() son los que estn involucrados en la presentacin de grficos en pantalla. El AWT, para reducir el tiempo que necesitan estos mtodos para realizar el repintado en pantalla de grficos, tiene dos axiomas: Primero, el AWT repinta solamente aquellos Componentes que necesitan ser repintados, bien porque estuviesen cubiertos por otra ventana o porque se pida su repintado directamente Segundo, si un Componente estaba tapado y se destapa, el AWT repinta solamente la porcin del Componente que estaba oculta

En la ejecucin del applet que aparece a continuacin, EjemploGraf.java, podemos observar como se realiza este proceso. Ignorar la zona de texto de la parte superior del applet de momento, y centrar la mirada en la parte coloreada. Utilizando otra ventana, tapar y destapar parte de la zona que ocupa el applet. Se observar que solamente el trozo de applet que

Tutorial de Java

169

estaba cubierto es el que se repinta. Yendo un poco ms all, solamente aquellos componentes que estn ocultos y se vuelvan a ver sern los que se repinten, sin tener en cuenta su posicin dentro de la jerarqua de componentes.

La pantalla en Java se incrementa de izquierda a derecha y de arriba hacia abajo, tal como muestra la figura:

Los pixels de la pantalla son pues: posicin 0 + ancho de la pantalla - 1. En los textos, el punto de insercin se encuentra en la lnea base de la primera letra.

Mtodos para Dibujos


Vamos a presentar mtodos para dibujar varias figuras geomtricas. Como estos mtodos funcionan solamente cuando son invocados por una instancia vlida de la clase Graphics, su mbito de aplicacin se restringe a los componentes que se utilicen en los mtodos paint() y update(). Normalmente los mtodos de dibujo de primitivas grficas funcionan por pares: un mtodo pinta la figura normal y el otro pinta la figura rellena.
drawLine( x1,y1,x2,y2 ) drawRect( x,y,ancho,alto )

170

Tutorial de Java

fillRect( x,y,ancho,alto ) clearRect( x,y,ancho.alto ) drawRoundRect( x,y,ancho,alto,anchoArco,altoArco ) fillRoundRect( x,y,ancho,alto,anchoArco,altoArco ) draw3DRect( x,y,ancho,alto,boolean elevado ) fill3DRect( x,y,ancho,alto,boolean elevado ) drawOval( x,y,ancho,alto ) fillOval( x,y,ancho,alto ) drawArc( x,y,ancho,alto,anguloInicio,anguloArco ) fillArc( x,y,ancho,alto,anguloInicio,anguloArco ) drawPolygon( int[] puntosX,int[] puntosY[],numPuntos ) fillPolygon( int[] puntosX,int[] puntosY[],numPuntos ) drawString( string s,x,y ) drawChars( char data[],offset,longitud,x,y ) drawBytes( byte data[],offset,longitud,x,y ) copyArea( xSrc,ySrc,ancho,alto,xDest,yDest )

Lneas
Si se necesita dibujar una lnea, se puede utilizar el mtodo
g.drawLine( x1,y1,ancho,alto );

donde g es una instancia de la clase Graphics. graphics.drawLine(..) tambin sera legal ya que graphics es tambin una instancia de Graphics. Graphics es una clase abstracta por lo que no se pueden crear objetos de esta clase, es decir, la siguiente sentencia es totalmente ilegal:
g = new Graphics();

porque no se puede utilizar new para obtener g. En el cdigo de la aplicacin Cuadrados.java, podemos observar el uso del mtodo de dibujar lneas. Es el ms simple de todos los mtodos grficos, que pinta una lnea recta de un pixel de ancho entre el punto inicial que se indica y el final, calculado en funcin del ancho y alto de la regin donde se pinta. En el siguiente applet, FigLinea.java, podemos ver el mtodo drawLine() en funcionamiento:

Tutorial de Java

171

Rectangulos
Los rectngulos, al igual que los valos, se definen por un punto de inicio de coordenadas x e y y el ancho y el alto de la caja que circunscribir a la figura. Para dibujar un rectngulo en pantalla, basta con llamar al mtodo drawRect() indicando la posicin en que deseamos colocar la esquina superior-izquierda del rectngulo y su ancho y alto. Por ejemplo, el cdigo siguiente, Rect1.java, pinta un rectngulo alrededor del applet que lo contiene:
import java.awt.*; import java.applet.Applet; public class Rect1 extends Applet { int ancho,alto; public void init() { Dimension d = size(); ancho = d.width; alto = d.height; repaint(); } public void paint( Graphics g ) { g.drawRect( 0,0,ancho-1,alto-1 ); } }

// devuelve el tamao del applet

En este caso hemos tenido que utilizar ancho-1 y alto-1, para que fuese visible todo el rectngulo y no solamente la mitad, por causa de la referencia de coordenadas que utiliza Java con respecto a la pantalla. El mtodo drawRect() pinta un rectngulo abierto, si queremos un rectngulo relleno, tendremos que invocar al mtodo fillRect(), que tiene idntica sintaxis. El cdigo siguiente, Rect2.java, pinta un rectngulo relleno en el centro del applet que lo contiene:
import java.awt.*; import java.applet.Applet; public class Rect2 extends Applet { int apAncho; int apAlto; int x,y,ancho,alto; public void init() { Dimension d = size(); apAncho = d.width; apAlto = d.height; ancho = apAncho/3; alto = apAlto/3; x = (apAncho - ancho)/2; y = (apAlto - alto)/2; repaint();

172

Tutorial de Java

} public void paint( Graphics g ) { g.drawRect( 0,0,apAncho-1,apAlto-1 ); g.fillRect( x,y,ancho-1,alto-1 ); } }

Si queremos pintar un montn de rectngulos en posiciones y de tamaos aleatorios, al estilo de Mondrian, tambin podemos hacerlo. Para ello utilizaremos el mtodo Math.random() de la clase Math. Este mtodo devuelve un doble entre 0.0 y 1.0. El mtodo aleatorio() sera:
private int aleatorio( int rango ) { double retornoMath; retornoMath = Math.random(); return( (int)( retornoMath * rango ) ); }

Este mtodo fuerza a que el resultado sea un entero en el rango que nosotros indiquemos. El casting o moldeo se realiza de double a int. El casting en Java es mucho ms seguro que en C u otros lenguajes que permiten castings arbitrarios. En Java solamente se pueden realizar castings que tengan sentido, por ejemplo entre un float y un int; y no podran realizarse un casting, por ejemplo entre un int y un String. Vamos tambin a pintar los rectngulos en diferentes colores, utilizando el mtodo setColor() de la clase Graphics. El AWT predefine suficientes colores, con lo cual, adems de pintar un rectngulo aleatorio, lo mostraremos en un color aleatorio. En el cdigo, Rect3.java, que mostramos, se pinta un rectngulo con tamao, posicin y color aleatorios:
import java.awt.*; import java.applet.Applet; public class Rect3 extends Applet { int apAncho,apAlto; int x,y,ancho,alto; Color color; public void init() { Dimension d = size(); apAncho = d.width; apAlto = d.height; x = aleatorio( apAncho ); y = aleatorio( apAlto ); ancho = aleatorio( apAncho - x ); alto = aleatorio( apAlto - y ); color = new Color( aleatorio(255),aleatorio(255),aleatorio(255) ); repaint(); } public void paint( Graphics g ) { g.setColor( color ); g.drawRect( 0,0,apAncho-1,apAlto-1 ); g.fillRect( x,y,ancho-1,alto-1 );

Tutorial de Java

173

} private int aleatorio( int rango ) { double retornoMath; retornoMath = Math.random(); return( (int)( retornoMath * rango ) ); } }

Si se carga repetidas veces el applet, aparecern diferentes rectngulos, con distintos colores y en distintas posiciones, as que vamos a automatizar ese proceso y a complicar del todo el ejemplo, permitiendo que se pinten multitud de rectngulos coloreados. Debido a que cada uno de los rectngulos va a ser diferente, tendremos que mover los clculos de posicin, tamao y color de cada rectngulo al mtodo paint(). Adems, vamos a permitir especificar en el fichero HTML que lance el applet, el nmero de rectngulos que se van a dibujar. Para ello fijaremos un nmero por defecto y en caso de que se incluya el nmero de rectngulos como parmetro de la invocacin al applet, utilizaremos este ltimo. He aqu el cdigo, Rect4.java, que realiza todo esto:
import java.awt.*; import java.applet.Applet; public class Rect4 extends Applet { int apAncho,apAlto; int x,y,ancho,alto; Color color; int numRect = 100; public void init() { Dimension d = size(); apAncho = d.width; apAlto = d.height; String s = getParameter( Numero ); if( s != null ) numRect = Integer.valueOf( s ).intValue(); repaint(); } public void paint( Graphics g ) { g.setColor( Color.black ); g.drawRect( 0,0,apAncho-1,apAlto-1 ); for( int i=0; i < numRect; i++ ) { x = aleatorio( apAncho ); y = aleatorio( apAlto ); ancho = aleatorio( apAncho - x ); alto = aleatorio( apAlto - y ); color = new Color( aleatorio(255),aleatorio(255), aleatorio(255) ); g.setColor( color );

174

Tutorial de Java

g.fillRect( x,y,ancho-1,alto-1 ); } } private int aleatorio( int rango ) { double retornoMath; retornoMath = Math.random(); return( (int)( retornoMath * rango ) ); } }

Los rectngulos redondeados necesitan dos parmetros adicionales, con respecto al mtodo habitual, para controlar el arco con que se redondearn las esquinas del rectngulo. El mtodo que pinta rectngulos tridimensionales todava necesitan un parmetro ms que indica si el rectngulo estar sobresaliendo o hundindose en la pantalla. En el applet siguiente, FigRectangulo.java, podemos ver y manipular los valores que pueden tomar estos mtodos:

Circulos, elipses
Los mtodos definidos en Java para la realizacin de crculos y elipses, al igual que en la mayora de los lenguajes, reciben como parmetros las coordenadas de la esquina superior-izquierda y el ancho y alto de la caja en la que se circunscribe el crculos o la elipse. En el ejemplo siguiente, Ojos.java, podemos ver la rplica de la aplicacin conocida por los usuarios de Motif, en la que los ojos siguen los movimientos del ratn cuando se encuentra dentro del campo del applet.

Tutorial de Java

175

import java.awt.*; import java.applet.Applet; public class Ojos extends Applet { int Mx = -1; int My = -1; int OjoR1; int OjoR2; int Ancho; int Alto; int OjoIzq; int OjoDch; int OjoPY; Color Pupila = Color.black; Color Iris = Color.green.brighter(); Color Orbita = Color.white; Image Imagen; Graphics OjoCG;

public void init() { setBackground( Color.darkGray ); Dimension d = size(); // Fijamos las variables que nos van a posicionar los // ojos sobre el applet OjoIzq = d.width >> 2; OjoDch = OjoIzq * 3; OjoPY = d.height >> 2; OjoR2 = d.width >> 4; OjoR1 = d.width >> 5; Ancho = ( d.width >> 3 ) + OjoR1; Alto = Ancho >> 1; }

public void update( Graphics g ) { paint( g ); }

176

Tutorial de Java

// Funcion auxiliar, para que no se desmanden los valores // y no se los salgan los ojos de las orbitas int swap( int i,int c ) { if( i > c ) i = c; else if( i < -c ) i = -c; return( i ); }

// Pintamos el ojo sobre el applet void pintaOjo( Graphics g,int x ) { // Fijamos los desplazamientos, las nuevas posiciones de // referencia, en funcion de la posicion del cursor del // raton, determinada por Mx y My int dx = x-Mx; int dy = OjoPY-My; // Pintamos el ojo solamente bordeado, es decir, cerrado if( dx < Ancho && dx > -Ancho && dy < Alto && dy > -Alto ) { g.setColor( getBackground() ); g.fillOval( x-Ancho,OjoPY-Alto,Ancho << 1,Alto << 1 ); g.setColor( getBackground().brighter() ); g.drawOval( x-Ancho,OjoPY-Alto,Ancho << 1,Alto << 1 ); } else { // Pintamos el hueco del ojo, por el que se movera el iris g.setColor( Orbita ); g.fillOval( x-Ancho,OjoPY-Alto,Ancho << 1,Alto << 1 ); int y = OjoPY; dx = swap( dx >> 3,OjoR1 << 1 ); dy = swap( dy >> 5,OjoR1 >> 1 ); if( Mx >= 0 && My >= 0 ) { x -= dx; y -= dy; } // Pintamos el iris, sobre el que se movera la pupila g.setColor( Iris ); g.fillOval( x-OjoR2,y-OjoR2,OjoR2 << 1,OjoR2 << 1 ); if( Mx >= 0 && My >= 0 ) { x -= ( dx >> 1 ); y -= dy; } // Pintamos la pupila dentro del iris g.setColor( Pupila ); g.fillOval( x-OjoR1,y-OjoR1,OjoR1 << 1,OjoR1 << 1 ); }

Tutorial de Java

177

public void paint( Graphics g ) { Dimension d = size(); // La primera vez que se llama a este metodo, todavia no // hay nada, asi que creamos el soporte de los ojos if( Imagen == null || OjoCG == null ) { Imagen = createImage( d.width,d.height >> 1 ); OjoCG = Imagen.getGraphics(); } // Pintamos los ojos OjoCG.setColor( getBackground() ); OjoCG.fillRect( 0,0,d.width,d.height ); pintaOjo( OjoCG,OjoIzq ); pintaOjo( OjoCG,OjoDch ); g.drawImage( Imagen,0,0,this ); }

// Cuando movemos el cursos dentro del applet, recibimos las // coordenadas y repintamos el ojo public boolean mouseMove( Event evt,int x,int y ) { Mx = x; My = y; repaint(); return true; }

// Si nos llega el evento de que el raton se ha salido del applet // ponemos los todo en el centro, con los ojos espantados, // como si se hubiese visto al diablo public boolean mouseExit( Event evt,int x,int y ) { Mx = My = -1; repaint(); return true; } }

Cada uno de los mtodos que nos permiten representar en pantalla elipses y arcos, requieren como parmetros las coordenadas del punto central del valo o arco y el ancho y alto, en valor positivo, del rectngulo que circunscribe al valo o al arco, como hemos dicho. Para pintar arcos, necesitamos dos parmetros adicionales, un ngulo de inicio y un ngulo para el arco; de este modo especificamos el inicio del arco y en tamao del arco en grados (no en radianes). En la figura que sigue mostramos cmo se tiene en cuanta el ngulo a la hora de las especificacin de ngulos en los valores de los parmetros a pasar a los mtodos drawArc() y fillArc().

178

Tutorial de Java

El applet siguiente, FigElipse.java, nos permite manipular el comportamiento de estos mtodos ante los valores que indiquemos en los parmetros que aceptan:

Polgonos
Los polgonos son figuras creadas a partir de una secuencia de segmentos. Cada mtodo que permite dibujar polgonos necesita como parmetros las coordenadas de los puntos donde termina cada uno de los segmentos que constituyen el polgono. Estos puntos se pueden especificar como dos arrays paralelos de enteros, uno conteniendo las coordenadas x de los puntos y otro las coordenadas y; o mediante una instancia de la clase Polygon. Esta clase proporciona el mtodo addPoint(), que permite ir construyendo el polgono punto a punto. El applet, FigPoligono.java, que aparece seguidamente permite ver los mtodos de dibujo de polgonos en accin.

Tutorial de Java

179

Funciones Grficas
A travs de lneas tambin se pueden realizar figuras ms complicadas que resulten de la representacin grfica de funciones matemticas. No pretendemos entrar en complicadas disquisiciones matemticas, sino presentar algunas de las particularidades de los grficos y Java. Vamos a presentar el cdigo siguiente, Grafico1.java, que pintar la curva de la funcin seno de izquierda a derecha de la ventana:
import java.awt.*; import java.applet.Applet; public class Grafico1 extends Applet { int x0,xN,y0,yN; public void init() { Dimension d = size(); x0 = y0 = 0; xN = d.width-1; yN = d.height-1; } public void paint( Graphics g ) { for( int x=x0; x < xN; x++ ) g.drawLine( x,(int)(yN*Math.sin(x)), x+1,(int)(yN*Math.sin(x+1)) ); } }

El corazn del funcionamiento del applet se encuentra en el bucle incluido en el mtodo paint():
for( int x=x0; x < xN; x++ ) g.drawLine( x,(int)(yN*Math.sin(x)),x+1,(int)(yN*Math.sin(x+1)));

El bucle va desplazndose a travs de cada pixel de la pantalla en el eje x, y en cada una de las posiciones, calcula el seno para ese pixel. Tambin calcula el seno del pixel siguiente y pinta una lnea que une esos dos puntos. Debido a que el seno de un nmero real est siempre comprendido entre 1 y -1, escalamos el valor de y con yN. Y por fin hacemos un casting de los valores del seno en el eje y porque son flotantes fundamentalmente y el mtodo drawLine() necesita enteros. El applet anterior funciona correctamente, pero adolece de dos dos problemas: Primero, los senos son operaciones en coma flotante, luego para que el applet tenga realmente utilidad necesitamos utilizar nmeros en coma flotante. Segundo, el sistema de coordenadas que maneja el applet va desde el punto (0,0), de la esquina superior-izquierda hasta la inferiorderecha. Pero el sistema normal de coordenadas Cartesianas empieza a contar desde la esquina inferior-izquierda. Lo que haremos ser mover el origen al centro del applet y transformar las coordenadas del eje y para que vayan en las direcciones adecuadas. La solucin a los dos problemas anteriores puede tomar un montn de caminos diferentes, sin embargo, la clave se encuentra en separar los datos de lo que es la pantalla. Es decir, vamos a realizar nuestros clculos matemticos suponiendo que estamos en el espacio de un rectngulo en coordenadas cartesianas y, por otro lado, tomaremos a la pantalla como un

180

Tutorial de Java

rectngulo de puntos de ancho y tamao fijos. As podremos realizar los clculos en el plano de los ejes cartesianos y presentarlos en el espacio reducido de la ventana del applet. Necesitaremos pues, un mtodo para convertir un punto de la ventana del applet en un punto en el mundo cartesiano, y otro que haga la funcin contraria. El cdigo siguiente, GraficoSeno.java, hace todo esto:
import java.awt.*; import java.applet.Applet; public class GraficoSeno extends Applet { int x0,xN,y0,yN; double xmin,xmax,ymin,ymax; int apAncho,apAlto; public void init() { Dimension d = size(); apAncho = d.width; apAlto = d.height; x0 = y0 = 0; xN = apAncho-1; yN = apAlto-1; xmin = -10.0; xmax = 10.0; ymin = -1.0; ymax = 1.0; } public void paint( Graphics g ) { double x1,y1,x2,y2; int j1,j2; j1 = ValorY( 0 ); for( int i=0; i < apAncho; i++ ) { j2 = ValorY( i+1 ); g.drawLine( i,j1,i+1,j2 ); j1 = j2; } } private int ValorY( int valor ) { double x,y; int retorno; // Cartesianas equivalentes al punto de la pantalla x = (valor * (xmax-xmin) / (apAncho-1)) + xmin; // Calculamos el seno de ese punto y = Math.sin( x ); // Escalamos la coordenada y dentro de los limites de la ventana retorno = (int)( (y-ymin) * (apAlto-1) / (ymax-ymin) ); // Reconvertinos el valor cartesiano a punto de pantalla retorno = apAlto - retorno; return( retorno ); }

Tutorial de Java

181

Ejecutando este applet, podremos observar que lo que se presenta ya se parece muchsimo ms a la imagen que todos conocemos de la onda senoidal.

Fractales
No vamos aqu a proporcionar una clase maestra sobre fractales, sino que vamos a mostrar que Java tambin se puede emplear para la implementacin de algunos ejemplos clsicos de la geometra de fractales. Vamos a realizar por uno sencillito, en el que se trata de un conjunto definido por todos los nmeros reales entre cero y uno, inclusive. Entonces, eliminamos el tercio central de ese conjunto, es decir, todo lo que se encuentre entre el primer tercio y el ltimo, ambos exclusive. Para visualizar lo anterior vamos a utilizar segmentos de lnea. Eliminaremos el tercio medio del segmento, es decir, nos quedaremos con el segmento que va desde el inicio del segmento hasta el segundo noveno, y entre el sptimo noveno y el final; y continuaremos este proceso indefinidamente. La verdad es que parece un poco confusa la explicacin, pero si una imagen vale ms que mil palabras, un buen programa Java vale ms que mil imgenes. Por ello, aqu esta el cdigo Java, Tercio.java, que muestra sucesivas lneas en que se demuestra la explicacin del prrafo anterior.
import java.awt.*; import java.applet.Applet; import java.util.Vector; public class Tercio extendsApplet { int apAncho,apAlto; Vector finPuntos = new Vector(); public void init() { apAncho = size().width; apAlto = size().height; finPuntos.addElement( new Float( 0.0f ) ); finPuntos.addElement( new Float( 1.0f ) );

182

Tutorial de Java

} public void paint( Graphics g ) { float x1,x2; Float tmpFloat; for( int i=0; i < apAncho; i+=5 ) { // Pintamos las lineas for( int j=0; j < finPuntos.size(); j+=2 ) { tmpFloat = (Float)finPuntos.elementAt( j ); x1 = tmpFloat.floatValue(); tmpFloat = (Float)finPuntos.elementAt( j+1 ); x2 = tmpFloat.floatValue(); g.drawLine( Math.round( x1*apAncho ),i, Math.round( x2*apAncho),i ); } // Eliminamos el tercio medio de las lineas BorraSegmentos(); // Comprobamos que no nos salimos de la pantalla tmpFloat = (Float)finPuntos.elementAt( 0 ); x1 = tmpFloat.floatValue(); tmpFloat = (Float)finPuntos.elementAt( 1 ); x2 = tmpFloat.floatValue(); if( Math.round( x1*apAncho ) == Math.round( x2*apAncho ) ) break; } } private void BorraSegmentos() { int indice = 0; int fin = finPuntos.size(); for( int i=0; i < fin; i+=2 ) { BorraTercioMedio( indice,indice+1 ); indice += 4; } } private void BorraTercioMedio( int izqda,int dcha ) { float gap; float x1,x2; Float tmpFloat1,tmpFloat2; tmpFloat1 = (Float)finPuntos.elementAt( izqda ); tmpFloat2 = (Float)finPuntos.elementAt( dcha ); gap = tmpFloat2.floatValue() - tmpFloat1.floatValue(); x1 = tmpFloat1.floatValue() + gap/3.0f; x2 = tmpFloat2.floatValue() - gap/3.0f; finPuntos.insertElementAt( new Float( x2 ),dcha ); finPuntos.insertElementAt( new Float( x1 ),dcha ); } }

Tutorial de Java

183

Aqu vemos el resultado de la ejecucin del applet. Est claro? Desde luego no es la representacin ms perfecta del conjunto, debido a que tenemos que manejar puntos de tamao finito en lugar de puntos matemticos genuinos. Dependiendo de lo larga que sea la ventana del applet, probablemente slo se vean entre seis y doce interacciones antes de necesitar de que los pixels se vuelvan fraccionarios. Es de hacer notar que en este programa Java hemos utilizado el objeto Vector. Debido a que Java no dispone de punteros, este objeto y la clase asociada, disponen de los mtodos necesarios para poder implementar una lista enlazada, que es lo que nosotros hemos utilizado.

Lneas flotantes
El ejemplo de las lneas flotantes es ms difcil de describir que su cdigo. Se ejecuta sobre un bucle infinito y son lneas que se estn moviendo continuamente por la ventana que ocupa el applet, rebotando en los bordes y movindose continuamente. El cdigo del fichero Lineas.java, es el que contiene las sentencias que crean el applet que se visualiza a continuacin. Repasar el cdigo delalgoritmo para entenderlo, que es bastante simple.

import java.awt.*; import java.applet.Applet; public class Lineas extends Applet { int LINEAS = 25; int gSup = 3; int gInf = 3;

184

Tutorial de Java

int gIzq = 2; int gDch = 6; int apAncho,apAlto; int gLineas[][] = new int[LINEAS][4]; public void init() { Dimension d = size(); apAncho = d.width; apAlto = d.height; } public void start() { gLineas[0][0] = aleatorio( apAncho ); gLineas[0][1] = aleatorio( apAlto ); gLineas[0][2] = aleatorio( apAncho ); gLineas[0][3] = aleatorio( apAlto ); for( int i=1; i < LINEAS; i++ ) { CopiaLinea( i,i-1 ); RecalcLinea( i ); } repaint(); } public void paint( Graphics g ) { while( true ) { for( int i=LINEAS-1; i > 0; i ) CopiaLinea( i,i-1 ); RecalcLinea( 0 ); g.setColor( Color.black ); g.drawLine( gLineas[0][0],gLineas[0][1], gLineas[0][2],gLineas[0][3] ); g.setColor( getBackground() ); g.drawLine( gLineas[LINEAS-1][0],gLineas[LINEAS-1][1], gLineas[LINEAS-1][2],gLineas[LINEAS-1][3] ); } } private void CopiaLinea( int desde,int hasta ) { for( int i=0; i < 4; i++ ) gLineas[desde][i] = gLineas[hasta][i]; } public int aleatorio( int rango ) { double retornoMath; retornoMath = Math.random(); return( (int)(retornoMath * rango) ); } private void RecalcLinea( int i ) { gLineas[i][1] += gSup; if( (gLineas[i][1] < 0 ) || (gLineas[i][1] > apAlto) )

Tutorial de Java

185

{ gSup *= -1; gLineas[i][1] += 2*gSup; } gLineas[i][3] += gInf; if( (gLineas[i][3] < 0 ) || (gLineas[i][3] > apAlto) ) { gInf *= -1; gLineas[i][3] += 2*gInf; } gLineas[i][0] += gIzq; if( (gLineas[i][0] < 0 ) || (gLineas[i][0] > apAncho) ) { gIzq *= -1; gLineas[i][0] += 2*gIzq; } gLineas[i][2] += gDch; if( (gLineas[i][2] < 0 ) || (gLineas[i][2] > apAncho) ) { gDch *= -1; gLineas[i][2] += 2*gDch; } } }

Mtodos para imagenes


Los objetos Graphics pueden mostrar imgenes a travs del mtodo:
drawImage( Image img,int x,int y,ImageObserver observador );

Hay que tener en cuenta que el mtodo drawImage() necesita un objeto Image y un objeto ImageObserver. Podemos cargar una imagen desde un fichero de dibujo (actualmente slo se soportan formatos GIF y JPEG) con el mtodo getImage():
Image img = getImage( getDocumentBase(),fichero.gif );

La forma de invocar al mtodo getImage() es indicando un URL donde se encuentre el fichero que contiene la imagen que queremos presentar y el nombre de ese fichero:
getImage( URL directorioImagen,String ficheroImagen );

Un URL comn para el mtodo getImage() es el directorio donde est el fichero HTML. Se puede acceder a esa localizacin a travs del mtodo getDocumentBase() de la clase Applet, como ya se ha indicado. Normalmente, se realiza el getImage() en el mtodo init() del applet y se muestra la imagen cargada en el mtodo paint(), tal como se muestra en el ejemplo siguiente:
public void init() { img = getImage( getDocumentBase(),pepe.gif ); }

186

Tutorial de Java

public void paint( Graphics g ) { g.drawImage( img,x,y,this ); }

En el applet Imagen.java, podemos ver el ejemplo completo. Su ponemos en l la existencia del fichero Imagenes/pepe.gif:
import java.awt.*; import sun.awt.image.URLImageSource; import java.applet.Applet; public class Imagen extends Applet { Imagen pepe; public void init() { pepe = getImage( getDocumentBase(),Imagenes/pepe.gif ); } public void paint( Graphics g ) { g.drawString( pepe,25,25,this ); } }

DOBLE-BUFFERING de grficos
Al mostrar grficos con las tcnicas estndar, las imgenes suelen aparecer a trozos o con parpadeo. Las aplicaciones Java permiten que los programas dibujen en memoria, para luego ir mostrando la imagen completa de forma suave. Este es el proceso conocido como doble-buffering, y tiene dos ventajas fundamentales sobre el proceso normal de pintar o dibujar directamente sobre la pantalla: Primero, el usuario ve aparecer de golpe la imagen en la pantalla. Mientras el usuario est viendo esa imagen, el programa est generando la siguiente para mostrarla de golpe a continuacin, y as una y otra vez. Segundo, la tcnica de doble-buffering involucra un objeto Image, que se puede pasar directamente a varios mtodos. Esta capacidad para manipular objetos Image permite descomponer las rutinas de dibujo en componentes funcionales, en lugar de un enorme mtodo paint(). No obstante, el doble-buffering slo debe usarse para animaciones grficas, no como mtodo normal. Lo usual en otras aplicaciones sera repintar la zona que interese solamente.

Contextos grficos
Para entender el doble-buffering, primero se necesita comprender qu es un contexto grfico. Un contexto grfico es simplemente una estructura de datos que el sistema sabe utilizar como tablero de dibujo, es decir, es la zona en que se va a pintar. Ya hemos visto y utilizado contextos grfico en las declaraciones del mtodo paint():

Tutorial de Java

187

public void paint( Graphics g ) {

El objeto Graphics g es el contexto grfico. Se utiliza g para realizar todo el dibujo en el applet. Por ejemplo:
g.drawString( Hola!,25,25 ); g.drawRect( 15,15,50,10 );

Entonces, Java traduce todo lo que se dibuja en g en imgenes sobre la pantalla. Para realizar doble-buffering, se necesita pues, primero crear un contexto grfico que no es presentado inmediatamente en la pantalla.

Creacin de Contextos Grficos


Crear contextos grficos tiene dos pasos: Crear una imagen vaca con las dimensiones adecuadas y obtener un objeto Graphics de esa imagen. El objeto Graphics que se construye en el segundo paso realiza la funcin de contexto grfico. Por ejemplo, CGrafico.java:
import java.awt.*; import java.applet.Applet; public class CGrafico extends Applet { Image dobleBuffer; Graphics miCG; public void init() { // Inicializa el doble buffer dobleBuffer = createImage( 300,300 ); miCG = dobleBuffer.getGraphics(); // Construye un rea grfica de trabajo miCG .setColor( Color.white ); miCG.fillRect( 0,0,300,300 ); resize( 500,450 ); }

Podemos utilizar miCG para dibujar cualquier cosa. Las imgenes se trazarn en doble buffer. Cuando el dibujo est terminado, se puede presentar el doble buffer en pantalla:
public void paint( Graphics g ) { // Slo se tiene que presentar la imagen del buffer g.drawImage( dobleBuffer,0,0,this ); }

Utilizacin de Contextos Grficos


Una vez definido un contexto grfico, podemos usarlo en cualquier parte de nuestro programa. Por ejemplo, podemos repartir la responsabilidad para dibujar sobre varias funciones:
public void titulo() { // Obtiene la fuente de texto actual y la guardamos Font f = miCG.getFont();

188

Tutorial de Java

// Seleccionamos otra fuente para el ttulo miCG.setFont( new Font( TimesRoman.Font.BOLD,36 ) ); miCG.drawString( Ejemplo de Espiral,15,50 ); miCG.drawString( Crculos,15,90 ); // Recuperamos la fuente original miCG.setFont( f ); } public void espiral() { int x,y; // Dibujamos circulos en los lados horizontales y = 100; for( x=100; x <= 200; x+=10 ) { miCG.drawOval( x,y,20,20 ); miCG.drawOval( x,y+100,20,20 ); } // Ahora en los verticales x = 100; for( y=100; y <= 200; y+=10 ) { miCG.drawOval( x,y,20,20 ); miCG.drawOval( x+100,y,20,20 ); } } public void start() { // Hace el dibujo off-line titulo(); espiral(); // Ahora muestra la imagen de golpe repaint(); }

Nuevas clases para dibujo


La descomposicin funcional de los mtodos para dibujo proporcionados por el doble-buffering se pueden extender a otras clases. Por ejemplo, se puede disear una clase especializada en un tipo de salida (grficos, informes, CAD, etc.). Pasndole el contexto grfico como argumento al constructor, la nueva clase podr construir la imagen. Esta clase puede ser reutilizada en cualquier otro applet que lo necesite. Por ejemplo, el cdigo del fichero fuente EspiralTest.java siguiente muestra la clase Espiral. El applet espiral crea y usa un objeto Espiral. La representacin en pantalla se construye enteramente en la nueva clase:
import java.awt.*; import java.applet.Applet; // Clase de ejemplo para dibujar en contextos grficos class Espiral { // Al ser privada, las variables son slo accesibles por esta clase private int iniX,iniY,iniAncho,iniAlto; private int incremento; private Graphics cg;

Tutorial de Java

189

// constructor. crea una referencia local al buffer grfico y define // el azul como color de los objetos que se dibujen sobre ella public Espiral( Graphics g ) { cg = g; cg.setColor( Color.blue ); } // Establece el tamao y posicin iniciales de la espiral public void setTamInicial( int x,int y,int w,int h ) { iniX = x; iniY = y; iniAncho = w; iniAlto = h; } // Establece el incremento para espaciar los giros public void setTamEspiral( int inc ) { incremento = inc; } // Construye la espiral public void creaEspiral() { int cX = iniX; int cY = iniY; // Dibuja lneas por pares, cubrindose entre s para formar la // espiral cuadrada hasta alcanzar el tamao del incremento while( iniAncho > incremento && iniAlto > incremnto ) { g.drawLine( cX,cY,(cX+iniAncho),cY ); g.drawLine( (cX+iniAncho),Cy,(cX+iniAncho),(cY+iniAlto) ); cX += iniAncho; cY += iniAlto; iniAncho -= incremento; iniAlto -= incremento; g.drawLine( cX,cY,(cX-iniAncho),cY ); g.drawLine( (cX-iniAncho),Cy,(cX-iniAncho),(cY-iniAlto) ); cX -= iniAncho; cY -= iniAlto; iniAncho -= incremento; iniAlto -= incremento; } } } // Esta es la llamada a la clase desde un Applet Html public class EspiralTest extends Applet { Image buffer; Espiral spiral; boolean cargado = false; synchronized public void init() { // crea un buffer donde dibujar buffer = createImage( 300,300 );

190

Tutorial de Java

// crea una nueva espiral spiral = new Espiral( buffer.getGraphics() ); // establece el punto inicial y dimensiones spiral.setTamInicial( 10,10,200,200 ); // establece el incremento spiral.setTamEspiral( 10 ); // construye la espiral spiral.creaEspiral(); } synchronized public void paint( Graphics g ) { g.drawImage( buffer,25,25,this ); } }

La clase MediaTracker
Si nuestro applet tiene que tratar con imgenes almacenadas en ficheros gif/jpeg, tendremos que recurrir a la clase MediaTracker. Esta clase proporciona muchos mtodos para manejar objetos multimedia y grupos de objetos.

Manejo de Imgenes para Animacin


Combinando MediaTracker con la tcnica de doble-buffering, se pueden conseguir animaciones. Estos son algunos mtodos de MediaTracker tiles para ver grficos
void addImage( Image img,int id )

Marca la imagen como parte de un grupo de identificadores id.


boolean checkID( int id )

Comprueba las imgenes pertenecientes al grupo id. Devuelve true si han sido cargadas todas, o false en otro caso (no carga los ficheros que falten). Se utiliza para saber cuando se han cargado todas las imgenes que componen una animacin, antes de echarla a andar sobre la pantalla.
boolean checkID( int id,boolean load )

Comprueba las imgenes pertenecientes al grupo id. Devuelve true si han sido cargadas todas, o false en otro caso. Carga las imgenes que falten si load es true.
boolean checkAll( boolean load )

Comprueba todas las imgenes. Devuelve true si todas estn cargadas, o false en otro caso. Carga las imgenes que falten si load es true.
void waitForID( int id )

Espera a que se carguen todas las imgenes del grupo id.


void waitForAll()

Espera a que se carguen todas las imgenes. Se debera utilizar addImage() con cada imagen que necesite un applet. MediaTracker slo

Tutorial de Java

191

supervisa imgenes asociadas mediante el mtodo addImage(). Si se quiere saber si un determinado grfico ha sido cargado, se pueden utilizar los mtodos check. Si se quiere que un grfico sea cargado antes de hacer cualquier otra cosa, usar los mtodos waitFor. El uso de estos mtodos es especialmente til ya que cuando realizamos la carga de una imagen con getImage(), esta carga se realiza en un thread aparte del de nuestro applet, con lo cual, aunque la imagen se encuentre en el otro lado del mundo, getImage() devuelve inmediatamente el control con lo cual podemos comenzar la ejecucin de la animacin, sin haberse cargado todava todas las imgenes.

Creacin de un Objeto MediaTracker


Los objetos MediaTracker necesitan saber qu ImageObserver ver las imgenes que se estn supervisando. Para los applets, el ImageObserver es el propio applet:
miTracker = new MediaTracker( this );

Ahora ya se puede usar el objeto miTracker para manejar todas las imgenes de este applet.

Ejemplo de animacin
Veamos ahora un ejemplo de animacin, Taza.java. Utilizaremos MediaTracker para asegurarnos de que se cargan todas las imgenes. De este modo, cuando se ejecute la animacin, no se ver parpadeo o que falta algn fotograma. Y el cdigo siguiente es el que corresponde a la animacin anterior:
import java.awt.*; import java.applet.Applet; public class Taza extends Applet { Image Images[]; MediaTracker tracker; int index = 0; int maxAncho,maxAlto; // Componentes off-screen para el doble buffering Image offScrImage; Graphics offScrCG; boolean cargado = false; // Inicializa el applet. Establece el tamao y carga las imgenes public void init() { // Establece el supervisor de imgenes y dimensiones tracker = new MediaTracker( this ); maxAncho = 78; maxAlto = 128; imagenes = new Image[6]; // Establece el doble buffer y cambia el tamao del applet try {

192

Tutorial de Java

offScrImage = createImage( maxAnho,maxAlto ); offScrCG = offScrImage.getGraphics(); offScrCG.setColor( Color.lightGray ); offScrCG.fillRect( 0,0,maxAncho,maxAlto ); resize( maxAncho,maxAlto ); } catch( exception e ) { e.printStackTrace(); } // Carga las imgenes en un array for( int i=0; i < 33; i++ ) { String imageFich = new String( taza+String.valueOf(i+1)+.gif ); imagenes[i] = getImage( getDocumentBase(),imageFich ); // Pasamos esta imagen al tracker tracker.addImage( imagenes[i],i ); } try { // Utilizamos el tracker para asegurar que se // cargaran todas las imgenes tracker.waitForAll(); } catch( InterruptedException e ) { } cargado = true; } // Pinta el fotograma actual public void paint( Graphics g ) { if( cargado ) { // Copia del doble buffer a la pantalla g.drawImage( offScrImage,0,0,this ); // Hacemos una pausa y cogemos la siguiente imagen timerloop(); } } // Establecemos la primera imagen public void start() { index = 0; if( tracker.checkID( index ) ) // Pintamos en el doble buffer offScrCG.drawImage( imagenes[index],0,0,this ); } // Actualiza los fotogramas para que avance la animacin public void timerloop() { // Se asegura que la imagen est presente y la mete en el buffer if( tracker.checkID( index ) ) { // Borra el fondo y obtiene la siguiente imagen

Tutorial de Java

193

offScrCG.fillRect( 0,0,100,100 ); offScrCG.drawImage( imagenes[index],0,0,this ); index++; // Vuelve al principio de la animacin if( index <= images.length ) index = 0; } // Bucle de retardo for( int retardo=0; retardo < 200000; retardo++ ); // Dibujamos el siguiente fotograma repaint(); } }

que tratar con imgenes almacenadas en ficheros gif/jpeg,

Sonido en Java
Java tambin posee mtodos predefinidos para reproducir sonido. El ordenador remoto no necesita tener un reproductor de audio; Java realizar la reproduccin (evidentemente, el ordenador remoto, en donde se ejecuta el applet, necesitar disponer de hardware de sonido).

Reproduccin de sonido
La forma ms fcil de reproducir sonido es a travs del mtodo play():
play( URL directorioSonido,String ficheroSonido );

o, simplemente:
play( URL unURLdeSonido );

Un URL comn para el mtodo play() es el directorio donde est el fichero HTML. Se puede acceder a esa localizacin a travs del mtodo getDocumentBase() de la clase Applet:
play( getDocumentBase(),sonido.au );

para que esto funcione, el fichero de la clase y el fichero sonido.au deberan estar en el mismo directorio. En el applet Sonido.java, podemos ver el ejemplo completo. Su ponemos en l la existencia del fichero sonidos/gong.au:
import java.awt.Graphics; import java.applet.Applet; public class Sonido extends Applet { public void paint( Graphics g ) { g.drawString( Prueba de Sonido,25,25 ); play( getDocumentBase(),sonidos/gong.au ); } }

194

Tutorial de Java

Reproduccin Repetitiva
Se puede manejar el sonido como si de imgenes se tratara. Se pueden cargar y reproducir ms tarde. Para cargar un clip de sonido, se utiliza el mtodo getAudioClip():
AudoClip sonido; sonido = getAudioClip( getDocumentBase(),risas.au );

Una vez que se carga el clip de sonido, se pueden utilizar tres mtodos:
sonido.play();

para reproducir el clip de sonido.


sonido.loop();

para iniciar la reproduccin del clip de sonido y que entre en un blucle de reproduccin, es decir, en una repeticin automtica del clip.
sonido.stop();

para detener el clip de sonido que se encuentre en ese instante en reproduccin. Veamos como en el applet Bucle.java utilizamos estos mtodos para repetir automticamente una pista de audio.
import java.awt.Graphics; import java.applet.Applet; public class Bucle extends Applet { AudioClip sonido; public void init() { sonido = getAudioClip( getDocumentBase(),sonidos/risas.au ); } public void paint( Graphics g ) { g.drawString( Prueba de Repeticin,25,25 ); } public void start() { sonido.loop(); } public void stop() { sonido.stop(); } }

Y, finalmente, el applet ControlSonido.java, nos permite a travs de una interface basada en botones, poder controlar la reproduccin de audioclips. El checkbox de carga se usa como indicador para saber cuando el fichero que contiene el clip de sonido a reproducir, se encuentra ya cargado en el sistema.

Tutorial de Java

195

Entrada por ratn


Una de las caractersticas ms tiles que ofrece Java es el soporte directo de la interactividad. La aplicacin puede reaccionar a los cambios producidos en el ratn, por ejemplo, sin necesidad de escribir ninguna lnea de cdigo para su control, solamente indicando qu se quiere hacer cuando el ratn haga algo. El evento ms comn en el ratn es el click. Este evento es gobernado por dos mtodos: mouseDown() (botn pulsado) y mouseUp() (botn soltado). Ambos mtodos son parte de la clase Applet, pero se necesita definir sus acciones asociadas, de la misma forma que se realiza con init() o con paint().
public boolean mouseDown( Event evt,int x,int y ) { /* ratn pulsado en x,y */ /* hacer algo */ } public boolean mouseUp( Event evt,int x,int y ) { /* ratn soltado en x,y */ /* hacer algo */ }

Para ambos mtodos, las coordenadas x e y de la posicin en que ha ocurrido el evento son relativas a la esquina superior izquierda del applet. En el applet Raton.java, se muestra la utilizacin del ratn para recibir las coordenadas en donde se ha pulsado el ratn, y ser en esa posicin donde se repinte el saludo habitual:
import java.awt.*; import java.applet.Applet; public class Raton extends Applet { int ratonX = 25; int ratonY = 25; public void paint( Graphics g ) { g.drawString( Hola Mundo!,ratonX,ratonY ); } public boolean mouseDown( Event evt,int x, int y ) { ratonX = x; ratonY = y; repaint(); return true; } }

196

Tutorial de Java

Captulo 9
Manejo de las excepciones
import java.awt.*; import java.applet.Applet; public class HolaIte extends Applet { private int i = 0; private String Saludos[] = { Hola Mundo!, HOLA Mundo!, HOLA MUNDO!! }; public void paint( Graphics g ) { g.drawString( Saludos[i],25,25 ); i++; } }

Excepciones en Java

Las excepciones en Java estn destinadas, al igual que en el resto de los lenguajes que las soportan, para la deteccin y correccin de errores. Si hay un error, la aplicacin no debera morirse y generar un core (o un crash en caso del DOS). Se debera lanzar (throw) una excepcin que nosotros deberamos capturar (catch) y resolver la situacin de error. Java sigue el mismo modelo de excepciones que se utiliza en C++. Utilizadas en forma adecuada, las excepciones aumentan en gran medida la robustez de las aplicaciones.

Vamos a mostrar como se utilizan las excepciones, reconvirtiendo nuestro applet de saludo a partir de la versin iterativa de HolaIte.java:

Normalmente, un programa termina con un mensaje de error cuando se lanza una excepcin. Sin embargo, Java tiene mecanismos para excepciones que permiten ver qu excepcin se ha producido e intentar recuperarse de ella. Vamos a reescribir el mtodo paint() de nuestra versin iterativa del saludo:
public void paint( Graphics g ) { try { g.drawString( Saludos[i],25,25 ); } catch( ArrayIndexOutOfBoundsException e ) { g.drawString( Saludos desbordado,25,25 ); } catch( Exception e ) { // Cualquier otra excepcin

Tutorial de Java

197

System.out.println( e.toString() ); } finally { System.out.println( Esto se imprime siempre! ); } i++; }

La palabra clave finally define un bloque de cdigo que se quiere que sea ejecutado siempre, de acuerdo a si se captur la excepcin o no. En el ejemplo anterior, la salida en la consola, con i=4 sera:
Saludos desbordado Esto se imprime siempre!

Generar excepciones en Java


Cuando se produce un error se debera generar, o lanzar, una excepcin. Para que un mtodo en Java, pueda lanzar excepciones, hay que indicarlo expresamente.
void MetodoAsesino() throws NullPointerException,CaidaException

Se pueden definir excepciones propias, no hay por qu limitarse a las predefinidas; bastar con extender la clase Exception y proporcionar la funcionalidad extra que requiera el tratamiento de esa excepcin. Tambin pueden producirse excepciones no de forma explcita como en el caso anterior, sino de forma implcita cuando se realiza alguna accin ilegal o no vlida. Las excepciones, pues, pueden originarse de dos modos: el programa hace algo ilegal (caso normal), o el programa explcitamente genera una excepcin ejecutando la sentencia throw (caso menos normal). La sentencia throw tiene la siguiente forma:
throw ObtejoExcepction;

El objeto ObjetoException es un objeto de una clase que extiende la clase Exception. El siguiente cdigo de ejemplo origina una excepcin de divisin por cero:
class melon { public static void main( String[] a ) { int i=0, j=0, k; k = i/j; } } // Origina un error de division-by-zero

Si compilamos y ejecutamos esta aplicacin Java, obtendremos la siguiente salida por pantalla:
> javac melon.java > java melon java.lang.ArithmeticException: / by zero at melon.main(melon.java:5)

Las excepciones predefinidas, como ArithmeticException, se conocen como excepciones runtime. Actualmente, como todas las excepciones son eventos runtime, sera mejor llamarlas excepciones irrecuperables. Esto contrasta con las excepciones que generamos explci-

198

Tutorial de Java

tamente, que suelen ser mucho menos severas y en la mayora de los casos podemos recuperarnos de ellas. Por ejemplo, si un fichero no puede abrirse, preguntamos al usuario que nos indique otro fichero; o si una estructura de datos se encuentra completa, podremos sobreescribir algn elemento que ya no se necesite.

Excepciones predefinidas
Las excepciones predefinidas y su jerarqua de clases es la que se muestra en la figura:

Tutorial de Java

199

Los nombres de las excepciones indican la condicin de error que representan. Las siguientes son las excepciones predefinidas ms frecuentes que se pueden encontrar:

ArithmeticException
Las excepciones aritmticas son tpicamente el resultado de una divisin por 0:
int i = 12 / 0;

NullPointerException
Se produce cuando se intenta acceder a una variable o mtodo antes de ser definido:
class Hola extends Applet { Image img; paint( Graphics g ) { g.drawImage( img,25,25,this ); } }

IncompatibleClassChangeException
El intento de cambiar una clase afectada por referencias en otros objetos, especficamente cuando esos objetos todava no han sido recompilados.

ClassCastException
El intento de convertir un objeto a otra clase que no es vlida.
y = (Prueba)x; // donde x no es de tipo Prueba

NegativeArraySizeException
Puede ocurrir si hay un error aritmtico al intentar cambiar el tamao de un array.

OutOfMemoryException
No debera producirse nunca! El intento de crear un objeto con el operador new ha fallado por falta de memoria. Y siempre tendra que haber memoria suficiente porque el garbage collector se encarga de proporcionarla al ir liberando objetos que no se usan y devolviendo memoria al sistema.

NoClassDefFoundException
Se referenci una clase que el sistema es incapaz de encontrar.

ArrayIndexOutOfBoundsException
Es la excepcin que ms frecuentemente se produce. Se genera al intentar acceder a un elemento de un array ms all de los lmites definidos inicialmente para ese array.

UnsatisfiedLinkException
Se hizo el intento de acceder a un mtodo nativo que no existe. Aqu no existe un mtodo a.kk

200

Tutorial de Java

class A { native void kk(); }

y se llama a a.kk(), cuando debera llamar a A.kk().

InternalException
Este error se reserva para eventos que no deberan ocurrir. Por definicin, el usuario nunca debera ver este error y esta excepcin no debera lanzarse.

Crear excepciones propias


Tambin podemos lanzar nuestras propias excepciones, extendiendo la clase System.exception. Por ejemplo, consideremos un programa cliente/servidor. El cdigo cliente se intenta conectar al servidor, y durante 5 segundos se espera a que conteste el servidor. Si el servidor no responde, el servidor lanzara la excepcin de time-out:
class ServerTimeOutException extends Exception {} public void conectame( String nombreServidor ) throws Exception { int exito; int puerto = 80; exito = open( nombreServidor,puerto ); if( exito == -1 ) throw ServerTimeOutException; }

Si se quieren capturar las propias excepciones, se deber utilizar la sentencia try:


public void encuentraServidor() { ... try { conectame( servidorDefecto ); catch( ServerTimeOutException e ) { g.drawString( Time-out del Servidor, intentando alternativa, 5,5 ); conectame( servidorAlterno ); } ... }

Cualquier mtodo que lance una excepcin tambin debe capturarla, o declararla como parte de la interface del mtodo. Cabe preguntarse entonces, el porqu de lanzar una excepcin si hay que capturarla en el mismo mtodo. La respuesta es que las excepciones no simplifican el trabajo del control de errores. Tienen la ventaja de que se puede tener muy localizado el control de errores y no tenemos que controlar millones de valores de retorno, pero no van ms all.

Tutorial de Java

201

Capturar excepciones
Las excepciones lanzadas por un mtodo que pueda hacerlo deben recoger en bloque try/ catch o try/finally.
int valor; try { for( x=0,valor = 100; x < 100; x ++ ) valor /= x; } catch( ArithmeticException e ) { System.out.println( Matemticas locas! ); } catch( Exception e ) { System.out.println( Se ha producido un error ); }

try
Es el bloque de cdigo donde se prev que se genere una excepcin. Es como si dijsemos intenta estas sentencias y mira a ver si se produce una excepcin. El bloque try tiene que ir seguido, al menos, por una clusula catch o una clusula finally

catch
Es el cdigo que se ejecuta cuando se produce la excepcin. Es como si dijsemos controlo cualquier excepcin que coincida con mi argumento. En este bloque tendremos que asegurarnos de colocar cdigo que no genere excepciones. Se pueden colocar sentencias catch sucesivas, cada una controlando una excepcin diferente. No debera intentarse capturar todas las excepciones con una sola clusula, como esta:
catch( Excepcion e ) { ...

Esto representara un uso demasiado general, podran llegar muchas ms excepciones de las esperadas. En este caso es mejor dejar que la excepcin se propague hacia arriba y dar un mensaje de error al usuario. Se pueden controlar grupos de excepciones, es decir, que se pueden controlar, a travs del argumento, excepciones semejantes. Por ejemplo:
class Limites extends Exception {} class demasiadoCalor extends Limites {} class demasiadoFrio extends Limites {} class demasiadoRapido extends Limites {} class demasiadoCansado extends Limites {} . . . try { if( temp > 40 ) throw( new demasiadoCalor() );

202

Tutorial de Java

if( dormir < 8 ) throw( new demasiado Cansado() ); } catch( Limites lim ) { if( lim instanceof demasiadoCalor ) { System.out.println( Capturada excesivo calor! ); return; } if( lim instanceof demasiadoCansado ) { System.out.println( Capturada excesivo cansancio! ); return; } } finally System.out.println( En la clausula finally );

La clusula catch comprueba los argumentos en el mismo orden en que aparezcan en el programa. Si hay alguno que coincida, se ejecuta el bloque. El operador instanceof se utiliza para identificar exactamente cual ha sido la identidad de la excepcin.

finally
Es el bloque de cdigo que se ejecuta siempre, haya o no excepcin. Hay una cierta controversia entre su utilidad, pero, por ejemplo, podra servir para hacer un log o un seguimiento de lo que est pasando, porque como se ejecuta siempre puede dejarnos grabado si se producen excepciones y nos hemos recuperado de ellas o no. Este bloque finally puede ser til cuando no hay ninguna excepcin. Es un trozo de cdigo que se ejecuta independientemente de lo que se haga en el bloque try. Cuando vamos a tratar una excepcin, se nos plantea el problema de qu acciones vamos a tomar. En la mayora de los casos, bastar con presentar una indicacin de error al usuario y un mensaje avisndolo de que se ha producido un error y que decida si quiere o no continuar con la ejecucin del programa. Por ejemplo, podramos disponer de un dilogo como el que se presenta en el cdigo siguiente:
public class DialogoError extends Dialog { DialogoError( Frame padre ) { super( padre,true ); setLayout( new BorderLayout() ); // Presentamos un panel con continuar o salir Panel p = new Panel(); p.add( new Button( Continuar? ) ); p.add( new Button( Salir ) ); add( Center,new Label( Se ha producido un error. Continuar? ) ) add( South,p ); }

Tutorial de Java

203

public boolean action( Event evt,Object obj ) { if( Salir.equals( obj ) ) { dispose(); System.exit( 1 ); } return false; } }

Y la invocacin, desde algn lugar en que se suponga que se generarn errores, podra ser como sigue:
try { // Cdigo peligroso } catch( AlgunaExcepcion e ) { VentanaError = new DialogoError( this ); VentanaError.show(); }

Propagacin de excepciones
La clusula catch comprueba los argumentos en el mismo orden en que aparezcan en el programa. Si hay alguno que coincida, se ejecuta el bloque y sigue el flujo de control por el bloque finally (si lo hay) y concluye el control de la excepcin. Si ninguna de las clusulas catch coincide con la excepcin que se ha producido, entonces se ejecutar el cdigo de la clusula finally (en caso de que la haya). Lo que ocurre en este caso, es exactamente lo mismo que si la sentencia que lanza la excepcin no se encontrase encerrada en el bloque try. El flujo de control abandona este mtodo y retorna prematuramente al mtodo que lo llam. Si la llamada estaba dentro del mbito de una sentencia try, entonces se vuelve a intentar el control de la excepcin, y as continuamente. Veamos lo que sucede cuando una excepcin no es tratada en la rutina en donde se produce. El sistema Java busca un bloque try..catch ms all de la llamada, pero dentro del mtodo que lo trajo aqu. Si la excepcin se propaga de todas formas hasta lo alto de la pila de llamadas sin encontrar un controlador especfico para la excepcin, entonces la ejecucin se detendr dando un mensaje. Es decir, podemos suponer que Java nos est proporcionando un bloque catch por defecto, que imprime un mensaje de error y sale. No hay ninguna sobrecarga en el sistema por incorporar sentencias try al cdigo. La sobrecarga se produce cuando se genera la excepcin. Hemos dicho ya que un mtodo debe capturar las excepciones que genera, o en todo caso, declararlas como parte de su llamada, indicando a todo el mundo que es capaz de generar excepciones. Esto debe ser as para que cualquiera que escriba una llamada a ese mtodo est avisado de que le puede llegar una excepcin, en lugar del valor de retorno normal. Esto permite al programador que llama a ese mtodo, elegir entre controlar la excepcin o propagarla hacia arriba en la pila de llamadas. La siguiente lnea de cdigo muestra la forma

204

Tutorial de Java

general en que un mtodo declara excepciones que se pueden propagar fuera de l:


tipo_de_retorno( parametros ) throws e1,e2,e3 { }

Los nombres e1,e2,... deben ser nombres de excepciones, es decir, cualquier tipo que sea asignable al tipo predefinido Throwable. Observar que, como en la llamada al mtodo se especifica el tipo de retorno, se est especificando el tipo de excepcin que puede generar (en lugar de un objeto exception). He aqu un ejemplo, tomado del sistema Java de entrada/salida:
byte readByte() throws IOException; short readShort() throws IOException; char readChar() throws IOException; void writeByte( int v ) throws IOException; void writeShort( int v ) throws IOException; void writeChar( int v ) throws IOException;

Lo ms interesante aqu es que la rutina que lee un char, puede devolver un char; no el entero que se requiere en C. C necesita que se devuelva un int, para poder pasar cualquier valor a un char, y adems un valor extra (-1) para indicar que se ha alcanzado el final del fichero. Algunas de las rutinas Java lanzan una excepcin cuando se alcanza el fin del fichero. En el siguiente diagrama se muestra grficamente cmo se propaga la excepcin que se genera en el cdigo, a travs de la pila de llamadas durante la ejecucin del cdigo:

Cuando se crea una nueva excepcin, derivando de una clase Exception ya existente, se

Tutorial de Java

205

puede cambiar el mensaje que lleva asociado. La cadena de texto puede ser recuperada a travs de un mtodo. Normalmente, el texto del mensaje proporcionar informacin para resolver el problema o sugerir una accin alternativa. Por ejemplo:
class SinGasolina extends Exception { SinGasolina( String s ) { // constructor super( s ); } .... // Cuando se use, aparecer algo como esto try { if( j < 1 ) throw new SinGasolina( Usando deposito de reserva ); } catch( SinGasolina e ) { System.out.println( o.getMessage() ); }

Esto, en tiempo de ejecucin originara la siguiente salida por pantalla:


> Usando deposito de reserva

Otro mtodo que es heredado de la superclase Throwable es printStackTrace(). Invocando a este mtodo sobre una excepcin se volcar a pantalla todas las llamadas hasta el momento en donde se gener la excepcin (no donde se maneje la excepcin). Por ejemplo:
// Capturando una excepcin en un mtodo class testcap { static int slice0[] = { 0,1,2,3,4 }; public static void main( String a[] ) { try { uno(); } catch( Exception e ) { System.out.println( Captura de la excepcion en main() ); e.printStackTrace(); } } static void uno() { try { slice0[-1] = 4; } catch( NullPointerException e ) { System.out.println( Captura una excepcion diferente ); } } }

Cuando se ejecute ese cdigo, en pantalla observaremos la siguiente salida:


> Captura de la excepcion en main() > java.lang.ArrayIndexOutOfBoundsException: -1 at testcap.uno(test5p.java:19) at testcap.main(test5p.java:9)

Con todo el manejo de excepciones podemos concluir que proporciona un mtodo ms seguro para el control de errores, adems de representar una excelente herramienta para organizar en sitios concretos todo el manejo de los errores y, adems, que podemos propor-

206

Tutorial de Java

cionar mensajes de error ms decentes al usuario indicando qu es lo que ha fallado y por qu, e incluso podemos, a veces, recuperarnos de los errores. La degradacin que se produce en la ejecucin de programas con manejo de excepciones est ampliamente compensada por las ventajas que representa en cuanto a seguridad de funcionamiento de esos mismos programas.

Tutorial de Java

207

208

Tutorial de Java

Captulo 10

Threads y Multithreading

Considerando el entorno multithread, cada thread (hilo, flujo de control del programa) representa un proceso individual ejecutndose en un sistema. A veces se les llama procesos ligeros o contextos de ejecucin. Tpicamente, cada thread controla un nico aspecto dentro de un programa, como puede ser supervisar la entrada en un determinado perifrico o controlar toda la entrada/salida del disco. Todos los threads comparten los mismos recursos, al contrario que los procesos en donde cada uno tiene su propia copia de cdigo y datos (separados unos de otros). Grficamente, los threads se parecen en su funcionamiento a lo que muestra la figura siguiente:

Flujo en programas
Programas de flujo nico
Un programa de flujo nico o mono-hilvanado (single-thread) utiliza un nico flujo de control (thread) para controlar su ejecucin. Muchos programas no necesitan la potencia o utilidad de mltiples flujos de control. Sin necesidad de especificar explcitamente que se quiere un nico flujo de control, muchos de los applets y aplicaciones son de flujo nico. Por ejemplo, en nuestra aplicacin estndar de saludo:
public class HolaMundo {

Tutorial de Java

209

static public void main( String args[] ) { System.out.println( Hola Mundo! ); } }

Aqu, cuando se llama a main(), la aplicacin imprime el mensaje y termina. Esto ocurre dentro de un nico thread.

Programas de flujo mltiple

En nuestra aplicacin de saludo, no vemos el thread que ejecuta nuestro programa. Sin embargo, Java posibilita la creacin y control de threads explcitamente. La utilizacin de threads en Java, permite una enorme flexibilidad a los programadores a la hora de plantearse el desarrollo de aplicaciones. La simplicidad para crear, configurar y ejecutar threads, permite que se puedan implementar muy poderosas y portables aplicaciones/applets que no se puede con otros lenguajes de tercera generacin. En un lenguaje orientado a Internet como es Java, esta herramienta es vital. Si se ha utilizado un navegador con soporte Java, ya se habr visto el uso de mltiples threads en Java. Habr observado que dos applet se pueden ejecutar al mismo tiempo, o que puede desplazar la pgina del navegador mientras el applet contina ejecutndose. Esto no significa que el applet utilice mltiples threads, sino que el navegador es multithreaded. Las aplicaciones (y applets) multithreaded utilizan muchos contextos de ejecucin para cumplir su trabajo. Hacen uso del hecho de que muchas tareas contienen subtareas distintas e independientes. Se puede utilizar un thread para cada subtarea. Mientras que los programas de flujo nico pueden realizar su tarea ejecutando las subtareas secuencialmente, un programa multithreaded permite que cada thread comience y termine tan pronto como sea posible. Este comportamiento presenta una mejor respuesta a la entrada en tiempo real. Vamos a modificar nuestro programa de saludo creando tres threads individuales, que imprimen cada uno de ellos su propio mensaje de saludo, MultiHola.java:
// Definimos unos sencillos threads. Se detendrn un rato // antes de imprimir sus nombres y retardos class TestTh extends Thread { private String nombre; private int retardo; // Constructor para almacenar nuestro nombre // y el retardo public TestTh( String s,int d ) { nombre = s; retardo = d; }

210

Tutorial de Java

// El metodo run() es similar al main(), pero para // threads. Cuando run() termina el thread muere public void run() { // Retasamos la ejecucin el tiempo especificado try { sleep( retardo ); } catch( InterruptedException e ) { ; } // Ahora imprimimos el nombre System.out.println( Hola Mundo! +nombre+ +retardo ); } } public class MultiHola { public static void main( String args[] ) { TestTh t1,t2,t3; // Creamos los threads t1 = new TestTh( Thread 1,(int)(Math.random()*2000) ); t2 = new TestTh( Thread 2,(int)(Math.random()*2000) ); t3 = new TestTh( Thread 3,(int)(Math.random()*2000) ); // Arrancamos los threads t1.start(); t2.start(); t3.start(); } }

Y ya ms como espectculo que otra cosa, aunque tambin podemos tomarlo por el lado ilustrativo, vemos a continuacin la elecucin del applet Figuras.java que muestra un montn de crculos, cada uno de ellos ejecutndose en un thread diferente y con distinta prioridad cada uno de ellos. La clase Circulo es la que se utiliza para lanzarla todas las veces que se quiere, de tal forma que cada uno de los crculos presentes en el applet son instancias de la misma clase Circulo.

Creacin y control de THREADS


Creacin de un Thread
Hay dos modos de conseguir threads en Java. Una es implementando la interface Runnable, la otra es extender la clase Thread.

Tutorial de Java

211

La implementacin de la interface Runnable es la forma habitual de crear threads. Las interfaces proporcionan al programador una forma de agrupar el trabajo de infraestructura de una clase. Se utilizan para disear los requerimientos comunes al conjunto de clases a implementar. La interface define el trabajo y la clase, o clases, que implementan la interface realizan ese trabajo. Los diferentes grupos de clases que implementen la interface tendrn que seguir las mismas reglas de funcionamiento. Hay una cuantas diferencias entre interface y clase. Primero, una interface solamente puede contener mtodos abstractos y/o variables estticas y finales (constantes). Las clases, por otro lado, pueden implementar mtodos y contener variables que no sean constantes. Segundo, una interface no puede implementar cualquier mtodo. Una clase que implemente una interface debe implementar todos los mtodos definidos en esa interface. Una interface tiene la posibilidad de poder extenderse de otras interfaces y, al contrario que las clases, puede extenderse de mltiples interfaces. Adems, una interface no puede ser instanciada con el operador new; por ejemplo, la siguiente sentencia no est permitida:
Runnable a = new Runnable(); // No se permite

El primer mtodo de crear un thread es simplemente extender la clase Thread:


class MiThread extends Thread { public void run() { ... }

El ejemplo anterior crea una nueva clase MiThread que extiende la clase Thread y sobrecarga el mtodo Thread.run() por su propia implementacin. El mtodo run() es donde se realizar todo el trabajo de la clase. Extendiendo la clase Thread, se pueden heredar los mtodos y variables de la clase padre. En este caso, solamente se puede extender o derivar una vez de la clase padre. Esta limitacin de Java puede ser superada a travs de la implementacin de Runnable:
public class MiThread implements Runnable { Thread t; public void run() { // Ejecucin del thread una vez creado } }

En este caso necesitamos crear una instancia de Thread antes de que el sistema pueda ejecutar el proceso como un thread. Adems, el mtodo abstracto run() est definido en la interface Runnable tiene que ser implementado. La nica diferencia entre los dos mtodos es que este ltimo es mucho ms flexible. En el ejemplo anterior, todava tenemos oportunidad de extender la clase MiThread, si fuese necesario. La mayora de las clases creadas que necesiten ejecutarse como un thread , implementarn la interface Runnable, ya que probablemente extendern alguna de su funcionalidad a otras clases. No pensar que la interface Runnable est haciendo alguna cosa cuando la tarea se est ejecutando. Solamente contiene mtodos abstractos, con lo cual es una clase para dar idea sobre el diseo de la clase Thread. De hecho, si vemos los fuentes de Java, podremos comprobar que solamente contiene un mtodo abstracto:
package java.lang; public interface Runnable {

212

Tutorial de Java

public abstract void run() ; }

Y esto es todo lo que hay sobre la interface Runnable. Como se ve, una interface slo proporciona un diseo para las clases que vayan a ser implementadas. En el caso de Runnable, fuerza a la definicin del mtodo run(), por lo tanto, la mayor parte del trabajo se hace en la clase Thread. Un vistazo un poco ms profundo a la definicin de la clase Thread nos da idea de lo que realmente est pasando:
public class Thread implements Runnable { ... public void run() { if( tarea != null ) tarea.run() ; } } ... }

De este trocito de cdigo se desprende que la clase Thread tambin implemente la interface Runnable. tarea.run() se asegura de que la clase con que trabaja (la clase que va a ejecutarse como un thread) no sea nula y ejecuta el mtodo run() de esa clase. Cuando esto suceda, el mtodo run() de la clase har que corra como un thread.

Arranque de un Thread
Las aplicaciones ejecutan main() tras arrancar. Esta es la razn de que main() sea el lugar natural para crear y arrancar otros threads. La lnea de cdigo:
t1 = new TestTh( Thread 1,(int)(Math.random()*2000) );

crea un nuevo thread. Los dos argumentos pasados representan el nombre del thread y el tiempo que queremos que espere antes de imprimir el mensaje. Al tener control directo sobre los threads, tenemos que arrancarlos explcitamente. En nuestro ejemplo con:
t1.start();

start(), en realidad es un mtodo oculto en el thread que llama al mtodo run().

Manipulacin de un Thread
Si todo fue bien en la creacin del thread, t1 debera contener un thread vlido, que controlaremos en el mtodo run(). Una vez dentro de run(), podemos comenzar las sentencias de ejecucin como en otros programas. run() sirve como rutina main() para los threads; cuando run() termina, tambin lo hace el thread. Todo lo que queramos que haga el thread ha de estar dentro de run(), por eso cuando decimos que un mtodo es Runnable, nos obliga a escribir un mtodo run(). En este ejemplo, intentamos inmediatamente esperar durante una cantidad de tiempo aleatoria

Tutorial de Java

213

(pasada a travs del constructor):


sleep( retardo );

El mtodo sleep() simplemente le dice al thread que duerma durante los milisegundos especificados. Se debera utilizar sleep() cuando se pretenda retrasar la ejecucin del thread. sleep() no consume recursos del sistema mientras el thread duerme. De esta forma otros threads pueden seguir funcionando. Una vez hecho el retardo, se imprime el mensaje Hola Mundo! con el nombre del thread y el retardo.

Suspensin de un Thread
Puede resultar til suspender la ejecucin de un thread sin marcar un lmite de tiempo. Si, por ejemplo, est construyendo un applet con un thread de animacin, querr permitir al usuario la opcin de detener la animacin hasta que quiera continuar. No se trata de terminar la animacin, sino desactivarla. Para este tipo de control de thread se puede utilizar el mtodo suspend().
t1.suspend();

Este mtodo no detiene la ejecucin permanentemente. El thread es suspendido indefinidamente y para volver a activarlo de nuevo necesitamos realizar una invocacin al mtodo resume():
t1.resume();

Parada de un Thread
El ltimo elemento de control que se necesita sobre threads es el mtodo stop(). Se utiliza para terminar la ejecucin de un thread:
t1.stop();

Esta llamada no destruye el thread, sino que detiene su ejecucin. La ejecucin no se puede reanudar ya con t1.start(). Cuando se desasignen las variables que se usan en el thread, el objeto thread (creado con new) quedar marcado para eliminarlo y el garbage collector se encargar de liberar la memoria que utilizaba. En nuestro ejemplo, no necesitamos detener explcitamente el thread. Simplemente se le deja terminar. Los programas ms complejos necesitarn un control sobre cada uno de los threads que lancen, el mtodo stop() puede utilizarse en esas situaciones. Si se necesita, se puede comprobar si un thread est vivo o no; considerando vivo un thread que ha comenzado y no ha sido detenido.
t1.isAlive();

Este mtodo devolver true en caso de que el thread t1 est vivo, es decir, ya se haya llamado a su mtodo run() y no haya sido parado con un stop() ni haya terminado el mtodo run() en su ejecucin.

214

Tutorial de Java

Arrancar y parar THREADS


Ahora que ya hemos visto por encima como se arrancan, paran y manipulan threads, vamos a mostrar un ejemplo un poco ms grfico, se trata de un contador, cuyo cdigo (App1Thread.java) es el siguiente:
import java.awt.*; import java.applet.Applet; public class App1Thread extends Applet implements Runnable { Thread t; int contador; public void init() { contador = 0; t = new Thread( this ); t.start(); } public void run() { while( true ) { contador++; repaint(); try { t.sleep( 10 ); } catch( InterruptedException e ) { ; }; } } public boolean mouseDown( Event evt,int x,int y ) { t.stop(); return( true ); } public void paint( Graphics g ) { g.drawString( Integer.toString( contador ),10,10 ); System.out.println( Contador = +contador ); } public void stop() { t.stop(); } }

Este applet arranca un contador en 0 y lo incrementa, presentando su salida tanto en la pantalla grfica como en la consola. Una primera ojeada al cdigo puede dar la impresin de que el programa empezar a contar y presentar cada nmero, pero no es as. Una revisin ms profunda del flujo de ejecucin del applet, nos revelar su verdadera identidad. En este caso, la clase App1Thread est forzada a implementar Runnable sobre la clase Applet que extiende. Como en todos los applets, el mtodo init() es el primero que se ejecuta. En init(), la variable contador se inicializa a cero y se crea una nueva instancia de la clase

Tutorial de Java

215

Thread. Pasndole this al constructor de Thread, el nuevo thread ya conocer al objeto que va a correr. En este caso this es una referencia a App1Thread. Despus de que hayamos creado el thread, necesitamos arrancarlo. La llamada a start(), llamar a su vez al mtodo run() de nuestra clase, es decir, a App1Thread.run(). La llamada a start() retornar con xito y el thread comenzar a ejecutarse en ese instante. Observar que el mtodo run() es un bucle infinito. Es infinito porque una vez que se sale de l, la ejecucin del thread se detiene. En este mtodo se incrementar la variable contador, se duerme 10 milisegundos y enva una peticin de refresco del nuevo valor al applet.
Es muy importante dormirse en algn lugar del thread, porque sino, el thread consumir todo el tiempo de la CPU para su proceso y no permitir que entren otros mtodos de otros threads a ejecutarse. Otra forma de detener la ejecucin del thread es hacer una llamada al mtodo stop(). En el contador, el thread se detiene cuando se pulsa el ratn mientras el cursor se encuentre sobre el applet. Dependiendo de la velocidad del ordenador, se presentarn los nmeros consecutivos o no, porque el incremento de la variable contador es independiente del refresco en pantalla. El applet no se refresca a cada peticin que se le hace, sino que el sistema operativo encolar las peticiones y las que sean sucesivas las convertirn en un nico refresco. As, mientras los refescos se van encolando, la variable contador se estar todava incrementando, pero no se visualiza en pantalla.

Suspender y reanudar THREADS


Una vez que se para un thread, ya no se puede rearrancar con el comando start(), debido a que stop() concluir la ejecucin del thread. Por ello, en ver de parar el thread, lo que podemos hacer es dormirlo, llamando al mtodo sleep(). El thread estar suspendido un cierto tiempo y luego reanudar su ejecucin cuando el lmite fijado se alcance. Pero esto no es til cuando se necesite que el thread reanude su ejecucin ante la presencia de ciertos eventos. En estos casos, el mtodo suspend() permite que cese la ejecucin del thread y el mtodo resume() permite que un mtodo suspendido reanude su ejecucin. En la siguiente versin de nuestra clase contador, App2Thread.java, modificamos el applet para que utilice los mtodos suspend() y resume():
public class App2Thread extends Applet implements Runnable { Thread t; int contador; boolean suspendido; ... public boolean mouseDown( Event evt,int x,int y ) { if( suspendido ) t.resume(); else t.suspend(); suspendido = !suspendido; return( true ); } ...

216

Tutorial de Java

Para controlar el estado del applet, hemos introducido la variable suspendido. Diferenciar los distintos estados de ejecucin del applet es importante porque algunos mtodos pueden generar excepciones si se llaman desde un estado errneo. Por ejemplo, si el applet ha sido arrancado y se detiene con stop(), si se intenta ejecutar el mtodo start(), se generar una excepcin IllegalThreadStateException.

Estados de un THREAD
Durante el ciclo de vida de un thread, ste se puede encontrar en diferentes estados. La figura siguiente muestra estos estados y los mtodos que provocan el paso de un estado a otro. Este diagrama no es una mquina de estados finita, pero es lo que ms se aproxima al funcionamiento real de un thread .

Nuevo Thread
La siguiente sentencia crea un nuevo thread pero no lo arranca, lo deja en el estado de Nuevo Thread:
Thread MiThread = new MiClaseThread();

Cuando un thread est en este estado, es simplemente un objeto Thread vaco. El sistema no ha destinado ningn recurso para l. Desde este estado solamente puede arrancarse llamando al mtodo start(), o detenerse definitivamente, llamando al mtodo stop(); la llamada a cualquier otro mtodo carece de sentido y lo nico que provocar ser la generacin de una excepcin de tipo IllegalThreadStateException.

Tutorial de Java

217

Ejecutable
Ahora veamos las dos lnea de cdigo que se presentan a continuacin:
Thread MiThread = new MiClaseThread(); MiThread.start();

La llamada al mtodo start() crear los recursos del sistema necesarios para que el thread puede ejecutarse, lo incorpora a la lista de procesos disponibles para ejecucin del sistema y llama al mtodo run() del thread. En este momento nos encontramos en el estado Ejecutable del diagrama. Y este estado es Ejecutable y no En Ejecucin, porque cuando el thread est aqu no esta corriendo. Muchos ordenadores tienen solamente un procesador lo que hace imposible que todos los threads estn corriendo al mismo tiempo. Java implementa un tipo de scheduling o lista de procesos, que permite que el procesador sea compartido entre todos los procesos o threads que se encuentran en la lista. Sin embargo, para nuestros propsitos, y en la mayora de los casos, se puede considerar que este estado es realmente un estado En Ejecucin, porque la impresin que produce ante nosotros es que todos los procesos se ejecutan al mismo tiempo. Cuando el thread se encuentra en este estado, todas las instrucciones de cdigo que se encuentren dentro del bloque declarado para el mtodo run(), se ejecutarn secuencialmente.

Parado
El thread entra en estado Parado cuando alguien llama al mtodo suspend(), cuando se llama al mtodo sleep(), cuando el thread est bloqueado en un proceso de entrada/salida o cuando el thread utiliza su mtodo wait() para esperar a que se cumpla una determinada condicin. Cuando ocurra cualquiera de las cuatro cosas anteriores, el thread estar Parado. Por ejemplo, en el trozo de cdigo siguiente:
Thread MiThread = new MiClaseThread(); MiThread.start(); try { MiThread.sleep( 10000 ); } catch( InterruptedException e ) { ; }

la lnea de cdigo que llama al mtodo sleep():


MiThread.sleep( 10000 );

hace que el thread se duerma durante 10 segundos. Durante ese tiempo, incluso aunque el procesador estuviese totalmente libre, MiThread no correra. Despus de esos 10 segundos. MiThread volvera a estar en estado Ejecutable y ahora s que el procesador podra hacerle caso cuando se encuentre disponible. Para cada una de los cuatro modos de entrada en estado Parado, hay una forma especfica de volver a estado Ejecutable. Cada forma de recuperar ese estado es exclusiva; por ejemplo, si el thread ha sido puesto a dormir, una vez transcurridos los milisegundos que se

218

Tutorial de Java

especifiquen, l solo se despierta y vuelve a estar en estado Ejecutable. Llamar al mtodo resume() mientras est el thread durmiendo no servira para nada. Los mtodos de recuperacin del estado Ejecutable, en funcin de la forma de llegar al estado Parado del thread, son los siguientes: Si un thread est dormido, pasado el lapso de tiempo Si un thread est suspendido, luego de una llamada al mtodo resume() Si un thread est bloqueado en una entrada/salida, una vez que el comando E/S concluya su ejecucin Si un thread est esperando por una condicin, cada vez que la variable que controla esa condicin vare debe llamarse a notify() o notifyAll()

Muerto
Un thread se puede morir de dos formas: por causas naturales o porque lo maten (con stop()). Un thread muere normalmente cuando concluye de forma habitual su mtodo run(). Por ejemplo, en el siguiente trozo de cdigo, el bucle while es un bucle finito -realiza la iteracin 20 veces y termina-:
public void run() { int i=0; while( i < 20 ) { i++; System.out.println( i = +i ); } }

Un thread que contenga a este mtodo run(), morir naturalmente despus de que se complete el bucle y run() concluya. Tambin se puede matar en cualquier momento un thread, invocando a su mtodo stop(). En el trozo de cdigo siguiente:
Thread MiThread = new MiClaseThread(); MiThread.start(); try { MiThread.sleep( 10000 ); } catch( InterruptedException e ) { ; } MiThread.stop();

se crea y arranca el thread MiThread, lo dormimos durante 10 segundos y en el momento de despertarse, la llamada a su mtodo stop(), lo mata. El mtodo stop() enva un objeto ThreadDeath al thread que quiere detener. As, cuando un thread es parado de este modo, muere asncronamente. El thread morir en el momento en que reciba la excepcin ThreadDeath. Los applets utilizarn el mtodo stop() para matar a todos sus threads cuando el navegador con soporte Java en el que se estn ejecutando le indica al applet que se detengan, por

Tutorial de Java

219

ejemplo, cuando se minimiza la ventana del navegador o cuando se cambia de pgina.

El mtodo isAlive()
La interface de programacin de la clase Thread incluye el mtodo isAlive(), que devuelve true si el thread ha sido arrancado (con start()) y no ha sido detenido (con stop()). Por ello, si el mtodo isAlive() devuelve false, sabemos que estamos ante un Nuevo Thread o ante un thread Muerto. Si nos devuelve true, sabemos que el thread se encuentra en estado Ejecutable o Parado. No se puede diferenciar entre Nuevo Thread y Muerto, ni entre un thread Ejecutable o Parado.

Scheduling
Java tiene un Scheduler, una lista de procesos, que monitoriza todos los threads que se estn ejecutando en todos los programas y decide cuales deben ejecutarse y cuales deben encontrarse preparados para su ejecucin. Hay dos caractersticas de los threads que el scheduler identifica en este proceso de decisin. Una, la ms importante, es la prioridad del thread; la otra, es el indicador de demonio. La regla bsica del scheduler es que si solamente hay threads demonio ejecutndose, la Mquina Virtual Java (JVM) concluir. Los nuevos threads heredan la prioridad y el indicador de demonio de los threads que los han creado. El scheduler determina qu threads debern ejecutarse comprobando la prioridad de todos los threads, aquellos con prioridad ms alta dispondrn del procesador antes de los que tienen prioridad ms baja. El scheduler puede seguir dos patrones, preemptivo y no-preemptivo . Los schedulers preemtivos proporcionan un segmento de tiempo a todos los threads que estn corriendo en el sistema. El scheduler decide cual ser el siguiente thread a ejecutarse y llama a resume() para darle vida durante un perodo fijo de tiempo. Cuando el thread ha estado en ejecucin ese perodo de tiempo, se llama a suspend() y el siguiente thread en la lista de procesos ser relanzado (resume()). Los schedulers no-preemtivos deciden que thread debe correr y lo ejecutan hasta que concluye. El thread tiene control total sobre el sistema mientras est en ejecucin. El mtodo yield() es la forma en que un thread fuerza al scheduler a comenzar la ejecucin de otro thread que est esperando. Dependiendo del sistema en que est corriendo Java, el scheduler ser preemtivo o no-preemptivo. En el siguiente ejemplo, SchThread.java, mostramos la ejecucin de dos threads con diferentes prioridades. Un thread se ejecuta a prioridad ms baja que el otro. Los threads incrementarn sus contadores hasta que el thread que tiene prioridad ms alta alcance al contador que corresponde a la tarea con ejecucin ms lenta.

220

Tutorial de Java

Prioridades, demonios...
Prioridades
El scheduler determina el thread que debe ejecutarse en funcin de la prioridad asignada a cada uno de ellos. El rango de prioridades oscila entre 1 y 10. La prioridad por defecto de un thread es Thread.NORM_PRIORITY, que tiene asignado un valor de 5. Hay otras dos variables estticas disponibles, que son Thread.MIN_PRORITY , fijada a 1, y Thread.MAX_PRIORITY, aque tiene un valor de 10. El mtodo getPriority() puede utilizarse para conocer el valor actual de la prioridad de un thread.

Threads Demonio
Los threads demonio tambin se llaman servicios, porque se ejecutan, normalmente, con prioridad baja y proporcionan un servicio bsico a un programa o programas cuando la actividad de la mquina es reducida. Un ejemplo de thread demonio que est ejecutndose continuamente es el recolector de basura (garbage collector). Este thread, proporcionado por la Mquina Virtual Java, comprueba las variables de los programas a las que no se accede nunca y libera estos recursos, devolvindolos al sistema. Un thread puede fijar su indicador de demonio pasando un valor true al mtodo setDaemon(). Si se pasa false a este mtodo, el thread ser devuelto por el sistema como un thread de usuario. No obstante, esto ltimo debe realizarse antes de que se arranque el thread (start()).

Diferencia de threads con fork()


fork() en Unix crea un proceso hijo que tiene su propia copia de datos y cdigo del padre. Esto funciona correctamente si estamos sobrados de memoria y disponemos de una CPU poderosa, y siempre que mantengamos el nmero de procesos hijos dentro de un lmite manejable, porque se hace un uso intensivo de los recursos del sistema. Los applets Java no pueden lanzar ningn proceso en el cliente, porque eso sera una fuente de inseguridad y no est permitido. Las aplicaciones y los applets deben utilizar threads.
La multi-tarea pre-emptiva tiene sus problemas. Un thread puede interrumpir a otro en cualquier momento, de ah lo de pre-emptive. Imaginarse lo que pasara si un thread est escribiendo en un array, mientras otro thread lo interrumpe y comienza a escribir en el mismo array. Los lenguajes como C y C++ necesitan de las funciones lock() y unlock() para antes y despus de leer o escribir datos. Java tambin funciona de este modo, pero oculta el bloqueo de datos bajo la sentencia synchronized:
synchronized int MiMetodo();

Otro rea en que los threads son muy tiles es en los interfaces de usuario. Permiten incrementar la respuesta del ordenador ante el usuario cuando se encuentra realizando complicados clculos y no puede atender a la entrada de usuario. Estos clculos se pueden reali-

Tutorial de Java

221

zar en segundo plano, o realizar varios en primer plano (msica y animaciones) sin que se d apariencia de prdida de rendimiento.

Ejemplo de animacin
Este es un ejemplo de un applet, Animacion.java, que crea un thread de animacin que nos presenta el globo terrqueo en rotacin. Aqu podemos ver que estamos creando un thread de s mismo, concurrencia. Adems, animacion.start() llama al start() del thread, no del applet, que automticamente llamar a run():
import java.awt.*; import java.applet.Applet; public class Animacion extends Applet implements Runnable { Image imagenes[]; MediaTracker tracker; int indice = 0; Thread animacion; int maxAncho,maxAlto; Image offScrImage; // Componente off-screen para doble buffering Graphics offScrGC; // Nos indicar si ya se puede pintar boolean cargado = false; // Inicializamos el applet, establecemos su tamao y // cargamos las imgenes public void init() { // Establecemos el supervisor de imgenes tracker = new MediaTracker( this ); // Fijamos el tamao del applet maxAncho = 100; maxAlto = 100; imagenes = new Image[36]; // Establecemos el doble buffer y dimensionamos el applet try { offScrImage = createImage( maxAncho,maxAlto ); offScrGC = offScrImage.getGraphics(); offScrGC.setColor( Color.lightGray ); offScrGC.fillRect( 0,0,maxAncho,maxAlto ); resize( maxAncho,maxAlto ); } catch( Exception e ) { e.printStackTrace(); } // Cargamos las imgenes en un array for( int i=0; i < 36; i++ ) { String fichero = new String( Tierra+String.valueOf(i+1)+.gif ); imagenes[i] = getImage( getDocumentBase(),fichero );

222

Tutorial de Java

// Registramos las imgenes con el tracker tracker.addImage( imagenes[i],i ); } try { // Utilizamos el tracker para comprobar que todas las // imgenes estn cargadas tracker.waitForAll(); } catch( InterruptedException e ) { ; } cargado = true; } // Pintamos el fotograma que corresponda public void paint( Graphics g ) { if( cargado ) g.drawImage( offScrImage,0,0,this ); } // Arrancamos y establecemos la primera imagen public void start() { if( tracker.checkID( indice ) ) offScrGC.drawImage( imagenes[indice],0,0,this ); animacion = new Thread( this ); animacion.start(); } // Aqu hacemos el trabajo de animacin // Muestra una imagen, para, muestra la siguiente... public void run() { // Obtiene el identificador del thread Thread thActual = Thread.currentThread(); // Nos aseguramos de que se ejecuta cuando estamos en un // thread y adems es el actual while( animacion != null && animacion == thActual ) { if( tracker.checkID( indice ) ) { // Obtenemos la siguiente imagen offScrGC.drawImage( imagenes[indice],0,0,this ); indice++; // Volvemos al principio y seguimos, para el bucle if( indice >= imagenes.length ) indice = 0; } // Ralentizamos la animacin para que parezca normal try { animacion.sleep( 200 ); } catch( InterruptedException e ) { ; } // Pintamos el siguiente fotograma repaint();

Tutorial de Java

223

} } }

En el ejemplo podemos observar ms cosas. La variable thActual es propia de cada thread que se lance, y la variable animacion la estarn viendo todos los threads. No hay duplicidad de procesos, sino que todos comparten las mismas variables; cada thread, sin embargo, tiene su pila local de variables, que no comparte con nadie y que son las que estn declaradas dentro de las llaves del mtodo run(). La excepcin InterruptedExcepcion salta en el caso en que se haya tenido al thread parado ms tiempo del debido. Es imprescindible recoger esta excepcin cuando se estn implementando threads, tanto es as, que en el caso de no recogerla, el compilador generar un error.

Comunicaciones entre THREADS


Otra clave para el xito y la ventaja de la utilizacin de mltiples threads en una aplicacin, o aplicacin multithreaded, es que pueden comunicarse entre s. Se pueden disear threads para utilizar objetos comunes, que cada thread puede manipular independientemente de los otros threads. El ejemplo clsico de comunicacin de threads es un modelo productor/consumidor. Un thread produce una salida, que otro thread usa (consume), sea lo que sea esa salida. Vamos entonces a crear un productor, que ser un thread que ir sacando caracteres por su salida; crearemos tambin un consumidor que ira recogiendo los caracteres que vaya sacando el productor y un monitor que controlar el proceso de sincronizacin entre los threads. Funcionar como una tubera, insertando el productor caracteres en un extremos y leyndolos el consumidor en el otro, con el monitor siendo la propia tubera.

Productor
El productor extender la clase Thread, y su cdigo es el siguiente:
class Productor extends Thread { private Tuberia tuberia; private String alfabeto = ABCDEFGHIJKLMNOPQRSTUVWXYZ; public Productor( Tuberia t ) {

224

Tutorial de Java

// Mantiene una copia propia del objeto compartido tuberia = t; } public void run() { char c; // Mete 10 letras en la tubera for( int i=0; i < 10; i++ ) { c = alfabeto.charAt( (int)(Math.random()*26 ) ); tuberia.lanzar( c ); // Imprime un registro con lo aadido System.out.println( Lanzado +c+ a la tuberia. ); // Espera un poco antes de aadir ms letras try { sleep( (int)(Math.random() * 100 ) ); } catch( InterruptedException e ) { ; } } } }

Notar que creamos una instancia de la clase Tuberia, y que se utiliza el mtodo tuberia.lanzar() para que se vaya construyendo la tubera, en principio de 10 caracteres.

Consumidor
Veamos ahora el cdigo del consumidor, que tambin extender la clase Thread:
class Consumidor extends Thread { private Tuberia tuberia; public Consumidor( Tuberia t ) { // Mantiene una copia propia del objeto compartido tuberia = t; } public void run() { char c; // Consume 10 letras de la tubera for( int i=0; i < 10; i++ ) { c = tuberia.recoger(); // Imprime las letras retiradas System.out.println( Recogido el caracter +c ); // Espera un poco antes de coger ms letras try { sleep( (int)(Math.random() * 2000 ) ); } catch( InterruptedException e ) { ; } } } }

Tutorial de Java

225

En este caso, como en el del productor, contamos con un mtodo en la clase Tuberia, tuberia.recoger(), para manejar la informacin.

Monitor
Una vez vistos el productor de la informacin y el consumidor, nos queda por ver qu es lo que hace la clase Tuberia. Lo que realiza la clase Tuberia, es una funcin de supervisin de las transacciones entre los dos threads, el productor y el consumidor. Los monitores, en general, son piezas muy importantes de las aplicaciones multithreaded, porque mantienen el flujo de comunicacin entre los threads.
class Tuberia { private char buffer[] = new char[6]; private int siguiente = 0; // Flags para saber el estado del buffer private boolean estaLlena = false; private boolean estaVacia = true; // Mtodo para retirar letras del buffer public synchronized char recoger() { // No se puede consumir si el buffer est vaco while( estaVacia == true ) { try { wait(); // Se sale cuando estaVacia cambia a false } catch( InterruptedException e ) { ; } } // Decrementa la cuenta, ya que va a consumir una letra siguiente; // Comprueba si se retir la ltima letra if( siguiente == 0 ) estaVacia = true; // El buffer no puede estar lleno, porque acabamos de consumir estaLlena = false; notify(); // Devuelve la letra al thread consumidor return( buffer[siguiente] ); } // Mtodo para aadir letras al buffer public synchronized void lanzar( char c ) { // Espera hasta que haya sitio para otra letra while( estaLlena == true ) { try { wait(); // Se sale cuando estaLlena cambia a false } catch( InterruptedException e ) { ; } }

226

Tutorial de Java

// Aade una letra en el primer lugar disponible buffer[siguiente] = c; // Cambia al siguiente lugar disponible siguiente++; // Comprueba si el buffer est lleno if( siguiente == 6 ) estaLlena = true; estaVacia = false; notify(); } }

En la clase Tuberia vemos dos caractersticas importantes: los miembros dato (buffer[]) son privados, y los mtodos de acceso (lanzar() y recoger()) son sincronizados. Aqu vemos que la variable estaVacia es un semforo, como los de toda la vida. La naturaleza privada de los datos evita que el productor y el consumidor accedan directamente a stos. Si se permitiese el acceso directo de ambos threads a los datos, se podran producir problemas; por ejemplo, si el consumidor intenta retirar datos de un buffer vaco, obtendr excepciones innecesarias, o se bloquear el proceso. Los mtodos sincronizados de acceso impiden que los productores y consumidores corrompan un objeto compartido. Mientras el productor est aadiendo una letra a la tubera, el consumidor no la puede retirar y viceversa. Esta sincronizacin es vital para mantener la integridad de cualquier objeto compartido. No sera lo mismo sincronizar la clase en vez de los mtodos, porque esto significara que nadie puede acceder a las variables de la clase en paralelo, mientras que al sincronizar los mtodos, s pueden acceder a todas las variables que estn fuera de los mtodos que pertenecen a la clase. Se pueden sincronizar incluso variables, para realizar alguna accin determinada sobre ellas, por ejemplo:
sincronized( p ) { // aqu se colocara el cdigo // los threads que estn intentando acceder a p se pararn // y generarn una InterruptedException }

El mtodo notify() al final de cada mtodo de acceso avisa a cualquier proceso que est esperando por el objeto, entonces el proceso que ha estado esperando intentar acceder de nuevo al objeto. En el mtodo wait() hacemos que el thread se quede a la espera de que le llegue un notify(), ya sea enviado por el thread o por el sistema. Ahora que ya tenemos un productor, un consumidor y un objeto compartido, necesitamos una aplicacin que arranque los threads y que consiga que todos hablen con el mismo objeto que estn compartiendo. Esto es lo que hace el siguiente trozo de cdigo, del fuente TubTest.java:
class TubTest { public static void main( String args[] ) { Tuberia t = new Tuberia(); Productor p = new Productor( t ); Consumidor c = new Consumidor( t );

Tutorial de Java

227

p.start(); c.start(); } }

Compilando y ejecutando esta aplicacin, podremos observar nuestro modelo el pleno funcionamiento.

Monitorizacin del Productor


Los programas productor/consumidor a menudo emplean monitorizacin remota, que permite al consumidor observar el thread del productor interaccionando con un usuario o con otra parte del sistema. Por ejemplo, en una red, un grupo de threads productores podran trabajar cada uno en una workstation. Los productores imprimiran documentos, almacenando una entrada en un registro (log). Un consumidor (o mltiples consumidores) podra procesar el registro y realizar durante la noche un informe de la actividad de impresin del da anterior. Otro ejemplo, a pequea escala podra ser el uso de varias ventanas en una workstation. Una ventana se puede usar para la entrada de informacin (el productor), y otra ventana reaccionara a esa informacin (el consumidor). Peer, es un observador general del sistema.

228

Tutorial de Java

Captulo 11
Escribir cdigo Java

Cdigo Nativo

Un mtodo nativo es un mtodo Java (una instancia de un objeto o una clase) cuya implementacin se ha realizado en otro lenguaje de programacin, por ejemplo, C. Vamos a ver cmo se integran mtodos nativos en clases Java. Actualmente, el lenguaje Java solamente proporciona mecanismos para integrar cdigo C en programas Java. Veamos pues los pasos necesarios para mezclar cdigo nativo C y programas Java. Recurriremos (Cmo no!) a nuestro saludo; en este caso, el programa HolaMundo tiene dos clases Java: la primera implementa el mtodo main() y la segunda, HolaMundo, tiene un mtodo nativo que presenta el mensaje de saludo. La implementacin de este segundo mtodo la realizaremos en C. Las acciones que debemos realizar, para conseguir que nuestra nueva versin del saludo funcione, sern las que desarrollaremos en las pginas siguientes del Tutorial.

En primer lugar, debemos crear una clase Java, HolaMundo, que declare un mtodo nativo. Tambin debemos crear el programa principal que cree el objeto HolaMundo y llame al mtodo nativo. Las siguientes lneas de cdigo definen la clase HolaMundo, que consta de un mtodo y un segmento esttico de cdigo:
class HolaMundo { public native void presentaSaludo(); static { System.loadLibrary( hola ); } }

Podemos decir que la implementacin del mtodo presentaSaludo() de la clase HolaMundo est escrito en otro lenguaje, porque la palabra reservada native aparece como parte de la definicin del mtodo. Esta definicin, proporciona solamente la definicin para presentaSaludo() y no porporciona ninguna implementacin para l. La implementacin la proporcionaremos desde un fichero fuente separado, escrito en lenguaje C. La definicin para presentaSaludo() tambin indica que el mtodo es un mtodo pblico, no acepta argumentos y no devuelve ningn valor. Al igual que cualquier otro mtodo, los m-

Tutorial de Java

229

todos nativos deben estar definidos dentro de una clase Java. El cdigo C que implementa el mtodo presentaSaludo() debe ser compilado en una librera dinmica y cargado en la clase Java que lo necesite. Esta carga, mapea la implementacin del mtodo nativo sobre su definicin. El siguiente bloque de cdigo carga la librera dinmica, en este caso hola. El sistema Java ejecutar un bloque de cdigo esttico de la clase cuando la cargue. Todo el cdigo anterior forma parte del fichero HolaMundo.java, que contiene la clase HolaMundo. En un fichero separado, Main.java, vamos a crear una aplicacin Java que instancie a la clase HolaMundo y llame al mtodo nativo presentaSaludo().
class Main { public static void main( String args[] ) { new HolaMundo().presentaSaludo(); } }

Como se puede observar, llamamos al mtodo nativo del mismo modo que a cualquier otro mtodo Java; aadimos el nombre del mtodo al final del nombre del objeto con un punto (.). El conjunto de parntesis que sigue al nombre del mtodo encierra los argumentos que se le pasen. En este caso, el mtodo presentaSaludo() no recibe ningn tipo de argumento.

Compilar el Cdigo Java


Utilizaremos ahora el compilador javac para compilar el cdigo Java que hemos desarrollado. Compilaremos los dos ficheros fuentes de cdigo Java que hemos creado, tecleando los siguientes comandos:
> javac HolaMundo.java > javac Main.java

Crear el fichero de cabecera


Ahora debemos utilizar la aplicacin javah para conseguir el fichero de cabecera .h. El fichero de cabecera define una estructura que representa la clase HolaMundo sobre cdigo C y proporciona la definicin de una funcin C para la implementacin del mtodo nativo presentaSaludo() definido en ese clase. Ejecutamos javah sobre la clase HolaMundo, con el siguiente comando:
> javah HolaMundo

Por defecto, javah crear el nuevo fichero .h en el mismo directorio en que se encuentra el fichero .class, obtenido al compilar con javac el cdigo fuente Java correspondiente a la clase. El fichero que crear, ser un fichero de cabecera del mismo nombre que la clase y con extensin .h. Por ejemplo, el comando anterior habr creado el fichero HolaMundo.h,

230

Tutorial de Java

cuyo contenido ser el siguiente:


/* DO NOT EDIT THIS FILE - it is machine generated */ #include <native.h> /* Header for class HolaMundo */ #ifndef _Included_HolaMundo #define _Included_HolaMundo typedef struct ClassHolaMundo { char PAD; /* ANSI C requires structures to have a least one member */ } ClassHolaMundo; HandleTo(HolaMundo); #ifdef __cplusplus extern C { #endif __declspec(dllexport) void HolaMundo_presentaSaludo(struct HHolaMundo *); #ifdef __cplusplus } #endif #endif

Este fichero de cabecera contiene la definicin de una estructura llamada ClassHolaMundo. Los miembros de esta estructura son paralelos a los miembros de la clase Java correspondiente; es decir, los campos en la estructura corresponden a las variables de la clase. Pero como HolaMundo no tiene ninguna variable, la estructura se encuentra vaca. Se pueden utilizar los miembros de la estructura para referenciar a variables instanciadas de la clase desde las funciones C. Adems de la estructura C similar a la clase Java, vemos que la llamada de la funcin C est declarada como:
extern void HolaMundo_presentaSaludo( struct HHolaMundo *);

Esta es la definicin de la funcin C que deberemos escribir para implementar el mtodo nativo presentaSaludo() de la clase HolaMundo. Debemos utilizar esa definicin cuando lo implementemos. Si HolaMundo llamase a otros mtodos nativos, las definiciones de las funciones tambin apareceran aqu. El nombre de la funcin C que implementa el mtodo nativo est derivado del nombre del paquete, el nombre de la clase y el nombre del mtodo nativo. As, el mtodo nativo presentaSaludo() dentro de la clase HolaMundo es HolaMundo_presentaSaludo(). En este ejemplo, no hay nombre de paquete porque HolaMundo se considera englobado dentro del paquete por defecto. La funcin C acepta un parmetro, aunque el mtodo nativo definido en la clase Java no acepte ninguno. Se puede pensar en este parmetro como si fuese la variable this de C++. En nuestro caso, ignoramos el parmetro this.

Tutorial de Java

231

Crear el fichero de STUBS


Volvemos a utilizar la aplicacin javah para crear el fichero de stubs, que contiene todas las declaraciones de mtodos, con sus llamadas y argumentos, listos para que nosotros rellenemos el cuerpo de los mtodos con los algoritmos que necesitemos implementar. Proporciona la unin entre la clase Java y su estructura C paralela. Para generar este fichero, debemos indicar el parmetro .stubs al ejecutar la aplicacin javah sobre la clase HolaMundo, de la siguiente forma:
> javah -stubs HolaMundo

Del mismo modo que se generaba el fichero .h; el nombre del fichero de stubs ser el nombre de la clase con la extensin .c. En nuestro ejemplo, ser HolaMundo.c, y su contenido ser el siguiente:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <StubPreamble.h> /* Stubs for class HolaMundo */ /* SYMBOL: HolaMundo/presentaSaludo()V, Java_HolaMundo_presentaSaludo_stub */ __declspec(dllexport) stack_item *Java_HolaMundo_presentaSaludo_stub(stack_item *_P_,struct execenv *_EE_) { extern void HolaMundo_presentaSaludo(void *); (void) HolaMundo_presentaSaludo(_P_[0].p); return _P_; }

Escribir la funcin C
Escribiremos la funcin C para el mtodo nativo en un fichero fuente de cdigo C. La implementacin ser una funcin habitual C, que luego integraremos con la clase Java. La definicin de la funcin C debe ser la misma que la que se ha generado con javah en el fichero HolaMundo.h. La implementacin que proponemos la guardaremos en el fichero HolaImp.c, y contendr las siguientes lnea de cdigo:
#include <StubPreamble.h> #include HolaMundo.h> #include <stdio.h> void HolaMundo_presentaSaludo( struct HHolaMundo *this ) { printf( Hola Mundo, desde el Tutorial de Java\n ); return; }

Como se puede ver, la implementacin no puede ser ms sencilla: hace una llamada a la funcin printf() para presentar el saludo y sale. En el cdigo se incluyen tres ficheros de cabecera:
StubsPreamble.h

232

Tutorial de Java

Proporciona la informacin para que el cdigo C pueda interactuar con el sistema Java. Cuando se escriben mtodos nativos, siempre habr que incluir este fichero en el cdigo fuente C.
HolaMundo.h

Es el fichero de cabecera que hemos generado para nuestra clase. Contiene la estructura C que representa la clase Java para la que estamos escribiendo el mtodo nativo y la definicin de la funcin para ese mtodo nativo.
stdio.h

Es necesario incluirlo porque utilizamos la funcin printf() de la librera estndar de C, cuya declaracin se encuentra en este fichero de cabecera.

Crear la librera dinmica


Utilizaremos el compilador C para compilar el fichero .h, el fichero de stubs y el fichero fuente .c; para crear una librera dinmica. Para crearla, utilizaremos el compilador C de nuestro sistema, haciendo que los ficheros HolaMundo.c y HolaImp.c generen una librera dinmica de nombre hola, que ser la que el sistema Java cargue cuando ejecute la aplicacin que estamos construyendo. Vamos a ver cmo generamos esta librera en Unix y en Windows 95.

Unix
Teclearemos el siguiente comando:
% cc -G HolaMundo.c HolaImp.c -o libhola.so

En caso de que no encuentre el compilador los ficheros de cabecera, se puede utilizar el flag -I para indicarle el camino de bsqueda, por ejemplo:
% cc -G -I$JAVA_HOME/include HolaMundo.c HolaImp.c -o libhola.so

donde $JAVA_HOME es el directorio donde se ha instalado la versin actual del Java Development Kit.

Windows 95
El comando a utilizar en este caso es el siguiente:
c:\>cl HolaMundo.c HolaImp.c -Fhola.dll -MD -LD javai.lib

Este comando funciona con Microsoft Visual C++ 2.x y posteriores. Si queremos indicar al compilador donde se encuentran los ficheros de cabecera y las libreras, tendremos que fijar dos variables de entorno:
c:\>SET INCLUDE=%JAVAHOME%\include;%INCLUDE% c:\>SET LIB=%JAVAHOME%\lib;%LIB%

Tutorial de Java

233

donde %JAVAHOME% es el directorio donde se ha instalado la versin actual del Java Development Kit.

Ejecutar el programa
Y, por fin, utilizaremos el intrprete de Java, java, para ejecutar el programa que hemos construido siguiendo todos los pasos anteriormente descritos. Si tecleamos el comando:
> java Main

obtendremos el resultado siguiente:


% Hola Mundo, desde el Tutorial de Java

Si no aparece este mensaje de saludo y lo que aparece en pantalla son expresiones como UnsatisfiedLinkError, es porque no tenemos fijado correctamente el camino de la librera dinmica que hemos generado. Este camino es la lista de directorios que el sistema Java utilizar para buscar las libreras que debe cargar. Debemos asegurarnos de que el directorio donde se encuentra nuestra librera hola recin creada, figura entre ellos. Si fijamos el camino correcto y ejecutamos de nuevo el programa, veremos que ahora s obtenemos el mensaje de saludo que esperbamos. Con ello, hemos visto como integrar cdigo C en programas Java. Quedan muchas cuestiones por medio, como la equivalencia de tipos entre Java y C, el paso de parmetros, el manejo de cadenas, etc. Pero eso supondra entrar en mucha ms profundidad dentro de Java de la que aqu pretendemos (por ahora).

234

Tutorial de Java

Captulo 12
La clase System
Stdin

Entrada/Salida Estndar

Los usuarios de Unix, y aquellos familiarizados con las lneas de comandos de otros sistemas como DOS, han utilizado un tipo de entrada/salida conocida comnmente por entrada/ salida estndar. El fichero de entrada estndar (stdin) es simplemente el teclado. El fichero de salida estndar (stdout) es tpicamente la pantalla (o la ventana del terminal). El fichero de salida de error estndar (stderr) tambin se dirige normalmente a la pantalla, pero se implementa como otro fichero de forma que se pueda distinguir entre la salida normal y (si es necesario) los mensajes de error.

Java tiene acceso a la entrada/salida estndar a travs de la clase System. En concreto, los tres ficheros que se implementan son:

System.in implementa stdin como una instancia de la clase InputStream. Con System.in, se accede a los mtodos read() y skip(). El mtodo read() permite leer un byte de la entrada. skip( long n ), salta n bytes de la entrada.

Stdout System.out implementa stdout como una instancia de la clase PrintStream. Se pueden utilizar los mtodos print() y println() con cualquier tipo bsico Java como argumento.

Stderr System.err implementa stderr de la misma forma que stdout. Como con System.out, se tiene acceso a los mtodos de PrintStream.
Vamos a ver un pequeo ejemplo de entrada/salida en Java. El cdigo siguiente, miType.java, reproduce, o funciona como la utilidad cat de Unix o type de DOS:
import java.io.*;

Tutorial de Java

235

class miType { public static void main( String args[] ) throws IOException { int c; int contador = 0; while( (c = System.in.read() ) != \n ) { contador++; System.out.print( (char)c ); } System.out.println(); // Lnea en blanco System.err.println( Contados + contador + bytes en total. ); } }

Clases comunes de Entrada/Salida


Adems de la entrada por teclado y salida por pantalla, se necesita entrada/salida por fichero, como son:
FileInputStream DataInputStream FileOutputStream DataOutputStream

Tambin existen otras clases para aplicaciones ms especficas, que no vamos a tratar, por ser de un uso muy concreto:
PipedInputStream BufferedInputStream PushBackInputStream StreamTokenizer PipedOutputStream BufferedOutputStream

236

Tutorial de Java

Captulo 13
Ficheros

Ficheros en Java

Todos los lenguajes de programacin tienen alguna forma de interactuar con los sistemas de ficheros locales; Java no es una excepcin. Cuando se desarrollan applets para utilizar en red, hay que tener en cuenta que la entrada/ salida directa a fichero es una violacin de seguridad de acceso. Muchos usuarios configurarn sus navegadores para permitir el acceso al sistema de ficheros, pero otros no. Por otro lado, si se est desarrollando una aplicacin Java para uso interno, probablemente ser necesario el acceso directo a ficheros.

Antes de realizar acciones sobre un fichero, necesitamos un poco de informacin sobre ese fichero. La clase File proporciona muchas utilidades relacionadas con ficheros y con la obtencin de informacin bsica sobre esos ficheros.

Creacin de un objeto File


Para crear un objeto File nuevo, se puede utilizar cualquiera de los tres constructores siguientes:
File miFichero; miFichero = new File( /etc/kk );

o
miFichero = new File( /etc,kk );

o
File miDirectorio = new File( /etc ); miFichero = new File( miDirectorio,kk );

El constructor utilizado depende a menudo de otros objetos File necesarios para el acceso. Por ejemplo, si slo se utiliza un fichero en la aplicacin, el primer constructor es el mejor. Si en cambio, se utilizan muchos ficheros desde un mismo directorio, el segundo o tercer constructor sern ms cmodos. Y si el directorio o el fichero es una variable, el segundo constructor ser el ms til.

Tutorial de Java

237

Comprobaciones y Utilidades
Una vez creado un objeto File, se puede utilizar uno de los siguientes mtodos para reunir informacin sobre el fichero: Nombres de fichero
String getName() String getPath() String getAbsolutePath() String getParent() boolean renameTo( File nuevoNombre )

Comprobaciones
boolean exists() boolean canWrite() boolean canRead() boolean isFile() boolean isDirectory() boolean isAbsolute()

Informacin general del fichero


long lastModified() long length()

Utilidades de directorio
boolean mkdir() String[] list()

Vamos a desarrollar una pequea aplicacin que muestra informacin sobre los ficheros pasados como argumentos en la lnea de comandos, InfoFichero.java:
import java.io.*; class InfoFichero { public static void main( String args[] ) throws IOException { if( args.length > 0 ) { for( int i=0; i < args.length; i++ ) { File f = new File( args[i] ); System.out.println( Nombre: +f.getName() ); System.out.println( Camino: +f.getPath() ); if( f.exists() ) { System.out.print( Fichero existente ); System.out.print( (f.canRead() ? y se puede Leer : ) ); System.out.print( (f.canWrite() ? y se puese Escribir : ) ); System.out.println( . ); System.out.println( La longitud del fichero son + f.length()+ bytes ); } else System.out.println( El fichero no existe. ); } } else

238

Tutorial de Java

System.out.println( Debe indicar un fichero. ); } }

Streams de Entrada
Hay muchas clases dedicadas a la obtencin de entrada desde un fichero. Este es el esquema de la jerarqua de clases de entrada por fichero:

Objetos FileInputStream
Los objetos FileInputStream tpicamente representan ficheros de texto accedidos en orden secuencial, byte a byte. Con FileInputStream, se puede elegir acceder a un byte, varios bytes o al fichero completo.

Apertura de un FileInputStream
Para abrir un FileInputStream sobre un fichero, se le da al constructor un String o un objeto File:
FileInputStream mi FicheroSt; miFicheroSt = new FileInputStream( /etc/kk );

Tambin se puede utilizar:


File miFichero FileInputStream miFicheroSt; miFichero = new File( /etc/kk ); miFicheroSt = new FileInputStream( miFichero );

Tutorial de Java

239

Lectura de un FileInputStream
Una vez abierto el FileInputStream, se puede leer de l. El mtodo read() tiene muchas opciones:
int read();

Lee un byte y devuelve -1 al final del stream.


int read( byte b[] );

Llena todo el array, si es posible. Devuelve el nmero de bytes ledos o -1 si se alcanz el final del stream.
int read( byte b[],int offset,int longitud );

Lee longitud bytes en b comenzando por b[offset]. Devuelve el nmero de bytes ledos o -1 si se alcanz el final del stream.

Cierre de FileInputStream
Cuando se termina con un fichero, existen dos opciones para cerrarlo: explcitamente, o implcitamente cuando se recicla el objeto (el garbage collector se encarga de ello). Para cerrarlo explcitamente, se utiliza el mtodo close():
miFicheroSt.close();

Ejemplo: Visualizacin de un fichero


Si la configuracin de la seguridad de Java permite el acceso a ficheros, se puede ver el contenido de un fichero en un objeto TextArea. El cdigo siguiente contiene los elementos necesarios para mostrar un fichero:
FileInputStream fis; TextArea ta; public void init() { byte b[] = new byte[1024]; int i; // El buffer de lectura se debe hacer lo suficientemente grande // o esperar a saber el tamao del fichero String s; try { fis = new FileInputStream( /etc/kk ); } catch( FileNotFoundException e ) { /* Hacer algo */ } try {

240

Tutorial de Java

i = fis.read( b ); } catch( IOException e ) { /* Hacer algo */ } s = new String( b,0 ); ta = new TextArea( s,5,40 ); add( ta ); }

Hemos desarrollado un ejemplo, Agenda.java, en el que partimos de un fichero agenda que dispone de los datos que nosotros deseamos de nuestros amigos, como son: nombre, telfono y direccin. Si tecleamos un nombre, buscar en el fichero de datos si existe ese nombre y presentar la informacin que se haya introducido. Para probar, intentar que aparezca la informacin de Pepe.

Objetos DataInputStream
Los objetos DataInputStream se comportan como los FileInputStream. Los streams de datos pueden leer cualquiera de las variables de tipo nativo, como floats, ints o chars. Generalmente se utilizan DataInputStream con ficheros binarios.

Apertura y cierre de DataInputStream


Para abrir y cerrar un objeto DataInputStream, se utilizan los mismos mtodos que para FileInputStream:
DataInputStream miDStream; FileInputStream miFStream; // Obtiene un controlador de fichero miFStream = new FileInputStream /etc/ejemplo.dbf ); //Encadena un fichero de entrada de datos miDStream = new DataInputStream( miFStream ); // Ahora se pueden utilizar los dos streams de entrada para // acceder al fichero (si se quiere...) miFStream.read( b ); i = miDStream.readInt(); // Cierra el fichero de datos explcitamente //Siempre se cierra primero el fichero stream de mayor nivel miDStream.close(); miFStream.close();

Lectura de un DataInputStream
Al acceder a un fichero como DataInputStream, se pueden utilizar los mismos mtodos read() de los objetos FileInputStream. No obstante, tambin se tiene acceso a otros mtodos dise-

Tutorial de Java

241

ados para leer cada uno de los tipos de datos:


byte readByte() int readUnsignedByte() short readShort() int readUnsignedShort() char readChar() int readInt() long readLong() float readFloat() double readDouble() String readLine()

Cada mtodo leer un objeto del tipo pedido. Para el mtodo String readLine(), se marca el final de la cadena con \n, \r, \r\n o con EOF. Para leer un long, por ejemplo:
long numeroSerie; ... numeroSerie = miDStream.readLong();

Streams de entrada de URLs


Adems del acceso a ficheros, Java proporciona la posibilidad de acceder a URLs como una forma de acceder a objetos a travs de la red. Se utiliza implcitamente un objeto URL al acceder a sonidos e imgenes, con el mtodo getDocumentBase() en los applets:
String imagenFich = new String( imagenes/pepe.gif ); imagenes[0] = getImage( getDocumentBase(),imagenFich );

No obstante, se puede proporcionar directamente un URL, si se quiere:


URL imagenSrc; imagenSrc = new URL( http://enterprise.com/~info ); imagenes[0] = getImage( imagenSrc,imagenes/pepe.gif );

Apertura de un Stream de entrada de URL


Tambin se puede abrir un stream de entrada a partir de un URL. Por ejemplo, se puede utilizar un fichero de datos para un applet:
ImputStream is; byte buffer[] = new byte[24]; is = new URL( getDocumentBase(),datos).openStream();

Ahora se puede utilizar is para leer informacin de la misma forma que se hace con un objeto FileInputStream:
is.read( buffer,0,buffer.length );

NOTA: Debe tenerse muy en cuenta que algunos usuarios pueden haber configurado la seguridad de sus navegadores para que los applets no accedan a ficheros.

242

Tutorial de Java

Streams de Salida
La contrapartida necesaria de la lectura de datos es la escritura de datos. Como con los Streams de entrada, las clases de salida estn ordenadas jerrquicamente:

Examinaremos las clases FileOutputStream y DataOutputStream para complementar los streams de entrada que se han visto. En los ficheros fuente del directorio $JAVA_HOME/src/ java/io se puede ver el uso y mtodos de estas clases, as como de los streams de entrada ($JAVA_HOME es el directorio donde se haya instalado el Java Development Kit, en sistemas UNIX).

Objetos FileOutputStream
Los objetos FileOutputStream son tiles para la escritura de ficheros de texto. Como con los ficheros de entrada, primero se necesita abrir el fichero para luego escribir en l.

Apertura de un FileOutputStream
Para abrir un objeto FileOutputStream, se tienen las mismas posibilidades que para abrir un fichero stream de entrada. Se le da al constructor un String o un objeto File.
FileOutputStream miFicheroSt; miFicheroSt = new FileOutputStream( /etc/kk );

Como con los streams de entrada, tambin se puede utilizar:


File miFichero FileOutputStream miFicheroSt; miFichero = new File( /etc/kk ); miFicheroSt = new FileOutputStream( miFichero );

Escritura en un FileOutputStream
Una vez abierto el fichero, se pueden escribir bytes de datos utilizando el mtodo write(). Como con el mtodo read() de los streams de entrada, tenemos tres posibilidades:
void write( int b );

Tutorial de Java

243

Escribe un byte.
void write( byte b[] );

Escribe todo el array, si es posible.


void write( byte b[],int offset,int longitud );

Escribe longitud bytes en b comenzando por b[offset].

Cierre de FileOutputStream
Cerrar un stream de salida es similar a cerrar streams de entrada. Se puede utilizar el mtodo explcito:
miFicheroSt.close();

O, se puede dejar que el sistema cierre el fichero cuando se recicle miFicheroSt.

Ejemplo: Almacenamiento de Informacin


Este programa, Telefonos.java, pregunta al usuario una lista de nombres y nmeros de telfono. Cada nombre y nmero se aade a un fichero situado en una localizacin fija. Para indicar que se ha introducido toda la lista, el usuario especifica Fin ante la solicitud de entrada del nombre. Una vez que el usuario ha terminado de teclear la lista, el programa crear un fichero de salida que se mostrar en pantalla o se imprimir. Por ejemplo:
95-4751232,Juanito 564878,Luisa 123456,Pepe 347698,Antonio 91-3547621,Maria

El cdigo fuente del programa es el siguiente:


import java.io.*; class Telefonos { static FileOutputStream fos; public static final int longLinea = 81; public static void main( String args[] ) throws IOException { byte tfno[] = new byte[longLinea]; byte nombre[] = new byte[longLinea]; fos = new FileOutputStream( telefono.dat ); while( true ) { System.err.println( Teclee un nombre (Fin termina) ); leeLinea( nombre ); if( fin.equalsIgnoreCase( new String( nombre,0,0,3 ) ) )

244

Tutorial de Java

break; System.err.println( Teclee el numero de telefono ); leeLinea( tfno ); for( int i=0; tfno[i] != 0; i++ ) fos.write( tfno[i] ); fos.write( , ); for( int i=0; nombre[i] != 0; i++ ) fos.write( nombre[i] ); fos.write( \n ); } fos.close(); } private static void leeLinea( byte linea[] ) throws IOException { int b = 0; int i = 0; while( (i < ( longLinea-1) ) && ( ( b = System.in.read() ) != \n ) ) linea[i++] = (byte)b; linea[i] = (byte)0; } }

Streams de salida con buffer


Si se trabaja con gran cantidad de datos, o se escriben muchos elementos pequeos, ser una buena idea utilizar un stream de salida con buffer. Los streams con buffer ofrecen los mismos mtodos de la clase FileOutputStream, pero toda salida se almacena en un buffer. Cuando se llena el buffer, se enva a disco con una nica operacin de escritura; o, en caso necesario, se puede enviar el buffer a disco en cualquier momento.

Creacin de Streams de salida con buffer


Para crear un stream BufferedOutput, primero se necesita un stream FileOutput normal; entonces se le aade un buffer al stream:
FileOutputStream miFileStream; BufferdOutpurStream miBufferStream; // Obtiene un controlador de fichero miFileStream = new FileOutputStream( /tmp/kk ); // Encadena un stream de salida con buffer miBufferStream = new BufferedOutputStream( miFileStream );

Volcado y Cierre de Streams de salida con buffer


Al contrario que los streams FileOutput, cada escritura al buffer no se corresponde con una escritura en disco. A menos que se llene el buffer antes de que termine el programa, cuando se quiera volcar el buffer explcitamente se debe hacer mediante una llamada a flush():

Tutorial de Java

245

// Se fuerza el volcado del buffer a disco miBufferStream.flush(); // Cerramos el fichero de datos. Siempre se ha de cerrar primero el // fichero stream de mayor nivel miBufferStream.close(); miFileStream.close();

Streams DataOutput
Java tambin implementa una clase de salida complementaria a la clase DataInputStream. Con la clase DataOutputStream, se pueden escribir datos binarios en un fichero.

Apertura y cierre de objetos DataOutputStream


Para abrir y cerrar objetos DataOutputStream, se utilizan los mismos mtodos que para los objetos FileOutputStream:
DataOutputStream miDataStream; FileOutputStream miFileStream; BufferedOutputStream miBufferStream; // Obtiene un controlador de fichero miFileStream = new FileOutputStream( /tmp/kk ); // Encadena un stream de salida con buffer (por eficiencia) miBufferStream = new BufferedOutputStream( miFileStream ); // Encadena un fichero de salida de datos miDataStream = new DataOutputStream( miBufferStream ); // Ahora se pueden utilizar los dos streams de entrada para // acceder al fichero (si se quiere) miBufferStream.write( b ); miDataStream.writeInt( i ); // Cierra el fichero de datos explcitamente. Siempre se cierra // primero el fichero stream de mayor nivel miDataStream.close(); miBufferStream.close(); miFileStream.close();

Escritura en un objeto DataOutputStream


Cada uno de los mtodos write() accesibles por los FileOutputStream tambin lo son a travs de los DataOutputStream. Tambin encontrar mtodos complementarios a los de DataInputStream:
void writeBoolean( boolean b ); void writeByte( int i ); void writeShort( int i ); void writeChar( int i ); void writeInt( int i ); void writeFloat( float f );

246

Tutorial de Java

void writeDouble( double d ); void writeBytes( String s ); void writeChars( string s );

Para las cadenas, se tienen dos posibilidades: bytes y caracteres. Hay que recordar que los bytes son objetos de 8 bits y los caracteres lo son de 16 bits. Si nuestras cadenas utilizan caracteres Unicode, debemos escribirlas con writeChars().

Contabilidad de la salida
Otra funcin necesaria durante la salida es el mtodo size(). Este mtodo simplemente devuelve el nmero total de bytes escritos en el fichero. Se puede utilizar size() para ajustar el tamao de un fichero a mltiplo de cuatro. Por ejemplo, de la forma siguiente:
... int numBytes = miDataStream.size() % 4; for( int i=0; i < numBytes; i++ ) miDataStream.write( 0 ); ...

Ficheros de acceso aleatorio


A menudo, no se desea leer un fichero de principio a fin; sino acceder al fichero como una base de datos, donde se salta de un registro a otro; cada uno en diferentes partes del fichero. Java proporciona una clase RandomAccessFile para este tipo de entrada/salida.

Creacin de un Fichero de Acceso Aleatorio


Hay dos posibilidades para abrir un fichero de acceso aleatorio: Con el nombre del fichero:
miRAFile = new RandomAccessFile( String nombre,String modo );

Con un objeto File:


miRAFile = new RandomAccessFile( File fichero,String modo );

El argumento modo determina si se tiene acceso de slo lectura (r) o de lectura/escritura (r/ w). Por ejemplo, se puede abrir un fichero de una base de datos para actualizacin:
RandomAccessFile miRAFile; miRAFile = new RandomAccessFile( /tmp/kk.dbf,rw );

Acceso a la Informacin
Los objetos RandomAccessFile esperan informacin de lectura/escritura de la misma manera que los objetos DataInput/DataOutput. Se tiene acceso a todas las operaciones read() y write() de las clases DataInputStream y DataOutputStream.

Tutorial de Java

247

Tambin se tienen muchos mtodos para moverse dentro de un fichero:


long getFilePointer();

Devuelve la posicin actual del puntero del fichero


void seek( long pos );

Coloca el puntero del fichero en una posicin determinada. La posicin se da como un desplazamiento en bytes desde el comienzo del fichero. La posicin 0 marca el comienzo de ese fichero.
long length();

Devuelve la longitud del fichero. La posicin length() marca el final de ese fichero.

Actualizacin de Informacin
Se pueden utilizar ficheros de acceso aleatorio para aadir informacin a ficheros existentes:
miRAFile = new RandomAccessFile( /tmp/kk.log,rw ); miRAFile.seek( miRAFile.length() ); // Cualquier write() que hagamos a partir de este punto del cdigo // aadir informacin al fichero

Vamos a ver un pequeo ejemplo, Log.java, que aade una cadena a un fichero existente:

import java.io.*; // Cada vez que ejecutemos este programita, se incorporara una nueva // linea al fichero de log que se crea la primera vez que se ejecuta // class Log { public static void main( String args[] ) throws IOException { RandomAccessFile miRAFile; String s = Informacion a incorporar\nTutorial de Java\n; // Abrimos el fichero de acceso aleatorio miRAFile = new RandomAccessFile( /tmp/java.log,rw ); // Nos vamos al final del fichero miRAFile.seek( miRAFile.length() ); // Incorporamos la cadena al fichero miRAFile.writeBytes( s ); // Cerramos el fichero miRAFile.close(); } }

248

Tutorial de Java

Captulo 14
Comunicaciones en UNIX

Comunicaciones en Java

En este captulo no nos vamos a extender demasiado en profundidades sobre la comunicacin y funcionamiento de redes, aunque s proporcionaremos un breve bao inicial para sentar, o recordar, los fundamentos de la comunicacin en red, tomando como base Unix. Presentaremos un ejemplo bsico de cliente/servidor sobre sockets TCP/IP, proporcionando un punto de partida para el desarrollo de otras aplicaciones cliente/servidor basadas en sockets, que posteriormente implementaremos.

El sistema de Entrada/Salida de Unix sigue el paradigma que normalmente se designa como Abrir-Leer-Escribir-Cerrar. Antes de que un proceso de usuario pueda realizar operaciones de entrada/salida, debe hacer una llamada a Abrir (open) para indicar, y obtener permisos para su uso, el fichero o dispositivo que quiere utilizar. Una vez que el objeto est abierto, el proceso de usuario realiza una o varias llamadas a Leer (read) y Escribir (write), para conseguir leer y escribir datos. Leer coge datos desde el objeto y los transfiere al proceso de usuario, mientras que Escribir transfiere datos desde el proceso de usuario al objeto. Una vez que todos estos intercambios de informacin estn concluidos, el proceso de usuario llamar a Cerrar (close) para informar al sistema operativo que ha finalizado la utilizacin del objeto que antes haba abierto. Cuando se incorporan las caractersticas a Unix de comunicacin entre procesos (IPC) y el manejo de redes, la idea fue implementar la interface con IPC similar a la que se estaba utilizando para la entrada/salida de ficheros, es decir, siguiendo el paradigma del prrafo anterior. En Unix, un proceso tiene un conjunto de descriptores de entrada/salida desde donde Leer y por donde Escribir. Estos descriptores pueden estar referidos a ficheros, dispositivos, o canales de comunicaciones (sockets). El ciclo de vida de un descriptor, aplicado a un canal de comunicacin (socket), est determinado por tres fases (siguiendo el paradigma): Creacin, apertura del socket Lectura y Escritura, recepcin y envo de datospor el socket Destruccin, cierre del socket

La interface IPC en Unix-BSD est implementada sobre los protocolos de red TCP y UDP. Los destinatarios de los mensajes se especifican como direcciones de socket; cada direccin de socket es un identificador de comunicacin que consiste en una direccin Internet y un nmero de puerto.

Tutorial de Java

249

Las operaciones IPC se basan en pares de sockets. Se intercambian informacin transmitiendo datos a travs de mensajes que circulan entre un socket en un proceso y otro socket en otro proceso. Cuando los mensajes son enviados, se encolan en el socket hasta que el protocolo de red los haya transmitido. Cuando llegan, los mensajes son encolados en el socket de recepcin hasta que el proceso que tiene que recibirlos haga las llamadas necesarias para recoger esos datos.

Socktes
Los sockets son puntos finales de enlaces de comunicaciones entre procesos. Los procesos los tratan como descriptores de ficheros, de forma que se pueden intercambiar datos con otros procesos transmitiendo y recibiendo a travs de sockets. El tipo de sockets describe la forma en la que se transfiere informacin a travs de ese socket.

Sockets Stream (TCP, Transport Control Protocol)


Son un servicio orientado a conexin donde los datos se transfieren sin encuadrarlos en registros o bloques. Si se rompe la conexin entre los procesos, stos sern informados. El protocolo de comunicaciones con streams es un protocolo orientado a conexin, ya que para establecer una comunicacin utilizando el protocolo TCP, hay que establecer en primer lugar una conexin entre un par de sockets. Mientras uno de los sockets atiende peticiones de conexin (servidor), el otro solicita una conexin (cliente). Una vez que los dos sockets estn conectados, se pueden utilizar para transmitir datos en ambas direcciones.

Sockets Datagrama (UDP, User Datagram Protocol)


Son un servicio de transporte sin conexin. Son ms eficientes que TCP, pero no est garantizada la fiabilidad. Los datos se envan y reciben en paquetes, cuya entrega no est garantizada. Los paquetes pueden ser duplicados, perdidos o llegar en un orden diferente al que se envi. El protocolo de comunicaciones con datagramas es un protocolo sin conexin, es decir, cada vez que se enven datagramas es necesario enviar el descriptor del socket local y la direccin del socket que debe recibir el datagrama. Como se puede ver, hay que enviar datos adicionales cada vez que se realice una comunicacin.

Sockets Raw
Son sockets que dan acceso directo a la capa de software de red subyacente o a protocolos de ms bajo nivel. Se utilizan sobre todo para la depuracin del cdigo de los protocolos.

250

Tutorial de Java

Diferencias entre Sockets Stream y Datagrama


Ahora se nos presenta un problema, qu protocolo, o tipo de sockets, debemos usar - UDP o TCP? La decisin depende de la aplicacin cliente/servidor que estemos escribiendo. Vamos a ver algunas diferencias entre los protocolos para ayudar en la decisin. En UDP, cada vez que se enva un datagrama, hay que enviar tambin el descriptor del socket local y la direccin del socket que va a recibir el datagrama, luego stos son ms grandes que los TCP. Como el protocolo TCP est orientado a conexin, tenemos que establecer esta conexin entre los dos sockets antes de nada, lo que implica un cierto tiempo empleado en el establecimiento de la conexin, que no existe en UDP. En UDP hay un lmite de tamao de los datagramas, establecido en 64 kilobytes, que se pueden enviar a una localizacin determinada, mientras que TCP no tiene lmite; una vez que se ha establecido la conexin, el par de sockets funciona como los streams: todos los datos se leen inmediatamente, en el mismo orden en que se van recibiendo. UDP es un protocolo desordenado, no garantiza que los datagramas que se hayan enviado sean recibidos en el mismo orden por el socket de recepcin. Al contrario, TCP es un protocolo ordenado, garantiza que todos los paquetes que se enven sern recibidos en el socket destino en el mismo orden en que se han enviado. Los datagramas son bloques de informacin del tipo lanzar y olvidar. Para la mayora de los programas que utilicen la red, el usar un flujo TCP en vez de un datagrama UDP es ms sencillo y hay menos posibilidades de tener problemas. Sin embargo, cuando se requiere un rendimiento ptimo, y est justificado el tiempo adicional que supone realizar la verificacin de los datos, los datagramas son un mecanismo realmente til. En resumen, TCP parece ms indicado para la implementacin de servicios de red como un control remoto (rlogin, telnet) y transmisin de ficheros (ftp); que necesitan transmitir datos de longitud indefinida. UDP es menos complejo y tiene una menor sobrecarga sobre la conexin; esto hace que sea el indicado en la implementacin de aplicaciones cliente/servidor en sistemas distribuidos montados sobre redes de rea local.

Uso de sockets
Podemos pensar que un Servidor Internet es un conjunto de sockets que proporciona capacidades adicionales del sistema, los llamados servicios.

Puertos y Servicios
Cada servicio est asociado a un puerto. Un puerto es una direccin numrica a travs de la cual se procesa el servicio. Sobre un sistema Unix, los servicios que proporciona ese sistema se indican en el fichero /etc/services, y algunos ejemplos son:

Tutorial de Java

251

daytime 13/udp ftp 21/tcp telnet 23/tcp telnet smtp 25/tcp mail http 80/tcp

La primera columna indica el nombre del servicio. La segunda columna indica el puerto y el protocolo que est asociado al servicio. La tercera columna es un alias del servicio; por ejemplo, el servicio smtp, tambin conocido como mail, es la implementacin del servicio de correo electrnico. Las comunicaciones de informacin relacionada con Web tienen lugar a travs del puerto 80 mediante protocolo TCP. Para emular esto en Java, usaremos la clase Socket. La fecha (daytime). Sin embargo, el servicio que coge la fecha y la hora del sistema, est ligado al puerto 13 utilizando el protocolo UDP. Un servidor que lo emule en Java usara un objeto DatagramSocket.

La clase URL
La clase URL contiene contructores y mtodos para la manipulacin de URL (Universal Resource Locator): un objeto o servicio en Internet. El protocolo TCP necesita dos tipos de informacin: la direccin IP y el nmero de puerto. Vamos a ver como podemos recibir pues la pgina Web principal de nuestro buscador favorito al teclear:
http://www.yahoo.com

En primer lugar, Yahoo tiene registrado su nombre, permitiendo que se use yahoo.com como su direccin IP, o lo que es lo mismo, cuando indicamos yahoo.com es como si hubiesemos indicado 205.216.146.71, su direccin IP real. La verdad es que la cosa es un poco ms complicada que eso. Hay un servicio, el DNS (Domain Name Service), que traslada www.yahoo.com a 205.216.146.71, lo que nos permite teclear www.yahoo.com, en lugar de tener que recordar su direccin IP. Si queremos obtener la direccin IP real de la red en que estamos corriendo, podemos realizar llamadas a los mtodos getLocalHost() y getAddress(). Primero, getLocalHost() nos devuelve un objeto iNetAddress, que si usamos con getAddress() generar un array con los cuatro bytes de la direccin IP, por ejemplo:
InetAddress direccion = InetAddress.getLocalHost(); byte direccionIp[] = direccion.getAddress();

Si la direccin de la mquina en que estamos corriendo es 150.150.112.145, entonces:


direccionIp[0] = 150 direccionIp[1] = 150 direccionIp[2] = 112 direccionIp[3] = 145

Una cosa interesante en este punto es que una red puede mapear muchas direcciones IP. Esto puede ser necesario para un Servidor Web, como Yahoo, que tiene que soportar grandes cantidades de trfico y necesita ms de una direccin IP para poder atender a todo ese trfico. El nombre interno para la direccin 205.216.146.71, por ejemplo, es www7.yahoo.com.

252

Tutorial de Java

El DNS puede trasladar una lista de direcciones IP asignadas a Yahoo en www.yahoo.com. Esto es una cualidad til, pero por ahora abre un agujero en cuestin de seguridad. Ya conocemos la direccin IP, nos falta el nmero del puerto. Si no se indica nada, se utilizar el que se haya definido por defecto en el fichero de configuracin de los servicios del sistema. En Unix se indican en el fichero /etc/services, en Windows-NT en el fichero services y en otros sistemas puede ser diferente. El puerto habitual de los servicios Web es el 80, as que si no indicamos nada, entraremos en el servidor de Yahoo por el puerto 80. Si tecleamos la URL siguiente en un navegador:
http://www.yahoo.com:80

tambin recibiremos la pgina principal de Yahoo. No hay nada que nos impida cambiar el puerto en el que residir el servidor Web; sin embargo, el uso del puerto 80 es casi estndar, porque elimina pulsaciones en el teclado y, adems, las direcciones URL son lo suficientemente difciles de recordar como para aadirle encima el nmero del puerto. Si necesitamos otro protocolo, como:
ftp://ftp.microsoft.com

el puerto se derivar de ese protocolo. As el puerto FTP de Microsoft es el 21, segn su fichero services. La primera parte, antes de los dos puntos, de la URL, indica el protocolo que se quiere utilizar en la conexin con el servidor. El protocolo http (HyperText Transmission Protocol), es el utilizado para manipular documentos Web. Y si no se especifica ningn documento, muchos servidores estn configurados para devolver un documento de nombre index.html. Con todo esto, Java permite los siguientes cuatro constructores para la clase URL:
public public public public URL( String spec ) throws MalformedURLException; URL( String protocol,String host,int port,String file ) throws MalformedURLException; URL( String protocol,String host,String file ) throws MalformedURLException; URL( URL context,String spec ) throws MalformedURLException;

As que podramos especificar todos los componenetes del URL como en:
URL( http,www.yahoo.com,80",index.html );

o dejar que los sistemas utilicen todos los valores por defecto que tienen definidos, como en:
URL( http://www.yahoo.com );

y en los dos casos obtendramos la visualizacin de la pgina principal de Yahoo en nuestro navegador.

Dominios de comunicaciones
El mecanismo de sockets est diseado para ser todo lo genrico posible. El socket por s mismo no contiene informacin suficiente para describir la comunicacin entre procesos. Los sockets operan dentro de dominios de comunicacin, entre ellos se define si los dos procesos que se comunican se encuentran en el mismo sistema o en sistemas diferentes y cmo pueden ser direccionados.

Tutorial de Java

253

Dominio Unix
Bajo Unix, hay dos dominios, uno para comunicaciones internas al sistema y otro para comunicaciones entre sistemas. Las comunicaciones intrasistema (entre dos procesos en el mismo sistema) ocurren (en una mquina Unix) en el dominio Unix. Se permiten tanto los sockets stream como los datagrama. Los sockets de dominio Unix bajo Solaris 2.x se implementan sobre TLI (Transport Level Interface). En el dominio Unix no se permiten sockets de tipo Raw.

Dominio Internet
Las comunicaciones intersistemas proporcionan acceso a TCP, ejecutando sobre IP (Internet Protocol). De la misma forma que el dominio Unix, el dominio Internet permite tanto sockets stream como datagrama, pero adems permite sockets de tipo Raw. Los sockets stream permiten a los procesos comunicarse a travs de TCP. Una vez establecidas las conexiones, los datos se pueden leer y escribir a/desde los sockets como un flujo (stream) de bytes. Algunas aplicaciones de servicios TCP son: File Tranfer Protocol, FTP Simple Mail Transfer Protocol, SMTP TELNET, servicio de conexin de terminal remoto

Los sockets datagrama permiten a los procesos utilizar el protocolo UDP para comunicarse a y desde esos sockets por medio de bloques. UDP es un protocolo no fiable y la entrega de los paquetes no est garantizada. Servicios UDP son: Simple Network Management Protocol, SNMP Trivial File Transfer Protocol, TFTP (versin de FTP sin conexin) Versatile Message Transaction Protocol, VMTP (servicio fiable de entrega punto a punto de datagramas independiente de TCP)

Los sockets raw proporcionan acceso al Internet Control Message Protocol, ICMP, y se utiliza para comunicarse entre varias entidades IP.

MODELO DE COMUNICACIONES CON JAVA


En Java, crear una conexin socket TCP/IP se realiza directamente con el paquete java.net. A continuacin mostramos un diagrama de lo que ocurre en el lado del cliente y del servidor:

254

Tutorial de Java

El modelo de sockets ms simple es: El servidor establece un puerto y espera durante un cierto tiempo ( timeout segundos), a que el cliente establezca la conexin. Cuando el cliente solicite una conexin, el servidor abrir la conexin socket con el mtodo accept(). El cliente establece una conexin con la mquina host a travs del puerto que se designe en puerto# El cliente y el servidor se comunican con manejadores InputStream y OutputStream

Hay una cuestin al respecto de los sockets, que viene impuesta por la implementacin del sistema de seguridad de Java. Actualmente, los applets slo pueden establecer conexiones con el nodo desde el cual se transfiri su cdigo. Esto est implementado en el JDK y en el intrprete de Java de Netscape. Esto reduce en gran manera la flexibilidad de las fuentes de datos disponibles para los applets. El problema si se permite que un applet se conecte a cualquier mquina de la red, es que entonces se podran utilizar los applets para inundar la red desde un ordenador con un cliente Netscape del que no se sospecha y sin ninguna posibilidad de rastreo.

Apertura de sockets
Si estamos programando un cliente, el socket se abre de la forma:
Socket miCliente; miCliente = new Socket( maquina,numeroPuerto );

Donde maquina es el nombre de la mquina en donde estamos intentando abrir la conexin y numeroPuerto es el puerto (un nmero) del servidor que est corriendo sobre el cual nos queremos conectar. Cuando se selecciona un nmero de puerto, se debe tener en cuenta que los puertos en el rango 0-1023 estn reservados para usuarios con muchos privilegios (superusuarios o root). Estos puertos son los que utilizan los servicios estndar del sistema

Tutorial de Java

255

como email, ftp o http. Para las aplicaciones que se desarrollen, asegurarse de seleccionar un puerto por encima del 1023. En el ejemplo anterior no se usan excepciones; sin embargo, es una gran idea la captura de excepciones cuando se est trabajando con sockets. El mismo ejemplo quedara como:
Socket miCliente; try { miCliente = new Socket( maquina,numeroPuerto ); } catch( IOException e ) { System.out.println( e ); }

Si estamos programando un servidor, la forma de apertura del socket es la que muestra el siguiente ejemplo:
Socket miServicio; try { miServicio = new ServerSocket( numeroPuerto ); } catch( IOException e ) { System.out.println( e ); }

A la hora de la implementacin de un servidor tambin necesitamos crear un objeto socket desde el ServerSocket para que est atento a las conexiones que le puedan realizar clientes potenciales y poder aceptar esas conexiones:
Socket socketServicio = null; try { socketServicio = miServicio.accept(); } catch( IOException e ) { System.out.println( e ); }

Creacin de Streams
Creacin de Streams de Entrada
En la parte cliente de la aplicacin, se puede utilizar la clase DataInputStream para crear un stream de entrada que est listo a recibir todas las respuestas que el servidor le enve.
DataInputStream entrada; try { entrada = new DataInputStream( miCliente.getInputStream() ); } catch( IOException e ) { System.out.println( e ); }

La clase DataInputStream permite la lectura de lneas de texto y tipos de datos primitivos de Java de un modo altamente portable; dispone de mtodos para leer todos esos tipos como: read(), readChar(), readInt(), readDouble() y readLine(). Deberemos utilizar la funcin que creamos necesaria dependiendo del tipo de dato que esperemos recibir del servidor.

256

Tutorial de Java

En el lado del servidor, tambin usaremos DataInputStream, pero en este caso para recibir las entradas que se produzcan de los clientes que se hayan conectado:
DataInputStream entrada; try { entrada = new DataInputStream( socketServicio.getInputStream() ); } catch( IOException e ) { System.out.println( e ); }

Creacin de Streams de Salida


En el lado del cliente, podemos crear un stream de salida para enviar informacin al socket del servidor utilizando las clases PrintStream o DataOutputStream:
PrintStream salida; try { salida = new PrintStream( miCliente.getOutputStream() ); } catch( IOException e ) { System.out.println( e ); }

La clase PrintStream tiene mtodos para la representacin textual de todos los datos primitivos de Java. Sus mtodos write y println() tienen una especial importancia en este aspecto. No obstante, para el envo de informacin al servidor tambin podemos utilizar DataOutputStream:
DataOutputStream salida; try { salida = new DataOutputStream( miCliente.getOutputStream() ); } catch( IOException e ) { System.out.println( e ); }

La clase DataOutputStream permite escribir cualquiera de los tipos primitivos de Java, muchos de sus mtodos escriben un tipo de dato primitivo en el stream de salida. De todos esos mtodos, el ms til quizs sea writeBytes(). En el lado del servidor, podemos utilizar la clase PrintStream para enviar informacin al cliente:
PrintStream salida; try { salida = new PrintStream( socketServicio.getOutputStream() ); } catch( IOException e ) { System.out.println( e ); }

Pero tambin podemos utilizar la clase DataOutputStream como en el caso de envo de informacin desde el cliente.

Tutorial de Java

257

Cierre de sockets
Siempre deberemos cerrar los canales de entrada y salida que se hayan abierto durante la ejecucin de la aplicacin. En la parte del cliente:
try { salida.close(); entrada.close(); miCliente.close(); } catch( IOException e ) { System.out.println( e ); }

Y en la parte del servidor:


try { salida.close(); entrada.close(); socketServicio.close(); miServicio.close(); } catch( IOException e ) { System.out.println( e ); }

Mnimo cliente SMTP


Vamos a desarrollar un mnimo cliente SMTP (simple mail transfer protocol), de forma que podamos encapsular todos los datos en la aplicacin. El cdigo es libre de modificacin para las necesidades que sean; por ejemplo, una modificacin interesante sera que aceptase argumentos desde la lnea de comandos y tambin capturase el texto del mensaje desde la entrada estndar del sistema. Con estas modificaciones tendramos casi la misma aplicacin de correo que utiliza Unix. Veamos el cdigo de nuestro cliente, smtpCliente.java:
import java.net.*; import java.io.*; class smtpCliente { public static void main( String args[] ) { Socket s = null; DataInputStream sIn = null; DataOutputStream sOut = null; // Abrimos una conexin con breogan en el puerto 25 // que es el correspondiente al protocolo smtp, e intentamos // abrir los streams de entrada y salida try { s = new Socket( breogan,25 ); sIn = new DataInputStream( s.getInputStream() ); sOut = new DataOutputStream( s.getOutputStream() ); } catch( UnknownHostException e ) { System.out.println( No conozco el host ); } catch( IOException e ) { System.out.println( e ); } // Si todo est inicializado correctamente, vamos a escribir

258

Tutorial de Java

// algunos datos en el canal de salida que se ha establecido // con el puerto del protocolo smtp del servidor if( s != null && sIn != null && sOut != null ) { try { // Tenemos que respetar la especificacin SMTP dada en // RFC1822/3, de forma que lo que va en maysculas // antes de los dos puntos tiene un significado especial // en el protocolo sOut.writeBytes( MAIL From: froufe@arrakis.es\n ); sOut.writeBytes( RCPT To: froufe@arrakis.es\n ); sOut.writeBytes( DATA\n ); sOut.writeBytes( From: froufe@arrakis.es\n ); sOut.writeBytes( Subject: Pruebas\n ); // Ahora el cuerpo del mensaje sOut.writeBytes( Hola, desde el Tutorial de Java\n ); sOut.writeBytes( \n.\n ); // Nos quedamos a la espera de recibir el Ok del // servidor para saber que ha recibido el mensaje // correctamente, momento en el cual cortamos String respuesta; while( ( respuesta = sIn.readLine() ) != null ) { System.out.println( Servidor: +respuesta ); if( respuesta.indexOf( Ok ) != -1 ) break; } // Cerramos todo lo que hemos abierto sOut.close(); sIn.close(); s.close(); } catch( UnknownHostException e ) { System.out.println( Intentando conectar: +e ); } catch( IOException e ) { System.out.println( e ); } } } }

Servidor de ECO
En el siguiente ejemplo, vamos a desarrollar un servidor similar al que se ejecuta sobre el puerto 7 de las mquinas Unix, el servidor echo. Bsicamente, este servidor recibe texto desde un cliente y reenva ese mismo texto al cliente. Desde luego, este es el servidor ms simple de los simples que se pueden escribir. El ejemplo que presentamos, ecoServidor.java, maneja solamente un cliente. Una modificacin interesante sera adecuarlo para que aceptase mltiples clientes simultneos mediante el uso de threads.
import java.net.*; import java.io.*;

Tutorial de Java

259

class ecoServidor { public static void main( String args[] ) { ServerSocket s = null; DataInputStream sIn; PrintStream sOut; Socket cliente = null; String texto; // Abrimos una conexin con breogan en el puerto 9999 // No podemos elegir un puerto por debajo del 1023 si no somos // usuarios con los mximos privilegios (root) try { s = new ServerSocket( 9999 ); } catch( IOException e ) { } // Creamos el objeto desde el cual atenderemos y aceptaremos // las conexiones de los clientes y abrimos los canales de // comunicacin de entrada y salida try { cliente = s.accept(); sIn = new DataInputStream( cliente.getInputStream() ); sOut = new PrintStream( cliente.getOutputStream() ); // Cuando recibamos datos, se los devolvemos al cliente // que los haya enviado while( true ) { texto = sIn.readLine(); sOut.println( texto ); } } catch( IOException e ) { System.out.println( e ); } } }

Cliente/Servidor TCP/IP
Mnimo Servidor TCP/IP
Veamos el cdigo que presentamos en el siguiente ejemplo, minimoServidor.java, donde desarrollamos un mnimo servidor TCP/IP, para el cual desarrollaremos despus su contrapartida cliente TCP/IP. La aplicacin servidor TCP/IP depende de una clase de comunicaciones proporcionada por Java: ServerSocket. Esta clase realiza la mayor parte del trabajo de crear un servidor.
import java.awt.*; import java.net.*; import java.io.*; class minimoServidor { public static void main( String args[] ) {

260

Tutorial de Java

ServerSocket s = (ServerSocket)null; Socket s1; String cadena = Tutorial de Java!; int longCad; OutputStream s1out; // Establece el servidor en el socket 4321 (espera 300 segundos) try { s = new ServerSocket( 4321,300 ); } catch( IOException e ) { System.out.println( e ); } // Ejecuta un bucle infinito de listen/accept while( true ) { try { // Espera para aceptar una conexin s1 = s.accept(); // Obtiene un controlador de fichero de salida asociado // con el socket s1out = s1.getOutputStream(); // Enviamos nuestro texto longCad = sendString.length(); for( int i=0; i < longCad; i++ ) s1out.write( (int)sendString.charAt( i ) ); // Cierra la conexin, pero no el socket del servidor s1.close(); } catch( IOException e ) { System.out.println( e ); } } } }

Mnimo Cliente TCP/IP


El lado cliente de una aplicacin TCP/IP descansa en la clase Socket. De nuevo, mucho del trabajo necesario para establecer la conexin lo ha realizado la clase Socket. Vamos a presentar ahora el cdigo de nuestro cliente ms simple, minimoCliente.java, que encaja con el servidor presentado antes. El trabajo que realiza este cliente es que todo lo que recibe del servidor lo imprime por la salida estndar del sistema.
import java.awt.*; import java.net.*; import java.io.*; class minimoCliente { public static void main( String args[] ) throws IOException { int c; Socket s; InputStream sIn;

Tutorial de Java

261

// Abrimos una conexin con breogan en el puerto 4321 try { s = new Socket( breogan,4321 ); } catch( IOException e ) { System.out.println( e ); } // Obtenemos un controlador de fichero de entrada del socket y // leemos esa entrada sIn = s.getInputStream(); while( ( c = sIn.read() ) != -1 ) System.out.print( (char)c ); // Cuando se alcance el fin de fichero, cerramos la conexin y // abandonamos s.close(); } }

Servidor simple de HTTP


Vamos a implementar un servidor de HTTP bsico, slo le permitiremos admitir operaciones GET y un rango limitado de tipos MIME codificados. Los tipos MIME son los descriptores de tipo para contenido multimedia. Esperamos que este ejemplo sirva como base para un entretenido ejercicio de ampliacin y exploracin porque, desde luego, lo que no pretendemos es inquietar a los Consejos de Direccin de Microsoft o Netscape. La aplicacin va a crear un ServerSocket conectado al puerto 80, que en caso de no tener privilegios para su uso, podemos cambiar, por ejemplo al 8080; y despus entra en un bucle infinito. Dentro del bucle, espera dentro del mtodo accept() del ServerSocket hasta que se establece una conexin cliente. Despus asigna un flujo de entrada y salida al socket. A continuacin lee la solicitud del cliente utilizando el mtodo getRawRequest(), que devolver un null si hay un error de entrada/salida o el cliente corta la conexin. Luego se identifica el tipo de solicitud y se gestiona mediante el mtodo handlget() o handleUnsup(). Finalmente se cierran los sockets y se comienza de nuevo. Cuando se ejecuta el programa completo, se escribe en pantalla lo que el navegador cliente enva al servidor. Aunque se capturan varias condiciones de error, en la prctica no aparecen. El ampliar este servidor para que soporte una carga de millones de visitas al da requiere bastante trabajo; no obstante, en el ordenador en que estoy escribiendo esto, no se enlenteci demasiado con una carga de hasta diez entradas por segundo, lo que permitira alrededor de un milln de visitas al da. Se podra mejorar mediante el uso de threads y control de la memoria cach para gestionar esas visitas, pero eso ya forma parte del ejercicio sobre el que se puede trabajar. El cdigo fuente de nuestro mini servidor de HTTP se encuentra en el fichero TutHttp.java, que reproducimos a continuacin:
import java.net.*; import java.io.*;

262

Tutorial de Java

import java.util.*; // Clase de utilidades donde declaramos los tipos MIME y algunos gestores // de los errores que se pueden generar en HTML class HttpUtilidades { final static String version = 1.0; final static String mime_text_plain = text/plain; final static String mime_text_html = text/html; final static String mime_image_gif = image/gif; final static String mime_image_jpg = image/jpg; final static String mime_app_os = application/octet-stream; final static String CRLF = \r\n; // Mtodo que convierte un objeto String en una matriz de bytes. // Java gestiona las cadenas como objetos, por lo que es necesario // convertir las matrices de bytes que se obtienen a Strings y // viceversa public static byte aBytes( String s )[] { byte b[] = new byte[ s.length() ]; s.getBytes( 0,b.length,b,0 ); return( b ); } // Este mtodo concatena dos matrices de bytes. El mtodo // arraycopy() asombra por su rapidez public static byte concatenarBytes( byte a[],byte b[] )[] { byte ret[] = new byte[ a.length+b.length ]; System.arraycopy( a,0,ret,0,a.length ); System.arraycopy( b,0,ret,a.length,b.length ); return( ret ); } // Este mtodo toma un tipo de contenido y una longitud, para // devolver la matriz de bytes que contiene el mensaje de cabecera // MIME con formato public static byte cabMime( String ct,int tam )[] { return( cabMime( 200,OK,ct,tam ) ); } // Es el mismo mtodo anterior, pero permite un ajuste ms fino // del cdigo que se devuelve y el mensaje de error de HTTP public static byte cabMime(int codigo,String mensaje,String ct, int tam )[] { Date d = new Date(); return( aBytes( HTTP/1.0 +codigo+ +mensaje+CRLF+ Date: +d.toGMTString()+CRLF+ Server: Java/+version +CRLF+ Content-type: +ct+CRLF+ ( tam > 0 ? Content-length: +tam+CRLF : )+CRLF ) ); } // Este mtodo construye un mensaje HTML con un formato decente // para presentar una condicin de error y lo devuelve como // matriz de bytes public static byte error( int codigo,String msg,String fname)[] { String ret = <BODY>+CRLF+<H1>+codigo+ +msg+</H1>+CRLF;

Tutorial de Java

263

if( fname != null ) ret += Error al buscar el URL: +fname+CRLF; ret += </BODY>+CRLF; byte tmp[] = cabMime( codigo,msg,mime_text_html,0 ); return( concatenarBytes( tmp,aBytes( ret ) ) ); } // Devuelve el tipo MIME que corresponde a un nombre de archivo dado public static String mimeTypeString( String fichero ) { String tipo; if( fichero.endsWith( .html ) || fichero.endsWith( .htm ) ) tipo = mime_text_html; else if( fichero.endsWith( .class ) ) tipo = mime_app_os; else if( fichero.endsWith( .gif ) ) tipo = mime_image_gif; else if( fichero.endsWith( .jpg ) ) tipo = mime_image_jpg; else tipo = mime_text_plain; return( tipo ); } } // Esta clase sirve para que nos enteremos de lo que est haciendo // nuestro servidor. En una implementacin real, todos estos mensajes // deberan registrarse en algn fichero class HTTPlog { public static void error( String entrada ) { System.out.println( Error: +entrada ); } public static void peticion( String peticion ) { System.out.println( peticion ); } } // Esta es la clase principal de nuestro servidor Http class TutHttp { public static final int puerto = 80; final static String docRaiz = /html; final static String fichIndice = index.html; final static int buffer = 2048; public static final int RT_GET=1; public static final int RT_UNSUP=2; public static final int RT_END=4; // Indica que la peticin no est soportada, por ejemplo POST y HEAD private static void ctrlNoSop(String peticion,OutputStream sout) { HTTPlog.error( Peticion no soportada: +peticion ); } // Este mtodo analiza gramaticalmente la solicitud enviada con el // GET y la descompone en sus partes para extraer el nombre del

264

Tutorial de Java

// archivo que se est solicitando. Entonces lee el fichero que // se pide private static void ctrlGet( String peticion,OutputStream sout ) { int fsp = peticion.indexOf( ); int nsp = peticion.indexOf( ,fsp+1 ); String fich = peticion.substring( fsp+1,nsp ); fich = docRaiz+fich+( fich.endsWith(/) ? fichIndice : ); try { File f = new File( fich ); if( !f.exists() ) { sout.write( HttpUtilidades.error( 404, No Encontrado,fich ) ); return; } if( !f.canRead() ) { sout.write( HttpUtilidades.error( 404, Permiso Denegado,fich ) ); return; } // Ahora lee el fichero que se ha solicitado InputStream sin = new FileInputStream( f ); String cabmime = HttpUtilidades.mimeTypeString( fich ); int n = sin.available(); sout.write( HttpUtilidades.cabMime( cabmime,n ) ); byte buf[] = new byte[buffer]; while( ( n = sin.read( buf ) ) >= 0 ) sout.write( buf,0,n ); sin.close(); } catch( IOException e ) { HTTPlog.error( Excepcion: +e ); } } // Devuelve la cabecera de la solicitud completa del cliente al // mtodo main de nuestro servidor private static String getPeticion( InputStream sin ) { try { byte buf[] = new byte[buffer]; boolean esCR = false; int pos = 0; int c; while( ( c = sin.read() ) != -1 ) { switch( c ) { case \r: break; case \n: if( esCR ) return( new String( buf,0,0,pos ) );

Tutorial de Java

265

esCR = true; // Contina, se ha puesto el primer \n en la cadena default: if( c != \n ) esCR = false; buf[pos++] = (byte)c; } } } catch( IOException e ) { HTTPlog.error( Error de Recepcion ); } return( null ); } private static int tipoPeticion( String peticion ) { return( peticion.regionMatches( true,0,get ,0,4 ) ? RT_GET : RT_UNSUP ); } // Funcin principal de nuestro servidor, que se conecta al socket // y se embucla indefinidamente public static void main( String args[] ) throws Exception { ServerSocket ss = new ServerSocket( puerto ); while( true ) { String peticion; Socket s = ss.accept(); OutputStream sOut = s.getOutputStream(); InputStream sIn = s.getInputStream(); if( ( peticion = getPeticion( sIn ) ) != null ) { switch( tipoPeticion( peticion ) ) { case RT_GET: ctrlGet( peticion,sOut ); break; case RT_UNSUP: default: ctrlNoSop( peticion,sOut ); break; } HTTPlog.peticion( peticion ); } sIn.close(); sOut.close(); s.close(); } } }

Ejecutar TCP/IP EN Windows 95


Las indicaciones que se proporcionan a continuacin, van a permitirnos fijar los parmetros de Windows 95 para que se puedan ejecutar programas cliente y servidor, sin necesidad de

266

Tutorial de Java

que el ordenador en el cual se est ejecutando Windows 95 est conectado a una red de rea local. Esto puede resultar til para mucha gente que est probando Java en su casa y no dispone de red, o incluso para aquellos programadores o estudiosos que quieren probar sus nuevos programas distribuidos pero no disponen de red o de Internet.

Advertencia: Si los siguientes parmetros se van a fijar en un ordenador porttil que en


ocasiones s se conecte a una red, sera conveniente anotar los parmetros actuales de la configuracin de Windows 95, para que sean fcilmente recuperables cuando este ordenador se vuelva a conectar a la red.
Configuracin del TCP/IP de Windows 95 Hay que seguir los pasos que vamos a relatar a continuacin, suponemos que se est ejecutando la versin espaola de Windows 95, en otras versiones puede que las opciones tengan nombre diferente: En el Panel de Control, seleccionar Red Picar Agregar, luego Protocolo y luego Agregar Seleccionar Microsoft y luego TCP/IP y picar en Aceptar En este momento probablemente se solicite la introduccin de los discos de Windows 95, o del CD-ROM Seleccionar la pestaa Configuracin de la ventana Red Seleccionar TCP/IP en la lista que aparece y picar Propiedades Seleccionar la pestaa Direccin IP Crearse una direccin IP, como por ejemplo: 102.102.102.102 Crearse una Mscara de subred, como: 255.255.255.0

Seleccionar la pestaa Configuracin DNS y desactivar DNS Los valores correspondientes a las otras cuatro pestaas pueden dejarse los que hay por defecto Picar Aceptar (Opcional) Seleccionar la pestaa Identificacin de la ventana Red (Opcional) Introducir un nombre para el ordenador, como por ejemplo: breogan (Opcional) Picar Aceptar

Crear una entrada en la red


Editar el fichero hosts.sam que est en el directorio de Windows

Tutorial de Java

267

Al final del fichero incorporar la direccin IP y el nombre del ordenador que se han introducido antes, en nuestro caso: 102.102.102.102 breogan Asegurarse de que la direccin IP y el nombre coinciden con la direccin IP que se ha fijado en el paso 7a de antes y que el nombre es el mismo que el indicado en el paso 12 anterior Salvar el fichero con el nombre hosts y reiniciar Windows 95

Comprobacin de la red
Abrir una sesin MS-DOS Teclear ping breogan Debera aparecer:
Pinging breogan [102.102.102.102] with 32 bytes of data: Reply from 102.102.102.102: bytes=32 time=1ms TTL=32 Reply from 102.102.102.102: bytes=32 time<10ms TTL=32 Reply from 102.102.102.102: bytes=32 time<10ms TTL=32 Reply from 102.102.102.102: bytes=32 time<10ms TTL=32

Teclear tracert 102.102.102.102 Debera aparecer:


Tracing route to 102.102.102.102 over a maximum of 30 hops 1 <10 ms 1 ms <10 ms 102.102.102.102 Trace complete.

En este instante, si todo ha ido bien, el ordenador est listo para funcionar como si estuviera en red. Dos o ms programas que se comuniquen en red a travs de sockets debera poder ejecutarse ahora dentro de los dominios del ordenador que acabamos de configurar

Problemas ms frecuentes
Los tres problemas que pueden presentarse cuando intentemos comprobar el funcionamiento correcto de la red interna que acabamos de montar son: Cuando hacemos ping obtenemos Bad IP address breogan Intentar teclear ping 102.102.102.102. Si ahora s se obtiene rplica, la causa del problema es que el los pasos 12 de la Configuracin y 3 de la Creacin de la entrada en la tabla de hosts, no se ha introducido correctamente el nombre de la mquina. Comprobar esos pasos y que todo coincide. El programa cliente o el servidor fallan al intentar el connect La causa podra estar en que se produzca un fallo por fichero no encontrado en el directorio Windows/System de las libreras WINSOCK.DLL o WSOCK32.DLL. Muchos programas que se utilizan en Internet reemplazan estos ficheros cuando se instalan.

268

Tutorial de Java

Asegurarse de que estn estos ficheros y que son los originales que vienen con la distribucin de Windows 95. El programa servidor dice que no puede bind a un socket Esto sucede porque tiene el DNS activado y no puede encontrar ese DNS o servidor de direcciones, porque estamos solos en la red. Asegurarse de que en el paso 8 de la Configuracin la opcin de DNS est deshabilitada.

Clases tiles en comunicaciones


Vamos a exponer otras clases que resultan tiles cuando estamos desarrollando programas de comunicaciones, aparte de las que ya se han visto. El problema es que la mayora de estas clases se prestan a discusin, porque se encuentran bajo el directorio sun. Esto quiere decir que son implementaciones Solaris y, por tanto, especficas del Unix Solaris. Adems su API no est garantizada, pudiendo cambiar. Pero, a pesar de todo, resultan muy interesantes y vamos a comentar un grupo de ellas solamente que se encuentran en el paquete sun.net
Socket

Es el objeto bsico en toda comunicacin a travs de Internet, bajo el protocolo TCP. Esta clase proporciona mtodos para la entrada/salida a travs de streams que hacen la lectura y escritura a travs de sockets muy sencilla.
ServerSocket

Es un objeto utilizado en las aplicaciones servidor para escuchar las peticiones que realicen los clientes conectados a ese servidor. Este objeto no realiza el servicio, sino que crea un objeto Socket en funcin del cliente para realizar toda la comunicacin a travs de l.
DatagramSocket

La clase de sockets datagrama puede ser utilizada para implementar datagramas no fiables (sockets UDP), no ordenados. Aunque la comunicacin por estos sockets es muy rpida porque no hay que perder tiempo estableciendo la conexin entre cliente y servidor.
DatagramPacket

Clase que representa un paquete datagrama conteniendo informacin de paquete, longitud de paquete, direcciones Internet y nmeros de puerto.
MulticastSocket

Clase utilizada para crear una versin multicast de las clase socket datagrama. Mltiples clientes/servidores pueden transmitir a un grupo multicast (un grupo de direcciones IP compartiendo el mismo nmero de puerto).

Tutorial de Java

269

NetworkServer

Una clase creada para implementar mtodos y variables utilizadas en la creacin de un servidor TCP/IP.
NetworkClient

Una clase creada para implementar mtodos y variables utilizadas en la creacin de un cliente TCP/IP.
SocketImpl

Es un Interface que nos permite crearnos nuestro propio modelo de comunicacin. Tendremos que implementar sus mtodos cuando la usemos. Si vamos a desarrollar una aplicacin con requerimientos especiales de comunicaciones, como pueden se la implementacin de un cortafuegos (TCP es un protocolo no seguro), o acceder a equipos especiales (como un lector de cdigo de barras o un GPS diferencial), necesitaremos nuestra propia clase Socket. Vamos a ver un ejemplo de utilizacin, presentando un sencillo ejemplo, servidorUDP.java, de implementacin de sockets UDP utilizando la clase DatagramSocket.
import java.net.*; import java.io.*; import sun.net.*; // Implementacin del servidor de datagramas UDP. Enva una cadena // tras peticin // class servidorUDP { public static void main( String args[] ) { DatagramSocket s = (DatagramSocket)null; DatagramPacket enviap,recibep; byte ibuffer[] = new byte[100]; String cadena = Hola Tutorial de Java!\n; InetAddress IP = (InetAddress)null; int longitud = sendString.length(); int puertoEnvio = 4321; int puertoRecep = 4322; int puertoRemoto; // Intentamos conseguir la direccin IP del host try { IP = InetAddress.getByName( bregogan ); } catch( UnknownHostException e ) { System.out.println( No encuentro al host breogan ); System.exit( -1 ); } // Establecemos el servidor para escuchar en el socket 4322 try { s = new DatagramSocket( puertoRecep ); } catch( SocketException e ) { System.out.println( Error - +e.toString() ); }

270

Tutorial de Java

// Creamos un paquete de solicitud en el cliente // y nos quedamos esperando a sus peticiones recibep = new DatagramPacket( ibuffer,longitud ); try { s.receive( recibep ); } catch( IOException e ) { System.out.println( Error - +e.toString() ); } // Creamos un paquete para enviar al cliente y lo enviamos sendString.getBytes( 0,longitud,ibuffer,0 ); enviap = new DatagramPacket( ibuffer,longitud,IP,puertoEnvio ); try { s.send( enviap ); } catch( IOException e ) { System.out.println( Error - +e.toString() ); System.exit( -1 ); } // Cerramos el socket s.close(); } }

Y tambin vamos a implementar el cliente, clienteUDP.java, del socket UDP correspondiente al servidor que acabamos de presentar:
import java.net.*; import java.io.*; import sun.net.*; // Implementacin del cliente de datagramas UDP. Devuelve la salida // de los servidores // class clienteUDP { public static void main( String args[] ) { int longitud = 100; DatagramSocket s = (DatagramSocket)null; DatagramPacket enviap,recibep; byte ibuffer[] = new byte[100]; InetAddress IP = (InetAddress)null; int puertoEnvio = 4321; int puertoRecep = 4322; // Abre una conexin y establece el cliente para recibir // una peticin en el socket 4321 try { s = new DatagramSocket( puertoRecep ); } catch( SocketException e ) { System.out.println( Error - +e.toString() ); } // Crea una peticin para enviar bytes. Intenta conseguir // la direccin IP del host try { IP = InetAddress.getByName( depserver );

Tutorial de Java

271

} catch( UnknownHostException e ) { System.out.println( No encuentro el host depserver ); System.exit( -1 ); } // Enva una peticin para que responda el servidor try { enviap = new DatagramPacket( ibuffer,ibuffer.length, IP,4322 ); s.send( enviap ); } catch( IOException e ) { System.out.println( Error - +e.toString() ); } // Consigue un controlador de fichero de entrada del socket y lee // dicha entrada. Creamos un paquete descriptor para recibir el // paquete UDP recibep = new DatagramPacket( ibuffer,longitud ); // Espera a recibir un paquete try { s.receive( recibep ); } catch( IOException e ) { System.out.println( Error - +e.toString() ); System.exit( -1 ); } // Imprimimos los resultados de lo que conseguimos System.out.println( Recibido: +recibep.getLength()+ bytes ); String datos = new String( recibep.getData(),0 ); System.out.println( Datos: +datos ); System.out.println( Recibido por puerto: +recibep.getPort() ); // Cerramos la conexin y abandonamos s.close(); } }

La salida que se producir cuando ejecutemos primero el servidor y luego el cliente ser la misma que reproducimos a continuacin:
%java clienteUDP Recibido: 17 bytes Datos: Hola Tutorial de Java! Recibido por puerto: 4322

272

Tutorial de Java

Captulo 15

Arquitectura MVC

Vamos a presentar, por curiosidad ms que por otra cosa, una introduccin a la interface Observer y a la clase Observable que proporciona Java. Vamos a implementar programas basados en la arquitectura Modelo/Vista/Controlador, popularizada por el lenguaje Smalltalk. Presentamos el problema: queremos disear un programa que visualice los datos de una escena en tres dimensiones y lo pase a dos dimensiones. El programa debe ser modular y permitir vistas mltiples y simultneas de la misma escena. Cada vista debe ser capaz de presentar la escena desde diferentes puntos de vista y distintas condiciones de iluminacin. Y, ms importante todava, si alguna porcin de una escena cambia, las vistas deben actualizarse automticamente. Otra versin del problema sera el de una hoja de clculo, en donde tenemos una serie de datos que queremos ver representados grficamente, para ello dispondremos de varias vistas con grficos de lnea, de barra o de tarta, y deben actualizarse automticamente segn vayan cambiando los datos que figuran en la hoja de clculo. Ninguno de los requerimientos anteriores presenta una carga de programacin imposible. Si el cdigo que controla cada uno de los requerimientos ha de ser escrito de nuevo, sin embargo, si que supondra un esfuerzo significativo. Afortunadamente, el soporte para estas tareas ya esta proporcionado por la librera de Java, a travs del interface Observer y la clase Observable. Las funcionalidades de las dos se han inspirado, en parte, en la arquitectura Modelo/Vista/Controlador.

Arquitectura Modelo/Vista/Controlador
La arquitectura MVC (Model/View/Controller) fue introducida como parte de la versin Smalltalk-80 del lenguaje de programacin Smalltalk. Fue diseada para reducir el esfuerzo de programacin necesario en la implementacin de sistemas mltiples y sincronizados de los mismos datos. Sus caractersticas principales son que el Modelo, las Vistas y los Controladores se tratan como entidades separadas; esto hace que cualquier cambio producido en el Modelo se refleje automticamente en cada una de las Vistas. Adems del programa ejemplo que hemos presentado al principio y que posteriormente implementaremos, este modelo de arquitectura se puede emplear en sistemas de representacin grfica de datos, como se ha citado, o en sistemas CAD, en donde se presentan partes del diseo con diferente escala de aumento, en ventanas separadas.

Tutorial de Java

273

En la figura siguiente, vemos la arquitectura MVC en su forma ms general. Hay un Modelo, mltiples Controladores que manipulan ese Modelo, y hay varias Vistas de los datos del Modelo, que cambian cuando cambia el estado de ese Modelo.

Este modelo de arquitectura presenta varias ventajas: Hay una clara separacin entre los componentes de un programa; lo cual nos permite implementarlos por separado Hay un API muy bien definido; cualquiera que use el API, podr reemplazar el Modelo, la Vista o el Controlador, sin aparente dificultad. La conexin entre el Modelo y sus Vistas es dinmica; se produce en tiempo de ejecucin, no en tiempo de compilacin.

Al incorporar el modelo de arquitectura MVC a un diseo, las piezas de un programa se pueden construir por separado y luego unirlas en tiempo de ejecucin. Si uno de los Componentes, posteriormente, se observa que funciona mal, puede reemplazarse sin que las otras piezas se vean afectadas. Este escenario contrasta con la aproximacin monoltica tpica de muchos programas Java. Todos tienen un Frame que contiene todos los elementos, un controlador de eventos, un montn de clculos y la presentacin del resultado. Ante esta perspectiva, hacer un cambio aqu no es nada trivial.

Definicin de las partes

El Modelo es el objeto que representa los datos del programa. Maneja los datos y controla todas sus transformaciones. El Modelo no tiene conocimiento especfico de los Controladores o de las Vistas, ni siquiera contiene referencias a ellos. Es el propio sistema el que tiene encomendada la responsabilidad de mantener enlaces entre el Modelo y sus Vistas, y notificar a las Vistas cuando cambia el Modelo. La Vista es el objeto que maneja la presentacin visual de los datos representados por el Modelo. Genera una representacin visual del Modelo y muestra los datos al usuario. Interacta con el Modelo a travs de una referencia al propio Modelo. El Controlador es el objeto que proporciona significado a las ordenes del usuario, actuando sobre los datos representados por el Modelo. Cuando se realiza algn cambio, entra en accin, bien sea por cambios en la informacin del Modelo o por alteraciones de la Vista. Interacta con el Modelo a travs de una referencia al propio Modelo.

274

Tutorial de Java

Vamos a mostrar un ejemplo concreto. Consideremos como tal el sistema descrito en la introduccin a este captulo, una pieza geomtrica en tres dimensiones, que representamos en la figura siguiente:

En este caso, la pieza central de la escena en tres dimensiones es el Modelo. El Modelo es una descripcin matemtica de los vrtices y las caras que componen la escena. Los datos que describen cada vrtice o cara pueden modificarse (quizs como resultado de una accin del usuario, o una distorsin de la escena, o un algoritmo de sombreado). Sin embargo, no tiene nocin del punto de vista, mtodo de presentacin, perspectiva o fuente de luz. El Modelo es una representacin pura de los elementos que componen la escena. La porcin del programa que transforma los datos dentro del Modelo en una presentacin grfica es la Vista. La Vista incorpora la visin del Modelo a la escena; es la representacin grfica de la escena desde un punto de vista determinado, bajo condiciones de iluminacin determinadas. El Controlador sabe que puede hacer el Modelo e implementa el interface de usuario que permite iniciar la accin. En este ejemplo, un panel de datos de entrada es lo nico que se necesita, para permitir aadir, modificar o borrar vrtices o caras de la figura.

Observador y observable
El lenguaje de programacin Java proporciona soporte para la arquitectura MVC mediante dos clases: Observer: Es cualquier objeto que desee ser notificado cuando el estado de otro objeto sea alterado Observable: Es cualquier objeto cuyo estado puede representar inters y sobre el cual otro objeto ha demostrado ese inters

Estas dos clases se pueden utilizar para muchas ms cosas que la implementacin de la arquitectura MVC. Sern tiles en cualquier sistema en que se necesite que algunos objetos sean notificados cuando ocurran cambios en otros objetos. El Modelo es un subtipo de Observable y la Vista es un subtipo de Observer. Estas dos clases manejan adecuadamente la funcin de notificacin de cambios que necesita la arquitectura MVC. Proporcionan el mecanismo por el cual las Vistas pueden ser notificadas automticamente de los cambios producidos en el Modelo. Referencias al objeto Modelo tanto en el Controlador como en la Vista permiten acceder a los datos de ese objeto Modelo.

Tutorial de Java

275

Funciones Observer y Observable


Vamos a enumerar las funciones que intervienen en el control de Observador y Observable:

Observer
public void update( Observableobs,Object obj )

Llamada cuando se produce un cambio en el estado del objeto Observable

Observable
public void addObserver( Observer obs )

Aade un observador a la lista interna de observadores


public void deleteObserver( Observer obs )

Borra un observador de la lista interna de observadores


public void deleteObservers()

Borra todos los observadores de la lista interna


public int countObserver()

Devuelve el nmero de observadores en la lista interna


protected void setChanged()

Levanta el flag interno que indica que el Observable ha cambiado de estado


protected void clearChanged()

Baja el flag interno que indica que el Observable ha cambiado de estado


protected boolean hasChanged()

Devuelve un valor booleano indicando si el Observable ha cambiado de estado


public void notifyObservers()

Comprueba el flag interno para ver si el Observable ha cambiado de estado y lo notifica a todos los observadores
public void notifyObservers( Object obj )

Comprueba el flag interno para ver si el Observable ha cambiado de estado y lo notifica a todos los observadores. Les pasa el objeto especificado en la llamada para que lo usen los observadores en su mtodo notify().

Utilizar Observador y Observable


Vamos a describir en los siguientes apartados, como crear una nueva clase Observable y una nueva clase Observer y como utilizar las dos conjuntamente.

276

Tutorial de Java

Extender un Observable
Una nueva clase de objetos observables se crea extendiendo la clase Observable. Como la clase Observable ya implementa todos los mtodos necesarios para proporcionar el funcionamiento de tipo Observador/Observable, la clase derivada solamente necesita proporcionar algn tipo de mecanismo que lo ajuste a su funcionamiento particular y proporcionar acceso al estado interno del objeto Observable. En la clase ValorObservable que mostramos a continuacin, el estado interno del Modelo es capturado en el entero n. A este valor se accede (y ms importante todava, se modifica) solamente a travs de sus mtodos pblicos. Si el valor cambia, el objeto invoca a su propio mtodo setChanged() para indicar que el estado del Modelo ha cambiado. Luego, invoca a su propio mtodo notifyObservers() para actualizar a todos los observadores registrados.
import java.util.Observable; public class ValorObservable extends Observable { private int nValor = 0; // Constructor al que indicamos el valor en que comenzamos y los // limites inferior y superior que no deben sobrepasarse public ValorObservable( int nValor,int nInferior,int nSuperior ) { this.nValor = nValor; } // Fija el valor que le pasamos y notifica a los observadores que // estan pendientes del cambio de estado de los objetos de esta // clase, que su etado se ha visto alterado public void setValor(int nValor) { this.nValor = nValor; setChanged(); notifyObservers(); } // Devuelve el valor actual que tiene el objeto public int getValor() { return( nValor ); } }

Implementar un Observador
Una nueva clase de objetos que observe los cambios en el estado de otro objeto se puede crear implementando la interface Observer. Esta interface necesita un mtodo update() que se debe proporcionar en la nueva clase. Este mtodo ser llamado siempre que el Observable cambie de estado, que anuncia este cambio llamando a su mtodo notifyObservers(). El observador entonces, debera interrogar al objeto Observable para determinar su nuevo estado; y, en el caso de la arquitectura MVC, ajustar su Vista adecuadamente. En la clase ObservadorDeTexto, que muestra el cdigo siguiente, el mtodo notify() prime-

Tutorial de Java

277

ro realiza una comprobacin para asegurarse de que el Observable que ha anunciado un cambio es el Observable que l esta observando. Si lo es, entonces lee su estado e imprime el nuevo valor.
import java.util.Observer; import java.util.Observable; public class TextoObservador extends Frame implements Observer { private ValorObservable vo = null; public TextoObservador( ValorObservable vo ) { this.vo = vo; } public void update( Observable obs,Object obj ) { if( obs == vo ) tf.setText( String.valueOf( vo.getValor() ) ); } }

Usando Observador y Observable


Un programa indica a un objeto Observable que hay un objeto observador que debe ser notificado cuando se produzca un cambio en su estado, llamando al mtodo addObserver() del objeto Observable. Este mtodo aade el Observador a la lista de observadores que el objeto Observable ha de notificar cuando su estado se altere. En el ejemplo siguiente, en donde mostramos la clase ControlValor, ControlValor.java, vemos como se usa el mtodo addObserver() para aadir una instancia de la clase TextoObservador a la lista que mantiene la clase ValorObservable.
public class ControlValor { // Constructor de la clase que nos permite crear los objetos de // observador y observable public ControlValor() { ValorObservable vo = new ValorObservable( 100,0,500 ); TextoObservador to = new TextoObservador( vo ); vo.addObserver( to ); } public static void main( String args[] ) { ControlValor m = new ControlValor(); } }

En la siguiente secuencia, vamos a describir como se realiza la interaccin entre un Observador y un objeto Observable, durante la ejecucin de un programa: En primer lugar el usuario manipula un elemento del interface de usuario representado por el Controlador. Este Controlador realiza un cambio en el Modelo a travs de uno de sus mtodos pblicos de acceso; en nuestro caso, llama a setValue().

278

Tutorial de Java

El mtodo pblico de acceso modifica el dato privado, ajusta el estado interno del Modelo y llama al mtodo setChanged() para indicar que su estado ha cambiado. Luego llama al mtodo notifyObservers() para notificar a los observadores que su estado no es el mismo. La llamada a este mtodo puede realizarse en cualquier lugar, incluso desde un bucle de actualizacin que se est ejecutando en otro thread. Se llama a los mtodos update() de cada Observador, indicando que hay un cambio en el estado del objeto que estaban observando. El Observador accede entonces a los datos del Modelo a travs del mtodo pblico del Observable y actualiza las Vistas.

Ejemplo de aplicacin MVC


En el ejemplo siguiente, vemos como colaboran juntos Observador y Observable en la arquitectura MVC:

El Modelo de este ejemplo es muy simple. Su estado interno consta de un valor entero. Este valor, o estado, es manipulado exclusivamente a travs de mtodos pblicos de acceso. El cdigo del modelo se encuentra implementado en ValorObservable.java. Inicialmente, hemos escrito una clase simple de Vista/Controlador. La clase combina las caractersticas de una Vista (presenta el valor que corresponde al estado actual del Modelo) y un Controlador (permite al usuario introducir un nuevo valor para alterar el estado del Modelo). El cdigo se encuentra en el fichero TextoObservador.java. Podemos crear instancias de esta vista pulsando el botn superior que aparece en el applet. A travs de este diseo utilizando la arquitectura MVC (en lugar de colocar el cdigo para que el Modelo, la Vista y el Controlador de texto en una clase monoltica), el sistema puede ser fcilmente rediseado para manejar otra Vista y otro Controlador. En este caso, hemos visto una clase Vista/Controlador con una barra de desplazamiento. La posicin del marcador en la barra representa el valor actual que corresponde con el estado del Modelo y puede ser alterado a travs de movimientos del marcador sobre la barra por accin del usuario. El cdigo de esta clase se encuentra en BarraObservador.java. Se pueden crear instancias de esta clase pulsando el botn inferior del applet de esta pgina.

Tutorial de Java

279

280

Tutorial de Java

Captulo 16
Etiqueta

Aplicaciones Java

Los applets anteriores son simplemente ejemplos de lo que se puede conseguir con Java. Son un poco ms complicados que los desarrollados en el Tutorial, pero tampoco tan complicados como para que no se pueda seguir adecuandamente el cdigo fuente. Se han desarrollado sobre Windows95, por lo que no estoy seguro de que en otros sistemas se visualice todo correctamente, sobre todo teniendo en cuenta que no todas las fuentes de caracteres estn disponibles siempre. Si se desea hacer un uso comercial de estos applets, habra que adirles ms detalles de configuracin y, sobre todo, capturar todas las excepciones posibles, porque lo que en un sitio va bien, en otro puede generar una excepcin.

Vamos a desarrolllar un nuevo Componente para el AWT, para mejorar el Label que se proporciona con el AWT, aadindole al original cosas como bordes, efectos de sombreado, etc., o posibilidad de fijarle caractersticas como el color o el font de caracteres con que se presentar en pantalla. En el cdigo fuente de la clase, Etiqueta.java, hemos introducido gran cantidad de comentarios, por lo que explicaciones aqu seran redundantes, en la mayora de las sentencias para que se vaya siguiendo paso a paso la construccin y entendiendo qu hace cada uno de los mtodos y, para que resulte sencillo al lector el aadir nuevos tipos de efectos sobre el texto de la Etiqueta. El applet EjEtiqueta.java presenta las tres posibilidades de presentar texto en pantalla que hemos implementado: Texto normal, resaltado y hundido. A continuacin se muestra el resultado de la ejecucin del applet.

Tutorial de Java

281

// // // // // // // // // // // // // // // //-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y esta sujeta a cambios que pueden ser incorporados en cualquier momento, sin avisar. Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 02-Jun-1996 05:18:10 No se asume ninguna software. responsabilidad por el uso o alteracion de este Etiqueta.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

//--------------------------------------------------------------------------

import java.awt.*;

public class Etiqueta extends Panel { private String texto = ""; private Font font = null; private FontMetrics metrica = null; private Color color = Color.black; private int ancho = 0; private int alto = 0; private int sombra = 0; private boolean borde = false;

// Declaramos las variables que indican como aparecera el texto en // pantalla, para ello las hacemos "public" para que sean visibles // a otras clases, "static" para que la compartan todos los objetos // de la clase y "final" porque son valores constantes, que no van

282

Tutorial de Java

// a variar de ninguna de las maneras public static final int TextoNormal = 0; public static final int TextoResaltado = 1; public static final int TextoHundido = 2;

// Constructor basico, con una cadena vacia public Etiqueta() { this( "" ); }

// Constructor de conveniencia al que le pasamos una cadena de texto // que se presentara con los valores de defecto public Etiqueta( String Texto ) { setText( Texto ); }

// Constructor de conveniencia al que le pasamos todos los datos // para que pinte el texto como nostros queremos // Tiene la particularidad de que lo podemos utilizar como plantilla // para crear otros objetos que sean iguales a uno que ya tengamos, // porque le pasamos este nuestro objeto y nos devuelve otro con // las mismas caracteristicas // Solo fijamos las caracteristicas, no indicamos el texto a // presentar public Etiqueta( Etiqueta plantilla ) { copiaPlantilla( plantilla ); }

// Constructor de conveniencia completo, en el que le decimos // el texto que debe aparecer y las caracteristicas con que // debe presentarlo en pantalla public Etiqueta( String Texto,Etiqueta plantilla ) { copiaPlantilla( plantilla );

Tutorial de Java

283

setText( Texto ); }

// Metodo que pasa los valores de las caracteristicas con que // queremos pintar, a los atributos del objeto Texto que // vamos a hacer aparecer en pantalla, crea un objeto a partir // de otro public void copiaPlantilla( Etiqueta plantilla ) { texto = plantilla.texto; font = plantilla.font; metrica = plantilla.metrica; color = plantilla.color; ancho = plantilla.ancho; sombra = plantilla.sombra; borde = plantilla.borde; }

// Nos aseguramos de que se seleccione una fuente para pintar los // caracteres. Si no se indica una, la creamos nosotros de la // forma mas sencilla posible public void checkFont() { if( font == null ) font = new Font( "Helvetica",Font.PLAIN,12 ); }

// Fija la cadena de texto que se va a presentar, asegurandose // de que se indica con que font de caracteres queremos hacerlo, // luego copia el texto en un miembro local y hace que el panel // se repinte public void setText( String Texto ) { checkFont(); texto = Texto; repaint();

284

Tutorial de Java

// Fijamos una nueva fuente de caracteres para pintar el texto public void setFont( String nombre,int estilo,int tam ) { font = new Font( nombre,estilo,tam ); }

// Fijamos el ancho del rectangulo en donde vamos a inscribir el // texto public void setAncho( int Ancho ) { ancho = Ancho; }

// Fijamos la altura del rectangulo en donde vamos a inscribir el // texto public void setAlto( int Alto ) { alto = Alto; }

// Fijamos el color con que queremos presentar el texto en pantalla public void setColor( Color cColor ) { color = cColor; }

// Fijamos los pixels de desplazamiento que habra respecto de los // dos texto que se escriben para dar el efecto de sombra. // Para conseguir este efecto, simplemente pintamos el texto con el // color seleccionado para la sombra y luego lo volvemos a pintar // con el color del texto y desplazado tantos pixels como indique // este paramentro public void setSombra( int Sombra ) {

Tutorial de Java

285

sombra = Sombra; }

// Fija el tamano del borde del rectangulo que circunscribe al // texto que estamos presentando en pantalla public void setBorde( boolean Borde ) { borde = Borde; }

// Funciones de recuperacion, equivalentes a las del grupo 'set', // utilizadas para recuperar los atributos actueles fijados para // el texto que actualmente se presenta en pantalla public String getText() { return( texto ); }

public Font getFont() { return( font ); }

public int getAncho() { return( ancho ); }

public Color getColor() { return( color ); }

public int getSombra() { return( sombra ); }

public boolean getBorde() { return( borde );

286

Tutorial de Java

// Este metodo se llama automaticamente cuando el objeto aparece // en pantalla por primera vez o cuando se ve expuesto de nuevo, // tras haber estado tapado por otra ventana public void paint( Graphics g ) { update( g ); }

// Este metodo es llamado por el propio objeto para mostrar // en pantalla algo que haya cambiado o porque alguien lo llame // directamente, como en el caso de update public void update( Graphics g ) { // Borramos todo el area que va a ocupar el texto Color color = g.getColor(); g.clearRect( 0,0,bounds().width,bounds().height ); // Si va a llevar borde el rectangulo que delimita el espacio en // donde se va a pintar el texto, se lo ponemos if( borde ) { g.setColor( Color.lightGray ); g.draw3DRect( 0,0,bounds().width-1,bounds().height-1,false ); } drawText( g ); g.setColor( color ); }

// Este es el metodo encargado de pintar realmente el texto en la // pantalla private void drawText( Graphics g ) { // Si no hay texto que pintar, nos vamos if( texto == null ) return;

Tutorial de Java

287

// Convertimos el texto en un array de caracteres char caracteres[] = texto.toCharArray(); int Longitud = texto.length(); Color colorant = g.getColor();

// Fijamos la fuente con que queremos que aparezca el texto y // recogemos la informacion de esa fuente, porque necesitamos // conocer el ancho de cada uno de los caracteres g.setFont( font ); FontMetrics metrica = getFontMetrics( font ); int stringWidth = metrica.charsWidth( caracteres,0,Longitud );

// Controlamos el efecto que queremos darle al texto, si // resaltado o hundido (hacia abajo), para conseguirlo // pintamos dos cadenas en diferente color if( sombra == TextoResaltado ) { g.setColor( Color.white ); g.drawChars( caracteres,0,Longitud,1,metrica.getHeight()-3 ); } else if( sombra == TextoHundido ) { g.setColor( Color.white ); g.drawChars( caracteres,0,Longitud,3,metrica.getHeight()-1 ); } // Aplicamos el color mas oscuro con un ligero desplazamiento // y recuperamos el color antiguo con que estabamos trabajando g.setColor( color ); g.drawChars( caracteres,0,Longitud,2,metrica.getHeight()-2 ); g.setColor( colorant ); }

// Este metodo devuelve el tamno minimo deseable para el rectangulo // donde vamos a pintar el texto. Es importante porque nuestro

288

Tutorial de Java

// objeto no deja de ser mas que un Componente en el Layout, y el // gestor del layout a la hora de reconstruirlo, lo llama para // saber cual es el minimo tamano posible public Dimension minimumSize() { if( alto == 0 ) { if( font != null ) alto = font.getSize(); else alto = 12; } if( ancho == 0 ) ancho = 100;

return( new Dimension( ancho+3,alto+3 ) ); }

// Con este metodo pasa lo mismo, el manejador del Layout lo llama // para saber cual es el tamano que ha fijado el creador del // componente e intentar respetarlo (siempre que pueda) public Dimension preferredSize() { return( minimumSize() ); }

// Creamos un bordecito entre el texto y el borde del rectangulo // que lo circunscribe public Insets insets() { return( new Insets( 3,3,3,3 ) ); } }

//------------------------------------------ Final del fichero Etiqueta.java

Tutorial de Java

289

RELOJ DIGITAL
Seguramente, siempre que hagamos una aplicacin con una envergadura mnima, necesitaremos un temporizador. As que vamos a implementar uno que llegue a resolucin de segundos; porque, tambin es casi seguro, que en una aplicacin normal, no necesitaremos ms precisin. Vamos a implementar nuestro temporizador, Timer.java, de la forma ms general, haciendo que tenga un time-out, que se pueda resetear automtica o manualmente y lo vamos a construir como un thread individual, con lo cual, podremos tener tantos timers como queramos. La interface Temporizador, Temporizador.java, nos proporciona los mtodos que necesitamos para implementar el funcionamiento del Timer, es decir, que consideraremos al Timer como un elemento observable, de forma que notifique a los observadores cuando algn evento ocurra, como puede ser el arranque del timer, la prdida o su muerte. As, si una clase determinada quiere ser avisada cuando le ocurra algo al timer, debe implementar la interface Temporizador, dejando vacos los mtodos que corresponden a los eventos que no le interese recoger. La interface Temporizador consiste en cuatro mtodos, de los cuales los ms interesante son timerMuerto(), que se llama cuando la duracin del timer se ha cumplido y, timerIntervalo(), que ocurre a intervalos regulares, segn el tiempo que hayamos fijado. Si una clase necesita ms de un timer, puede implementar esta interface, pero entonces necesitaramos incorporar a la clase que la use mtodos para que fije e indique su nombre; de este modo, los timers estarn identificados dentro de la interface Temporizador y no habr problemas al estar varios timers corriendo a la vez. El applet que presentamos, RelojDigital.java, simplemente recoge la hora del sistema y la va actualizando cada segundo utilizando un timer. Hacemos uso tambin del componente Etiqueta que hemos desarrollado en otro ejemplo, para presentar el texto con la hora en la pantalla.

// // // // // // // No se asume ninguna software. responsabilidad por el uso o alteracion de este Timer.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

290

Tutorial de Java

// // // // // // // //

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 23-Oct-1996 014:53:09

//-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y esta sujeta a cambios que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

import java.lang.*; import java.util.*;

public class Timer implements Runnable { private Date inicio,parada; private Thread thread = null; private int duracion = 0; private int duracionAnt = 0; private int intervalo = 1000; private boolean repeticion = false; private boolean enEjecucion = false; private Temporizador handler = null;

// Contructor basico, usado por defecto Timer() { }

// Los siguientes constructores son de conveniencia, porque todas // las caracteristicas del timer se pueden ver y modificar a // traves de los metodos get y set // Constructor de conveniencia que acepta un tiempo de duracion Timer( int tiempo ) { setDuracion( tiempo );

Tutorial de Java

291

// Constructor de conveniencia que acepta un Temporizador Timer( Temporizador Handler ) { setHandler( Handler ); }

// Constructor de conveniencia que acepta un temporizador y una // duracion Timer( int tiempo,Temporizador Handler ) { setDuracion( tiempo ); setHandler( Handler ); }

// Fija el numero se segundos que correra el timer public void setDuracion( int tiempo ) { duracion = tiempo; }

// Fija el objeto que ha de ser notificado de los eventos que // sucedan al timer public void setHandler( Temporizador Handler ) { handler = Handler; }

// Fija el numero de milisegundos entre pulsos del timer public void setIntervalo( int Intervalo ) { intervalo = Intervalo; }

292

Tutorial de Java

// Funciones "get" para recoger los datos de las caracteristicas // que se han fijado antes public int getDuration() { return( duracion ); }

public Temporizador getHandler() { return( handler ); }

public int getIntervalo() { return( intervalo ); }

// Devuelve el numero de segundos que han transcurrido desde que // se arranco el timer public int getElapsed() { return( calculaLapso( new Date() ) ); }

// Este metodo permite resetear el timer antes de relanzarlo. Se // podria usar el metodo setDuracion, pero este es mas corto // y elegante public void resetDuracion() { duracion = duracionAnt; }

// Aqui creamos un nuevo thread para correr el Timer. Lo incializamos // con "this" de forma que el metodo run() se llame inmediatamente // como comience la ejecucion del thread public void start() { thread = new Thread( this ); thread.start();

Tutorial de Java

293

// Aqui almacenamos el momento en que se llama a este metodo. // Tambien comprobamos si hay algun Temporizador asociado al Timer // que estamos parando, en cuyo caso, notificamos a los observadores // de este Timer que lo hemos detenido (para eso esta la interface // Temporizador, que debera estar implementada en las clases que // miren a este Timer) public void stop() { enEjecucion = false; parada = new Date(); if ( handler != null ) handler.timerParado( this ); }

public void run() { enEjecucion = true; duracionAnt = duracion;

// Arrancamos el Timer y lo notificamos a las clases que esten // poendientes inicio = new Date(); if( handler != null ) handler.timerArrancado( this );

while( enEjecucion ) { // Esperamos el tiempo que nos hayan dicho en la configuracion // del intervalo try { esperar( intervalo ); } catch( InterruptedException e ) { return; }

294

Tutorial de Java

// Cuando se cumple el intervalo, avisamos a las clases que // esten pendientes. Si esas clases no quieren hacer nada // con este evento periodico, es suficiente con que no lo // sobrecarguen, que se quede vacio if( handler != null ) handler.timerIntervalo( this );

// Si no indicamos una duracion para el Timer, estara // corriendo indefinidamente if( duracion > 0 ) { // Comprobamos si el Timer esta muerto ya, para no // tener que matarlo if( estaMuerto() ) { // Si esta muerto, lo propagamos if( handler != null ) handler.timerMuerto( this );

// Aqui comprobamos si se quiere una repeticion // automatica, en cuyo caso, volvemos a arrancar // el Timer if( repeticion ) { enEjecucion = true; inicio = new Date(); if( handler != null ) handler.timerArrancado( this ); } else { enEjecucion = false; } } }

Tutorial de Java

295

} }

// Metodos que nos informan del estado del Timer public boolean estaCorriendo() { return( enEjecucion ); }

public boolean estaParado() { return( !enEjecucion ); }

public boolean estaMuerto() { int segundos = 0;

// Calculamos el intervalo de tiempo que ha transcurrido desde // que se ha arrancado el Timer segundos = calculaLapso( new Date() );

if( segundos >= duracion ) return( true ); else return( false ); }

private int calculaLapso( Date actual ) { Date dfinal; int segundos = 0;

if( enEjecucion ) dfinal = actual; else dfinal = parada;

296

Tutorial de Java

// Si se quiere mas precision, en vez de Date(), se puede // utilizar System.currentTimeMillis(), que proporciona // muchisima mas resolucion segundos += ( dfinal.getHours() - inicio.getHours() ) * 3600; segundos += ( dfinal.getMinutes() - inicio.getMinutes() ) * 60; segundos += ( dfinal.getSeconds() - inicio.getSeconds() ); return( segundos ); }

// Aqui implementamos la espera. El lapso en milisegundos se lo // pasamos al metodo wait() del thread // Este metodo es sincronizado porque sino salta una excepcion // interna en el interprete de Java. No esta muy bien documentado // el porque, pero parece ser que no se puede llamar al metodo // wait() de un thread desde otro que no sea sincronizado private synchronized void esperar( int lapso ) throws InterruptedException { this.wait( lapso ); } }

//--------------------------------------------- Final del fichero Timer.java

// // // // // // // // // // // // Compilador: javac 1.0 No se asume ninguna software. responsabilidad por el uso o alteracion de este Temporizador.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

Tutorial de Java

297

// // //

Autor: Agustin Froufe Creacion: 23-Oct-1996 13:56:30

//-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y esta sujeta a cambios que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

// // Interface que vamos a utilizar en la implementacion del timer, con el // que luego generaremos el reloj digital de demostracion // Esta interface debe ser implementada por cualquier clase que desee // ser informada de los eventos que genere el Timer, en nuetro caso el // reloj // public interface Temporizador { public void timerArrancado( Timer timer ); public void timerParado( Timer timer ); public void timerMuerto( Timer timer ); public void timerIntervalo( Timer timer ); }

//------------------------------------- Final del fichero Temporizador.java

// // // // // // // // // // // No se asume ninguna software. responsabilidad por el uso o alteracion de este RelojDigital.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

298

Tutorial de Java

// // // //

Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 02-Jun-1996 05:18:10

//-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y esta sujeta a cambios que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

import java.awt.*; import java.lang.*; import java.util.*; import java.applet.*;

public class RelojDigital extends Applet implements Temporizador { private Timer timer; private Etiqueta horaActual; private Color fondo;

// Este es el metodo main() para que podamos ejecutar la aplicacion // desde la linea de comandos public static void main( String args[] ) { RelojDigital reloj = new RelojDigital();

reloj.init(); reloj.start();

// Craemos un frame para que se pueda visualizar el reloj Frame frame = new Frame( "Reloj Digital" ); frame.add( "Center",reloj ); frame.resize( 50,20 ); frame.show(); }

public void init() {

Tutorial de Java

299

// Cargamos el parametro que nos indica el color de fondo que // queremos para el reloj, si no se especifica o no se indica // correctamente, fijamos uno por defecto // Lo adecuado, en caso de presentar el applet sobre una pagina // de un navegador es que este parametro coincida con el que // se especifique para el color de Background de esa pagina String Fondo = getParameter( "Fondo" ); if( Fondo == null ) fondo = Color.lightGray; else { try { fondo = new Color( Integer.parseInt( Fondo,16 ) ); } catch( NumberFormatException e ) { fondo = Color.lightGray; } } setBackground( fondo );

// Nos creamos un timer, para actualizar el reloj cada segundo Timer timer = new Timer( this );

// Creamos un objeto Etiqueta, para que nuestro reloj aparezca // con un texto decente y bonito horaActual = new Etiqueta(); horaActual.setFont( "Helvetica",Font.PLAIN,18 ); horaActual.setColor( new Color( 128,0,0 ) ); horaActual.setAlto( 25 ); horaActual.setAncho( 100 ); horaActual.setSombra( Etiqueta.TextoHundido ); horaActual.setBorde( true ); add( horaActual );

// Arrancamos el timer para que el reloj empiece a marchar timer.start(); }

300

Tutorial de Java

// En cada aviso de que nuestro Timer ha llegado al intervalo de // tiempo que nosotros hemos fijado, cogemos la hora del sistema // y la presentamos public void timerIntervalo( Timer t ) { Date actual = new Date(); int horas = actual.getHours(); int minutos = actual.getMinutes(); int segundos = actual.getSeconds(); int hora; String tiempo = "";

// Los "ifs" que siguen son para formatear la hora // correctamente y que siempre ocupe lo mismo en la // Etiqueta, para que no salten las horas de posicion al // presentarse en pantalla if( horas > 12 ) hora = horas - 12; else hora = horas; if( hora < 10 ) tiempo += "0"; tiempo += hora; tiempo += ":";

if( minutos < 10 ) tiempo += "0"; tiempo += minutos + ":";

if( segundos < 10 ) tiempo += "0"; tiempo += segundos;

if( horas > 12 ) tiempo += " pm";

Tutorial de Java

301

else tiempo += " am";

horaActual.setText( tiempo ); }

// Sobrecargamos los metodos que no nos interesan de la interface // Temporizador, porque solamente vamos a utilizar la que genera // un evento cada cierto intervalo de tiempo, las demas no tienen // interes en este ejemplo public void timerArrancado( Timer t ) { }

public void timerParado( Timer t ) { }

public void timerMuerto( Timer t ) { }

public void paint( Graphics g ) { g.setColor( fondo ); g.fillRect( 0,0,size().width,size().height ); } }

//-------------------------------------- Final del fichero RelojDigital.java

302

Tutorial de Java

PERSIANA
Este applet presenta informacin desplazndose a travs de la zona de pantalla que constituye el applet. Podemos incorporar cualquier tipo de informacin para que sea presentada en pantalla, tal como se muestra en el ejemplo de esta misma pgina. Se pueden conseguir mltiples efectos, a travs de la seleccin de fuentes y utilizacin de colores. El texto que se presenta en la pantalla va incluido en el fichero de cdigo fuente, Persiana.java, y no en la llamada al applet. Adems, se puede incorporar un dibujo de fondo, que ser presentado a la vez que se ejecute el applet. El cdigo de este applet es muy sencillo y fcil de comprender, a pesar de que el efecto que se visualiza en pantalla sugiera una programacin complicada.

// // // // // // // // // // // // // // // //-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y esta sujeta a cambios que pueden ser incorporados en cualquier momento, sin avisar. Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 17-Oct-1996 06:22:56 No se asume ninguna software. responsabilidad por el uso o alteracion de este Persiana.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

//--------------------------------------------------------------------------

import java.awt.*;

Tutorial de Java

303

import java.applet.Applet;

public class Persiana extends Applet implements Runnable { Image imagenFondo; Image offI; Graphics offG; int posicion; String texto[]; Font font; Font font1; Font font2; Font font3; boolean puedePintar; Thread thrd; MediaTracker mdt;

public void init() { mdt = new MediaTracker( this );

// Instanciacin de los objetos fuente que vamos a utilizar durante // la presentacin del texto en su desplazamiento por la persiana font = new Font( "Arial",0,15 ); font1 = new Font( "Helv",1,23 ); font2 = new Font( "TimesNewRoman",1,30 ); font3 = new Font( "Arial",1,16 );

// Cadenas de texto correspondientes al primer grupo de informacin // que se va a presentar en la ventana texto[0] = "Este Tutorial de Java proporciona suficientes conocimientos"; texto[1] = "para que, tanto los programadores noveles como aquellos que ya"; texto[2] = "conocen las bondades del lenguaje Java, puedan acercarse en un"; texto[3] = "caso y profundizar en el otro, en los ms diversos aspectos del"; texto[4] = "lenguaje. Se recorren casi todas las caractersticas de Java,";

304

Tutorial de Java

texto[5] = "desde las ms sencillas como puede ser la descripcin de los"; texto[6] = "tipos soportados por el lenguaje, hasta las ms complicadas"; texto[7] = "como la implementacin de un entorno cliente-servidor o el"; texto[8] = "desarrollo de un modelo de arquitectura Modelo-Vista completo.";

// Informacin que se presentar a continuacin del ttulo // del segundo bloque de informacin de la persiana texto[9] = "Se hace un profundo recorrido por el Abstract Window Toolkit,"; texto[10] = "presentado todos los elementos que lo componen en su actual"; texto[11] = "versin, con mltiples lneas de cdigo fuente para que el"; texto[12] = "navegante pueda ir comprobando cmo se usan y cmo Java"; texto[13] = "utiliza los recursos que pone en manos del programador."; texto[14] = "Tambin hay mltiples applets Java en funcionamiento a lo"; texto[15] = "largo del Tutorial, de forma que se puede ir comprobando"; texto[16] = "la apariencia que presentan los applets sobre el browser que"; texto[17] = "estemos utilizando.";

// Bloque final de informacin texto[18] = "Como compendio a todo lo desarrollado en este Tutorial de"; texto[19] = "Java, se presentan varias aplicaciones autnomas y algunos"; texto[20] = "applets listos para utilizar, con toda la informacin"; texto[21] = "necesaria para su implementacin y uso.";

// Recuperamos la imagen que se va a utilizar como fondo de la // cadena que represente el ttulo principal de la persiana imagenFondo = getImage( getCodeBase(),"fondo.gif" ); try { offI = createImage( 700,55 ); offG = offI.getGraphics(); } catch( Exception e ) { offG = null; }

Tutorial de Java

305

return; }

public void update( Graphics g ) { if( puedePintar ) paint( g ); }

public void paint( Graphics g ) { if( offG != null ) { paintApplet( offG ); g.drawImage( offI,0,0,this ); return; } paintApplet( g ); }

public void paintApplet( Graphics g ) { // Presentamos la imagen de fondo y el ttulo principal que // queremos asignar a la infromacin que va a ir presentando // la persiana. // Para hacer un poco ms atractiva la visualizacin del ttulo // hacemos que aparezca una especie de sombra de la cadena de // texto. Para ello, simplemente la volvemos a escribir pero // desplazada g.setColor( Color.black ); g.fillRect( 0,0,600,700 ); g.setFont( font2 ); g.drawImage( imagenFondo,0,-posicion,this );

306

Tutorial de Java

g.setColor( Color.white ); g.drawString( "Tutorial de Java",200,50-posicion ); g.setColor( Color.blue ); g.drawString( "Turorial de Java",198,48-posicion ); g.setColor( Color.orange ); g.setFont( font1 ); g.fillRect( 130,60-posicion,190,24 ); g.setColor( Color.black ); g.drawString( "Presentacin",150,80-posicion ); g.setColor( Color.white ); g.setFont( font ); for( int i=0; i < 9; i++ ) g.drawString( texto[i],5,105+i*19-posicion );

// Creamos otro rectngulo de color para colocar el ttulo que // encabeza al grupo de cadenas que van a seguir desplazndose // por la persiana g.setColor( Color.white ); g.fillRect( 120,265-posicion,180,26 ); g.setColor( Color.red ); g.setFont( font1 ); g.drawString( "AWT",180,287-posicion ); g.setColor( Color.white ); g.setFont( font ); for( int j=9; j < 18; j++ ) g.drawString( texto[j],5,140+j*19-posicion );

// Presentamos el siguiente grupo de informacin, creando una // cabecera para indicar de qu se trata la informacin que va a // aparecer en la persiana g.setColor( Color.red ); g.fillRect( 120,471-posicion,200,30 ); g.setColor( Color.white ); g.setFont( font1 ); g.drawString( "Ejemplos",165,493-posicion ); g.setColor( Color.white );

Tutorial de Java

307

g.setFont( font ); for( int j=18; j < 22; j++ ) g.drawString( texto[j],5,177+j*19-posicion );

// Esta es la misma informacin que la del principio g.setFont( font2 ); g.drawImage( imagenFondo,0,600-posicion,this ); g.setColor( Color.white ); g.drawString( "Tutorial de Java",200,650-posicion ); g.setColor( Color.blue ); g.drawString( "Tutorial de Java",198,648-posicion ); }

public void start() { // Creamos un nuevo thread y lo arrancamos thrd = new Thread( this ); thrd.start(); }

public void stop() { thrd.stop(); }

public void run() { for( puedePintar=false; true; puedePintar=false ) { try { puedePintar = true; repaint(); // Si estamos al principio, alargamos un poco la espera if( posicion == 0 ) { Thread.currentThread();

308

Tutorial de Java

Thread.sleep( 5000 ); } posicion++; // Cuando lleguemos al final del texto, volvemos al // principio, para repetir todos los mensajes if( posicion == 600 ) posicion = 0; Thread.currentThread(); // Aqui podemos hacer ms rpido o ms lento el // desplazamiento del texto por la persiana Thread.sleep( 100 ); } catch( InterruptedException e ) { ; } } }

// El constructor del applet se limita a crear el array de texto que // contendr todas las cadenas o lneas de texto que se irn pasando // por la ventana public Persiana() { texto = new String[22]; } }

//------------------------------------------ Final del fichero Persiana.java

Tutorial de Java

309

SOLAPAS
Este applet nos muestra cmo se pueden incorporar nuevos Componentes al AWT. Hemos diseado un sistema de solapas o Tabs, como se suelen reconocer, para incorporar a nuestras aplicaciones. En el fichero EjFicha.java, se encuentra el cdigo que muestra cmo se usa y que corresponde a la ejecucin del applet que aparece en esta misma pgina. Los ficheros de cdigo fuente que se emplean para la implementacin del sistema de solapas propuesto son: CuerpoFicha.java Se encarga de presentar el cuerpo de la ficha que corresponde a una solapa. Nos permite incorporar cualquier otro elemento que deseemos a esa ficha, tal como muestra el ejemplo, en el cual hemos aadido diferentes componentes a cada una de las fichas que se abren al seleccionar las solapas. Solapa.java Se encarga de gestionar cada una de las solapas. Nos indica cuando se ha pulsado en una solapa y tiene implementados los mtodos necesarios para indicarnos cuando una solapa est selecciona o no, cuando se pica con el ratn dentro de una solapa, o para resaltar la solapa cuando se selecciona, entre otros. Ficha.java Este fichero contiene una clase compuesta de dos Objetos, uno de clase Solapa y otro de clase CuerpoFicha. Se encarga de gestionar la presentacin de la Ficha al completo, de forma que cuando se selecciona una solapa, se presenta el contenido del objeto CuerpoFicha que tiene asociado ese objeto Solapa que se ha elegido. ListaFichas.java Contiene un objeto Vector con todas las fichas que hayamos incorporado al applet. Aqu es donde podemos indicar que el applet contendr ms o menos Fichas, que estarn representadas por las Solapas que el usuario podr elegir con el ratn.

310

Tutorial de Java

// // // // // // No se asume ninguna este // software. ningun // de // do // // // // // // //------------------------------------------------------------------------// Esta informacion no es necesariamente definitiva y est bios // sujeta a camCompilador: javac 1.0 Autor: Agustin Froufe Creacion: 11-Sep-1996 11:35:43 responsabilidad por el uso o alteracion de CuerpoFicha.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de

tipo de su funcionamiento y en ningun caso sera el autor responsable daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuan-

que pueden ser incorporados en cualquier momento, sin avisar.

//-------------------------------------------------------------------------

import java.awt.*;

// Presenta el cuerpo de la ficha en blanco, para que se puedan insertar los // componentes que correspondan // Es el contenedor del control tabular que estamos implementando public class CuerpoFicha extends Panel {

// Lo unico que se pasa al constructor de esta clase es el color de // fondo con que queremos que aparezca public CuerpoFicha( Color colorCuerpo ) { setBackground( colorCuerpo ); }

Tutorial de Java

311

public void paint( Graphics g ) { Rectangle area;

area = bounds();

// Pintamos el fondo del cuerpo de la ficha con el color // que se haya indicado en el constructor de la clase g.setColor( getBackground() ); g.fillRect( area.x,area.y + 1,area.width,area.height-1 );

// Pintamos una linea brillante el los lados superior e // izquierdo g.setColor( Color.white ); g.drawLine( area.x,area.y,area.x+area.width,area.y ); g.drawLine( area.x,area.y,area.x,area.y+area.height );

// Pintamos una linea gris en los lados inferior y derecho // para dar sensacion de sombra g.setColor( Color.gray ); g.drawLine( area.x,area.y+area.height-1, area.x+area.width-1,area.y+area.height-1 ); g.drawLine( area.x+area.width-1,area.y+area.height-1, area.x+area.width-1,area.y+1 );

// Pintamos la sombra de la sobra de los lados inferior y // derecho, para aumentar la sensacion de tres dimensiones y // profundidad g.setColor( Color.black ); g.drawLine( area.x,area.y+area.height, area.x+area.width,area.y+area.height ); g.drawLine( area.x+area.width,area.y+area.height, area.x+area.width,area.y+1 ); } }

312

Tutorial de Java

//--------------------------------------- Final del fichero CuerpoFicha.java

// // // // // // No se asume ninguna este // software. ningun // de // do // // // // // // //------------------------------------------------------------------------// Esta informacion no es necesariamente definitiva y est bios // sujeta a camCompilador: javac 1.0 Autor: Agustin Froufe Creacion: 12-Sep-1996 11:57:07 responsabilidad por el uso o alteracion de Solapa.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de

tipo de su funcionamiento y en ningun caso sera el autor responsable daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuan-

que pueden ser incorporados en cualquier momento, sin avisar.

//-------------------------------------------------------------------------

import java.awt.*;

// Este objeto se utiliza para seleccionar cada una de las fichas // Pulsando con el raton sobre una de ellas, se visualizara el // contenido de la ficha que tiene adosada public class Solapa extends Canvas { protected String rotulo; protected Font seleFont; nada protected Font normFont; // Font del estado normal de la solapa // Rotulo indicador de la solapa // Font cuando la solapa esta seleccio-

Tutorial de Java

313

protected boolean seleccionada; // Indicador de seleccion de la solapa

public Solapa( String rotulo,Font normFont, Font seleFont,Color captionColor,Color bgColor ) { super(); this.rotulo = rotulo; this.normFont = normFont; this.seleFont = seleFont; setForeground( captionColor ); setBackground( bgColor ); seleccionada = false; }

public void pintaRotulo( Graphics g ) { int rotuloX; int rotuloY; FontMetrics fm;

// Comprobamos la fuente de caracteres que vamos a utilizar en la // presentacion del rotulo, en funcion del estado de la ficha if( seleccionada ) { g.setFont( seleFont ); fm = g.getFontMetrics( seleFont ); } else { g.setFont( normFont ); fm = g.getFontMetrics( normFont ); }

// Calculamos las coordenadas en que vamos a presentar el rotulo // Teniendo en cuenta la longitud del texto del rotulo y // la fuente de caracteres rotuloX =

314

Tutorial de Java

bounds().x +( (bounds().width - fm.stringWidth(rotulo) ) / 2); rotuloY = bounds().y + ( bounds().height / 2 ) fm.getDescent() + ( fm.getHeight() / 2 );

// Fijamos el color y pintamos el rotulo g.setColor( getForeground() ); g.drawString( rotulo,rotuloX,rotuloY ); }

// Pinta la solapa en base a pares de lineas para proporcionar // sensacion de tres dimensiones, en caso de que este seleccionada // se pintara dos pixels mas grande para que de la sensacion // de que esta delante del resto de fichas public void pintaSolapa( Graphics g ) { int delante; Rectangle area;

delante = ( seleccionada ) ? 2 : 0; area = bounds(); g.setColor( getBackground() );

// Pintamos el rectangulo que va a ocupar la solapa if ( !seleccionada ) { g.fillRect( area.x,area.y+2, area.width,area.height-2 ); g.drawLine( area.x+1,area.y+1,area.width+area.x-2,area.y+1 ); } else { g.fillRect( area.x-1,area.y,area.width+2,area.height ); g.drawLine( area.x,area.y-1,area.x+area.width,area.y-1 ); }

// left side, rounded corner and top side lit g.setColor( Color.white );

Tutorial de Java

315

g.drawLine( area.x-delante,area.y+area.height-2, area.x-delante,area.y+2-delante ); g.drawLine( area.x-delante,area.y+2-delante, area.x+2-delante,area.y-delante ); g.drawLine( area.x+2-delante,area.y-delante, area.x+area.width-2+delante,area.y-delante );

if( !seleccionada ) { // Pintamos la sombra del lado derecho g.setColor( Color.gray ); g.drawLine( area.x+area.width-1,area.y, area.x+area.width-1,area.y+area.height-2 );

// Pintamos la sombra de la sombra del lado derecho // para dar mas sensacion de profundidad g.setColor( Color.black ); g.drawLine( area.x+area.width,area.y+2, area.x+area.width,area.y+area.height-2 ); } else { // Pintamos la sombra del lado derecho g.setColor( Color.gray ); g.drawLine( area.x+area.width-1+delante,area.y-delante, area.x+area.width-1+delante,area.y+area.height-2 );

// Pintamos la sombra de la sombra del lado derecho g.setColor( Color.black ); g.drawLine( area.x+area.width+delante,area.y+2-delante, area.x + area.width+delante,area.y + area.height-2 );

// Como la solapa esta seleccionada, pintamos en color del // fondo la linea que une la solapa anterior con la // siguiente, dando sensacion de que la solapa esta // efectivamente pegada a la ficha

316

Tutorial de Java

g.setColor( getBackground() ); g.drawLine( area.x-delante+1,area.y+area.height, area.x+area.width,area.y+area.height ); } }

// Devuelve el texto del rotulo de la solapa public String getRotulo() { return( rotulo ); }

// Devuelve el punto correspondiente a la posicion final de la // solapa public Point getFinal() { return( new Point(bounds().x+bounds().width+2,bounds().y ) ); }

// Devuelve la altura de la solapa public int getAlto() { return( size().height ); }

// Devuelve la fuente de caracteres con que se pinta el rotulo de // la solapa cuando esta seleccionada public Font getFuenteSel() { return( seleFont ); }

// Indica si la posicion que se pasa como parametro esta dentro // de los limites de la solapa o no public boolean estaDentro( int x,int y ) {

Tutorial de Java

317

return( bounds().inside( x,y ) ); }

// Indica si la solapa esta seleccionada o no public boolean estaSeleccionada() { return( seleccionada ); }

// Cuando pulsamos el raton, nos devuelve true si lo hemos hecho // sobre la solapa o false si lo hemos hecho fuera del rectangulo // que ocupa la solapa public boolean mouseDown( Event evt,int x,int y ) { if( ( estaDentro( x,y ) ) && ( !estaSeleccionada() ) ) return( true ); else return( false ); }

public void paint( Graphics g ) { pintaSolapa( g ); pintaRotulo( g ); }

// Indica que la solapa esta seleccionada public void seleccionar() { seleccionada = true; }

// Indica que la solapa deja de estar seleccionada public void deSeleccionar() { seleccionada = false;

318

Tutorial de Java

} }

//-------------------------------------------- Final del fichero Solapa.java

// // // // // // No se asume ninguna este // software. ningun // de // do // // // // // // //------------------------------------------------------------------------// Esta informacion no es necesariamente definitiva y est bios // sujeta a camCompilador: javac 1.0 Autor: Agustin Froufe Creacion: 12-Sep-1996 11:43:24 responsabilidad por el uso o alteracion de Ficha.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de

tipo de su funcionamiento y en ningun caso sera el autor responsable daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuan-

que pueden ser incorporados en cualquier momento, sin avisar.

//-------------------------------------------------------------------------

import java.awt.*; import java.lang.String; import CuerpoFicha; import Solapa;

// Crea un objeto que sera seleccionado cuando se pulse con el raton // en su solapa. Contiene a dos objetos, uno de tipo Solapa y otro de

Tutorial de Java

319

// tipo cuerpoFicha, de forma que al seleccionar en el primero // aparecera la solapa marcada y el contenido del objeto cuerpoFichero // asociado a esa solapa public class Ficha extends Panel { public static final int SOLAPA_SELECCIONADA = 2000; protected Solapa solapa; protected CuerpoFicha cuerpoFicha;

public Ficha( String rotulo,Font normFont,Font seleFont, Color colorRotulo,Color colorFicha ) { super(); solapa = new Solapa( rotulo,normFont,seleFont, colorRotulo,colorFicha ); cuerpoFicha = new CuerpoFicha( colorFicha ); }

// Devuelve el rotulo identificador de la solapa public String getRotulo() { return( solapa.getRotulo() ); }

// Devuelve los limites del cuerpo de la Ficha public Rectangle getBordesFicha() { return( cuerpoFicha.bounds() ); }

// Devuelve los limites que ocupa la solapa public Rectangle getBordesSolapa() { return( solapa.bounds() ); }

// Devuelve el punto final, ams alejado de la solapa

320

Tutorial de Java

public Point getFinalSolapa() { return( solapa.getFinal() ); }

public boolean handleEvent( Event evt ) { // La solapa devolvera true solamente si ha sido seleccionada // en ese momento por el evento, aunque lo que siempre haremos // sera notificarlo a ListaFichas, por lo que devolvemos false // y el evento SOLAPA_SELECCIONADA se propagara hacia arriba if( solapa.handleEvent( evt ) ) { evt.id = SOLAPA_SELECCIONADA; evt.arg = this; } return( false ); }

// Indica si la ficha esta seleccionada o no public boolean estaSeleccionada() { return( solapa.estaSeleccionada() ); }

public void medidas( Point solapaXY,Graphics g, Dimension appTam,Point appPos ) { FontMetrics fm; int anchoRotulo; int altoRotulo;

fm = g.getFontMetrics( solapa.getFuenteSel() ); anchoRotulo = fm.stringWidth( solapa.getRotulo() ); altoRotulo = fm.getHeight(); solapa.reshape( solapaXY.x,solapaXY.y,anchoRotulo+altoRotulo, altoRotulo+(altoRotulo / 3) );

Tutorial de Java

321

cuerpoFicha.reshape( appPos.x,appPos.y+solapa.getAlto(), appTam.width-1,appTam.height-solapa.getAlto()-1 ); }

public void paint( Graphics g ) { if( estaSeleccionada() ) cuerpoFicha.paint( g ); solapa.paint( g ); }

public void seleccionar() { solapa.seleccionar(); }

public void deSeleccionar() { solapa.deSeleccionar(); } }

//--------------------------------------------- Final del fichero Ficha.java

// // // // // // No se asume ninguna este // software. ningun // de // do responsabilidad por el uso o alteracion de ListaFichas.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de

tipo de su funcionamiento y en ningun caso sera el autor responsable daos o perjuicios que se deriven del mal uso del software, aun cuan-

322

Tutorial de Java

// // // // // //

este haya sido notificado de la posibilidad de dicho dao.

Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 12-Sep-1996 12:46:13

//------------------------------------------------------------------------// Esta informacion no es necesariamente definitiva y est bios // sujeta a cam-

que pueden ser incorporados en cualquier momento, sin avisar.

//-------------------------------------------------------------------------

import java.awt.*; import java.util.Vector; import Ficha;

public class ListaFichas extends Panel { protected Vector fichas; protected Point posicion; protected Dimension tama; protected Font normFont; protected Font seleFont; protected Color fgColor; protected Color bgColor; protected Panel contenido;

public ListaFichas( Point posicion,Dimension tama,Font normFont, Font seleFont,Color fgColor,Color bgColor, String primerRotulo,Panel primerPanel ) {

super();

// Aqui fijamos el layout a null, porque el metodo medidas() // sera el que controle ese layout setLayout( null );

Tutorial de Java

323

// Nos creamos un objeto Vector, que nos va a permitir la // manipulacion de la lista de las fichas fichas = new Vector(); this.posicion = posicion; this.tama = tama; this.normFont = normFont; this.seleFont = seleFont; this.fgColor = fgColor; this.bgColor = bgColor; contenido = new Panel(); add( contenido ); contenido.setLayout( new CardLayout() );

// Incorporamos la primera ficha y la dejamos seleccionada addFicha( primerRotulo,primerPanel ); primeraFicha().seleccionar(); }

// En este metodo incorporamos una ficha a la lista, con todos // los valores por defecto public synchronized void addFicha( String rotulo,Panel panel ) { fichas.addElement( new Ficha( rotulo,normFont,seleFont, fgColor,bgColor) ); contenido.add( rotulo,panel ); }

// En este metodo, nos permitimos especificar los colores de // primer plano y de fondo con que se va a presentar la ficha // en pantalla, a demas de incorporarla a la lista de fichas public synchronized void addFicha( Color fgColor,Color bgColor, String rotulo,Panel panel ) { fichas.addElement( new Ficha( rotulo,normFont,seleFont, fgColor,bgColor ) );

324

Tutorial de Java

contenido.add( rotulo,panel ); }

// Presenta el contenido de la ficha que se ha seleccionado public void presentaFichaSeleccionada() { ( (CardLayout)contenido.getLayout() ).show( contenido, ( getSeleccionada().getRotulo() ) ); }

// Devuelve la primera ficha de la lista public synchronized Ficha primeraFicha() { return( (Ficha)fichas.firstElement() ); }

// Devuelve el contenido del control Tabular completo public Panel getContenido() { return contenido; }

// Devuelve la siguiente ficha a la que se le pasa como parametro public synchronized Ficha getSigFicha( Ficha f ) { Ficha nextF;

// La numeracion de los elementos empieza en 0 if( fichas.indexOf( f ) < (fichas.size() - 1) ) nextF = fichaAt( fichas.indexOf(f)+1 ); else nextF = null;

return( nextF ); }

Tutorial de Java

325

// Devuelve la ficha que actualmente esta seleccionada public synchronized Ficha getSeleccionada() { Ficha f;

// Nos colocamos en la primera y vamos comprobando // ficha por ficha cual es la que esta seleccionada f = primeraFicha(); while( (!f.estaSeleccionada()) && (f != null) ) f = getSigFicha( f );

return( f ); }

// Devuelve informacisn de si el evento se ha controlado o no // en este manejador public boolean handleEvent( Event evt ) { boolean manejado; Ficha f;

// Localizamos la ficha que ha generado el evento f = primeraFicha(); manejado = f.handleEvent( evt ); if( !manejado ) f = getSigFicha( f ); while( (f != null) && (!manejado) ) { manejado = f.handleEvent( evt ); f = getSigFicha( f ); }

// Si han seleccionado otra ficha, liberamos la actualmente // seleccionada y seleccionamos la que nos llega en el // evento, que correponde a la solapa que se ha pulsado if( evt.id == Ficha.SOLAPA_SELECCIONADA )

326

Tutorial de Java

{ getSeleccionada().deSeleccionar(); ( (Ficha)evt.arg ).seleccionar(); presentaFichaSeleccionada(); repaint(); manejado = true; }

// Si aqui no se ha controlado el evento, se intenta que lo // controle directamente alguno de los componentes que se // hayan colocado en la ficha if( !manejado ) manejado = contenido.handleEvent( evt );

return( manejado ); }

// Devuelve la ultima ficha del control Tabular public synchronized Ficha ultimaFicha() { return( (Ficha)fichas.lastElement() ); }

// Calcula el area que ocupa todo el Control Tabular public void medidas( Graphics g ) { Ficha f; Point solapaXY;

// Calcula el area ocupada por las solapas tama = size(); f = primeraFicha(); solapaXY = new Point( posicion.x+2,posicion.y+2 ); f.medidas( solapaXY,g,tama,posicion ); if( f != ultimaFicha() ) {

Tutorial de Java

327

do { solapaXY = f.getFinalSolapa(); f = getSigFicha( f ); f.medidas( solapaXY,g,tama,posicion ); } while( f != ultimaFicha() ); }

// Calcula el area ocupada por los paneles que forman el // cuerpo de las fichas Rectangle r = primeraFicha().getBordesSolapa(); contenido.move( r.x+3,r.y+r.height+3 ); r = primeraFicha().getBordesFicha(); contenido.resize( r.width-10,r.height-10 ); }

public void paint( Graphics g ) { Ficha f;

medidas( g ); f = primeraFicha(); while( f != null ) { if( !f.estaSeleccionada() ) f.paint( g ); f = getSigFicha( f ); } getSeleccionada().paint( g ); contenido.paintAll( g ); }

// Devuelve la ficha que se encuentre en la posicion que se indique // en el indice que se pasa como paramentro public synchronized Ficha fichaAt( int indice ) { Ficha f;

328

Tutorial de Java

// Capturamos la excepcion de que se nos vaya fuera de // limites el indice que nos pasan try { f = (Ficha)fichas.elementAt( indice ); } catch( ArrayIndexOutOfBoundsException e ) { System.out.println("Error : No hay ficha #" + indice ); return( null ); } return( f ); } }

//--------------------------------------- Final del fichero ListaFichas.java

TRANSPARENCIA
Vamos a mostrar en esta aplicacin un ejemplo de las nuevas tcnicas que se estn desarrollando actualmente. Utilizaremos para demostrar este mtodo de Transparencia el dibujo de presentacin de la Home Page del Workshop de Sun. No s si el applet est hecho tal como yo lo voy a reproducir, pero el resultado que obtendremos ser exactamente el mismo. La tcnica es muy sencilla, consiste en tener un dibujo grande con varias imgenes de la zona que queremos reproducir en pantalla, y utilizando los eventos del ratn, presentamos una imagen u otra. Primero desarrollamos una clase para que controle el repintado de las distintas imgenes, AppletFijo.java, que se van a ir presentando y que no se repinte la zona total del applet, lo que producira un desagradable efecto de parpadeo. Luego extenderemos esta clase para la imagen completa, creando instancias de objetos BotonImg (BotonImg.java), que son los que van a controlar cada una de las opciones. Funcionan como botones normales, slo que cotnrolan de forma exhaustiva los eventos que llegan del ratn, BotonImg.java. Finalmente, en la clase MenuDeBotones (MenuDeBotones.java) es donde creamos la clase principal, cargando la imagen de fondo y recuperando todos los parmetros que se pasen en la llamada al applet. Como podr observarse, el cdigo es muy sencillo y fcil de entender, y el resultado es altamente profesional (tanto, que hasta Sun lo utiliza). Espero no haber violado ningn secreto comercial al reproducir el funcionamiento del applet, aunque seguramente el mtodo que he utilizado yo no es ni por asomo el que utiliza Sun. Yo no dejo de ser un mero aficionado.

Tutorial de Java

329

Esta tcnica se puede utilizar con otras imgenes, otras opciones, y otros lanzamientos a direcciones. Espero que a alguien pueda serle til.

// // // // // AppletFijo.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

330

Tutorial de Java

// // // // // // // // // //

No se asume ninguna software.

responsabilidad por el

uso o

alteracion

de este

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 29-Oct-1996 11:04:12

//-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y est sujeta a cambios

que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

import java.awt.*; import java.applet.Applet;

// En este applet lo que intentamos es que cuando llegue el mensaje de que // actualicemos la pantalla, el mtodo update() se encargue solamente de la // zona que nosotros le indiquemos, y que no flucte todo el dibujo public class AppletFijo extends Applet { private Image imagen; private Graphics imgGr; private Dimension imgTam;

public final synchronized void update( Graphics g ) { Dimension dim = size();

if( imagen == null || dim.width != imgTam.width || dim.height != imgTam.height ) { imagen = createImage( dim.width,dim.height ); imgTam = dim; imgGr = imagen.getGraphics(); imgGr.setFont( getFont() );

Tutorial de Java

331

} imgGr.fillRect( 0,0,dim.width,dim.height ); paint( imgGr ); g.drawImage( imagen,0,0,null ); }

public AppletFijo() {} }

//---------------------------------------- Final del fichero AppletFijo.java

// // // // // // // // // // // // // // // //-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y est sujeta a cambios Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 11-Sep-1996 11:35:43 No se asume ninguna software. responsabilidad por el uso o alteracion de este BotonImg.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

import java.awt.*; import java.awt.image.*; import java.applet.*; import java.net.*;

332

Tutorial de Java

public class BotonImg { private Applet appSuperior; public Image imagen; public Image imagenUp; public Image imagenDown; public Image imagenMove; public ImageFilter filtro; public String mensaje; public int estado; public int offX; public int offY; public int tamX; public int tamY; public URL url; public String href;

// En este mtodo vamos fijando las imgenes que se presentarn en funcin // del estado en que se encuentre el ratn // Utilizamos un mtodo que Java proporciona en la clase java.awt.imagen // que nos permite extraer una zona de una imagen y guardarla como imagen // completa. De este modo extraemos del dibujo que contiene las tres imgenes // posibles, cada imagen correspondiente a los eventos que le llegan al // objeto enviados por el ratn public void init() { filtro = new CropImageFilter( 0,0,tamX,tamY ); imagenUp = appSuperior.createImage( new FilteredImageSource( imagen.getSource(),filtro ) ); filtro = new CropImageFilter( 0, tamY,tamX,tamY ); imagenMove = appSuperior.createImage( new FilteredImageSource( imagen.getSource(),filtro ) ); filtro = new CropImageFilter( 0,tamY*2,tamX,tamY ); imagenDown = appSuperior.createImage( new FilteredImageSource( imagen.getSource(),filtro ) ); // Aqu componemos la direccin a la que saltar cuando se pique con el // ratn la zona que corresponda al botn try {

Tutorial de Java

333

url = new URL( appSuperior.getDocumentBase(),href ); } catch( MalformedURLException e ) { url = null; } return; }

// Constructor, al que le pasamos el applet que correponde al dibujo completo public BotonImg( Applet applet ) { appSuperior = applet; }

// Sobrecargamos el mtodo showDocument, para que no se abra con las // caractersticas del applet del botn, sino con las del padre, que s // tendr las caractersticas de la pgina public void showDocument( URL uRL ) { appSuperior.getAppletContext().showDocument( uRL ); }

// En este mtodo, pintamos un dibujo u otro sobre el botn, en funcin del // estado que nos devuelva el sistema para el cursor y los botones del // ratn public void paint( Graphics g ) { if( estado == 0 ) g.drawImage( imagenUp,offX,offY,appSuperior ); else if(estado == 1) g.drawImage( imagenDown,offX,offY,appSuperior ); else if( estado == 2 ) g.drawImage( imagenMove,offX,offY,appSuperior ); }

// En los tres mtodos que siguen, controlamos los eventos que nos llegan del // ratn. La tcnica a seguir es la misma en cada uno de ellos, mirar si // el cursor se encuentra dentro del campo de accin del botn que estamos // trantando, si no est dentro del campo del botn, pero s dentro del // dibujo general del applet, devolvemos el control al applet general para

334

Tutorial de Java

// que siga comprobando si est el cursor sobre otro botn. Si el cursor se // encuentra en nuestro campo de accin, actuamos en consecuencia public boolean mouseDown( Event evt,int i,int j ) { if( i >= offX j >= offY { // Fijamos el estado a 1, para que cuando se suelte el botn // podamos saltar a la direccin que se haya especificado en la // lista de parmetros del applet estado = 1; appSuperior.repaint(); } else if( estado != 0 ) { appSuperior.showStatus( "" ); estado = 0; appSuperior.repaint(); } return( false ); } && && i < offX + tamX j < offY + tamY ) &&

public boolean mouseUp( Event evt,int i,int j ) { if( i >= offX j >= offY { // Si previamente habamos pulsado el ratn sobre este botn, hacemos // que se muestre la pgina correcpondiente a la direccin URL que se // haba cargado if( estado == 1 ) showDocument( url ); estado = 2; appSuperior.repaint(); } else if( estado != 0 ) { && && i < offX + tamX j < offY + tamY ) &&

Tutorial de Java

335

appSuperior.showStatus( "" ); estado = 0; appSuperior.repaint(); } return( false ); }

public boolean mouseMove( Event evt,int i,int j ) { if( i >= offX j >= offY { // Si slo movemos el cursor sobre el botn, nos limitamos a presentar // el mensaje que se ha definido en los parmetros de llamada al // applet estado = 2; appSuperior.showStatus( mensaje ); appSuperior.repaint(); } else if( estado != 0 ) { appSuperior.showStatus( "" ); estado = 0; appSuperior.repaint(); } return( false ); } } && && i < offX + tamX j < offY + tamY ) &&

//------------------------------------------ Final del fichero BotonImg.java

// // // // // MenuDeBotones.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

336

Tutorial de Java

// // // // // // // // // //

No se asume ninguna software.

responsabilidad por el

uso o

alteracion

de este

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 29-Oct-1996 11:44:46

//-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y est sujeta a cambios

que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

import java.awt.*;

public class MenuDeBotones extends AppletFijo { private int colFondo; BotonImg tit; BotonImg dem; BotonImg ovw; BotonImg faq; BotonImg res; Image fondo;

// En este mtodo leemos todos los datos que se pasan desde la llamada // al applet en la pgina HTML. Por cada una de las opciones que se // encuentren en la pgina, crearemos un objeto BotonImg que sera el // que controle los movimientos del ratn sobre ese Boton public void init() { colFondo = Integer.parseInt( getParameter( "Color" ),16 ); // Carga la imagen de fondo fondo = getImage( getCodeBase(),getParameter( "Fondo" ) );

// Obtenemos los datos del botn que corresponder a la opcin de

Tutorial de Java

337

// "Java WorkShop - Home Page" tit = new BotonImg( this ); // Esta es la posicin X,Y en la que se coloca la imagen del botn // sobre la imagen principal tit.offX = Integer.valueOf( getParameter( "tit.offX" ) ).intValue(); tit.offY = Integer.valueOf( getParameter( "tit.offY" ) ).intValue(); // Este es el tamao de la imagen que corresponde al botn en ancho y // alto. En la imagen asociada que contiene las distintas imgenes de // estados en que se puede encotnrar el botn, nos encontraremos con // tres imgenes diferentes que estarn despalazadas en Y justamente // el tamao en altura que ocupa cada botn tit.tamX = Integer.valueOf( getParameter( "tit.tamX" ) ).intValue(); tit.tamY = Integer.valueOf( getParameter( "tit.tamY" ) ).intValue(); // Esta es el dibujo que contiene las tres imgenes en que va a poder // presentarse este botn tit.imagen = getImage( getCodeBase(),getParameter( "tit.imagen" ) ); // Este es el mensaje que aparecer en la zona de estado del Navegador // cuando el cursor del ratn pase por la zona ocupada por el botn tit.mensaje = getParameter( "tit.mensaje" ); // Esta es la direccin URL a la que se saltar cuando se acte sobre // el botn tit.href = getParameter( "tit.href" ); tit.init();

// Obtenemos los datos del botn que corresponder a la opcin de // "Demo Download Area & Product Pricing" // Los parmetros son los mismos que en el caso anterior dem = new BotonImg( this ); dem.offX = Integer.valueOf(getParameter( "dem.offX" ) ).intValue(); dem.offY = Integer.valueOf(getParameter( "dem.offY" ) ).intValue(); dem.tamX = Integer.valueOf(getParameter( "dem.tamX" ) ).intValue(); dem.tamY = Integer.valueOf(getParameter( "dem.tamY" ) ).intValue(); dem.imagen = getImage( getCodeBase(),getParameter( "dem.imagen" ) ); dem.mensaje = getParameter( "dem.mensaje" ); dem.href = getParameter( "dem.href" ); dem.init();

338

Tutorial de Java

// Obtenemos los datos del botn que corresponder a la opcin de // "Development Tools Overview & Tutorials" // Los parmetros son los mismos que en el caso anterior ovw = new BotonImg( this ); ovw.offX = Integer.valueOf(getParameter( "ovw.offX" ) ).intValue(); ovw.offY = Integer.valueOf(getParameter( "ovw.offY" ) ).intValue(); ovw.tamX = Integer.valueOf(getParameter( "ovw.tamX" ) ).intValue(); ovw.tamY = Integer.valueOf(getParameter( "ovw.tamY" ) ).intValue(); ovw.imagen = getImage( getCodeBase(),getParameter( "ovw.imagen" ) ); ovw.mensaje = getParameter( "ovw.mensaje" ); ovw.href = getParameter( "ovw.href" ); ovw.init();

// Obtenemos los datos del botn que corresponder a la opcin de // "Java WorkShop FAQ" faq = new BotonImg(this); faq.offX = Integer.valueOf( getParameter( "faq.offX" ) ).intValue(); faq.offY = Integer.valueOf( getParameter( "faq.offY" ) ).intValue(); faq.tamX = Integer.valueOf( getParameter( "faq.tamX" ) ).intValue(); faq.tamY = Integer.valueOf( getParameter( "faq.tamY" ) ).intValue(); faq.imagen = getImage( getCodeBase(),getParameter("faq.imagen" ) ); faq.mensaje = getParameter( "faq.mensaje" ); faq.href = getParameter( "faq.href" ); faq.init();

// Obtenemos los datos del botn que corresponder a la opcin de // "Resources" res = new BotonImg( this ); res.offX = Integer.valueOf(getParameter( "res.offX" ) ).intValue(); res.offY = Integer.valueOf(getParameter( "res.offY" ) ).intValue(); res.tamX = Integer.valueOf(getParameter( "res.tamX" ) ).intValue(); res.tamY = Integer.valueOf(getParameter( "res.tamY" ) ).intValue(); res.imagen = getImage( getCodeBase(),getParameter( "res.imagen" ) ); res.mensaje = getParameter( "res.mensaje" ); res.href = getParameter( "res.href" );

Tutorial de Java

339

res.init(); }

// Aqu pintamos la imagen de fondo y le pasamos el control a cada uno de los // botones para que controles la imagen que deben pintar en funcin de la // accin que se realiza sobre ellos public void paint( Graphics g ) { g.setColor( new Color( colFondo ) ); g.fillRect( 0,0,size().width,size().height ); g.drawImage( fondo,0,0,this ); tit.paint( g ); dem.paint( g ); ovw.paint( g ); faq.paint( g ); res.paint( g ); }

// Ahora en los mtodos que controlan los estados de los botones del ratn, // pasamos el control a los botones, para que cambien la imagen que // corresponde al botn sobre el que est actuando el ratn, ya sea solamente // movindose sobre l o actuando con los botones sobre la zona que // corresponde a ese botn public boolean mouseUp( Event evt,int i,int j ) { tit.mouseUp( evt,i,j ); dem.mouseUp( evt,i,j ); ovw.mouseUp( evt,i,j ); faq.mouseUp( evt,i,j ); res.mouseUp( evt,i,j ); return( false ); }

public boolean mouseDown( Event evt,int i,int j ) { tit.mouseDown( evt,i,j ); dem.mouseDown( evt,i,j ); ovw.mouseDown( evt,i,j ); faq.mouseDown( evt,i,j );

340

Tutorial de Java

res.mouseDown( evt,i,j ); return( false ); }

public boolean mouseMove( Event evt,int i,int j ) { tit.mouseMove( evt,i,j ); dem.mouseMove( evt,i,j ); ovw.mouseMove( evt,i,j ); faq.mouseMove( evt,i,j ); res.mouseMove( evt,i,j ); return( false ); }

public MenuDeBotones() {} }

//------------------------------------- Final del fichero MenuDeBotones.java

CALCULADORA
El applet Calculadora.java presenta una calculadora bsica, tomando como herramienta de construccin el AWT. Es una calculadora que realiza las operaciones ms habituales, tipo calculadora solar, pero que ilustra perfectamente los mecanismos de presentacin en pantalla de Componentes del AWT sobre un layout propio, ya que todos los botones se colocan en posiciones determinadas. Las operaciones matemticas no son motivo de estudio, pero s se podra completar la calculadora con ms funciones y hacerla semejante a las que se proporcionan con casi todos los entornos grficos. La clase BotonCalculadora es la fundamental en este ejemplo, ya que nos permite colocar botones en cualquier sitio y con cualquier tamao.

Tutorial de Java

341

// // // // // // // // // // // // // // // //-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y est sujeta a cambios Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 12-Sep-1996 15:39:38 No se asume ninguna software. responsabilidad por el uso o alteracion de este Calculadora.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

import java.awt.*; import java.applet.Applet;

public class Calculadora extends Applet { TextField pantallita; public final int OP_NADA = 0; public final int OP_SUMA = 1; public final int OP_RESTA = 2; public final int OP_MULT = 3; public final int OP_DIV = 4; public final int OP_IGUAL = 5; public final int OP_BORRAR_C = 6; public final int OP_BORRAR_AC = 7; public final int PUNTO_DECIMAL = -1;

String cadDecimal;

342

Tutorial de Java

int operador = OP_NADA; boolean nuevoNumero = true; boolean esDecimal = false; double Total = 0.0; boolean esConstante = false; double numConstante = 0.0; int opConstante = OP_NADA;

public void init() { BotonCalculadora bot0,bot1,bot2,bot3,bot4,bot5,bot6,bot7,bot8,bot9; BotonCalculadora botDecimal,botSuma,botResta; BotonCalculadora botMult,botDiv,botIgual,botBorraC,botBorraAC;

// No utilizamos ningn la layout porque vamos a colocar // los botones y el campo de texto para mostrar el resultado // de las operaciones, en posiciones fijas setLayout( null ); setFont( new Font( "Helvetica",Font.PLAIN,14 ) ); setBackground( new Color( 0x66,0xA2,0xD4 ) );

// Pintamos los botones de los numeros bot0 = new BotonCalculadora( "0",OP_NADA,0 ); add( bot0 ); bot0.reshape( 8,144,96,24 ); bot1 = new BotonCalculadora( "1",OP_NADA,1 ); add( bot1 ); bot1.reshape( 8,112,40,24 ); bot2 = new BotonCalculadora( "2",OP_NADA,2 ); add( bot2 ); bot2.reshape( 64,112,40,24 ); bot3 = new BotonCalculadora( "3",OP_NADA,3 ); add( bot3 ); bot3.reshape( 120,112,40,24 ); bot4 = new BotonCalculadora( "4",OP_NADA,4 ); add( bot4 ); bot4.reshape( 8,80,40,24 );

Tutorial de Java

343

bot5 = new BotonCalculadora( "5",OP_NADA,5 ); add( bot5 ); bot5.reshape( 64,80,40,24 ); bot6 = new BotonCalculadora( "6",OP_NADA,6 ); add( bot6 ); bot6.reshape( 120,80,40,24 ); bot7 = new BotonCalculadora( "7",OP_NADA,7 ); add( bot7 ); bot7.reshape( 8,48,40,24 ); bot8 = new BotonCalculadora( "8",OP_NADA,8 ); add( bot8 ); bot8.reshape( 64,48,40,24 ); bot9 = new BotonCalculadora( "9",OP_NADA,9 ); add( bot9 ); bot9.reshape( 120,48,40,24 );

// Pintamos los botones que nos van a permitir realizar las // operaciones soportadas poe esta mini-calculadora botDecimal = new BotonCalculadora( "",OP_NADA,PUNTO_DECIMAL ); add( botDecimal ); botDecimal.reshape( 120,144,40,24 ); botSuma = new BotonCalculadora( "+",OP_SUMA,0 ); add( botSuma ); botSuma.reshape( 176,112,40,56 ); botResta = new BotonCalculadora( "-",OP_RESTA,0 ); add( botResta ); botResta.reshape( 232,112,40,24 ); botMult = new BotonCalculadora( "*",OP_MULT,0 ); add( botMult ); botMult.reshape( 176,80,40,24 ); botDiv = new BotonCalculadora( "/",OP_DIV, 0); add( botDiv ); botDiv.reshape( 176,48,40,24 ); botIgual = new BotonCalculadora( "=",OP_IGUAL,0 ); add( botIgual ); botIgual.reshape( 232,144,40,24 );

344

Tutorial de Java

botBorraC = new BotonCalculadora( "C",OP_BORRAR_C,0 ); add( botBorraC ); botBorraC.reshape( 232,48,40,24 ); botBorraAC = new BotonCalculadora( "AC",OP_BORRAR_AC,0 ); add( botBorraAC ); botBorraAC.reshape( 232,80,40,24 );

// Pintamos el campo de texto en que vamos a reflejar lo que se // se va introduciendo y el resultado de las operaciones pantallita = new TextField( "0",80 ); pantallita.setEditable( false ); add( pantallita ); pantallita.reshape( 48,8,184,31 ); String unaDecima = ( new Double(0.1) ).toString(); cadDecimal = unaDecima.substring( unaDecima.length()-2 ).substring( 0,1 ); }

public static void main( String args[] ) { Frame f = new Frame( "Calculadora Java" ); Calculadora c = new Calculadora();

c.init(); f.add( "Center",c ); f.pack(); f.resize( 395,181 ); f.show(); }

public void anadir( int valor ) { String digito;

// Controlamos el punto decimal, para que si es lo primero que se // pulsa, desplazar un cero a la izquierda, y si se pulsa cuando

Tutorial de Java

345

// ya estamos en un numero decimal, no hacerle caso if( valor == PUNTO_DECIMAL ) if( !esDecimal ) { if( nuevoNumero ) { pantallita.setText( "0" ); nuevoNumero = false; } esDecimal = true; digito = cadDecimal; } else return; else digito = ( new Integer(valor) ).toString();

// Si tenemos que presentar un numero nuevo, porque estemos ya en // otra operacion, borramos antes la pantallaita if( nuevoNumero ) { pantallita.setText( digito ); if( valor != 0 ) nuevoNumero = false; } else pantallita.setText( pantallita.getText() + digito );

repaint(); }

// Ejecuta la operacion que indica el operador del boton que se // haya pulsado public void haceOperacion( int nuevoOperador ) { double numero;

346

Tutorial de Java

numero = ( new Double( pantallita.getText() ) ).doubleValue(); switch( nuevoOperador ) { case OP_SUMA: case OP_RESTA: case OP_MULT: case OP_DIV: if( nuevoNumero ) { if( nuevoOperador == operador && !esConstante ) { esConstante = true; numConstante = numero; opConstante = nuevoOperador; } else esConstante = false; } else esConstante = false; case OP_IGUAL: if( !nuevoNumero || esOperadorIgual( nuevoOperador ) ) { if( esConstante ) { Total = numConstante; operador = opConstante; }

// Controla los operadores de operaciones // matematicas switch( operador ) { case OP_SUMA: Total = Total + numero;

Tutorial de Java

347

break; case OP_RESTA: Total = Total - numero; break; case OP_MULT: Total = Total * numero; break; case OP_DIV: Total = Total / numero; break; case OP_IGUAL: case OP_NADA: Total = numero; break; }

// Presenta el resultado del tecleo en la pantalla pantallita.setText(( new Double(Total) ).toString()); } operador = nuevoOperador; nuevoNumero = true; esDecimal = false; break; }

// Controla los operadores de borrado tanto el Clear como el // AllClear switch( nuevoOperador ) { // Borra el numero y cancela la operacion en curso // solamente case OP_BORRAR_C: numero = 0.0; pantallita.setText( "0" ); if( esOperadorIgual( operador ) ) Total = numero;

348

Tutorial de Java

nuevoNumero = true; esDecimal = false; break; // Borra todas las operaciones y numeros, lo deja // todo a cero case OP_BORRAR_AC: pantallita.setText( "0" ); operador = OP_NADA; nuevoNumero = true; esDecimal = false; Total = 0.0; esConstante = false; break; } }

// Comprueba si es el operador igual, para presentar resultado private protected boolean esOperadorIgual( int oper ) { return( oper == OP_IGUAL ); } }

// Esta clase se utiliza para pintar cada uno de los botones que van // a conformar la calculadora class BotonCalculadora extends Button { int operador; int bvalor;

BotonCalculadora( String texto, int oper, int valor ) { super( texto ); operador = oper; bvalor = valor; }

Tutorial de Java

349

public boolean action( Event evt, Object arg ) { Calculadora par = ( Calculadora )getParent();

// Si no se tiene ninguna operacion matematica asociada, solamente // se aade el numero a la pantallita, y si ya es una // operacion, entonces se hace y se presenta el resultado if( operador == par.OP_NADA ) par.anadir( bvalor ); else par.haceOperacion( operador );

return( true ); } }

//--------------------------------------- Final del fichero Calculadora.java

CUENTA-KILOMETROS
El applet Contador.java presenta una imagen conocida de cuenta-kilmetros de coche, pero cuyo cdigo nos sirve para demostrar varias cosas. Podemos comprobar cmo se manipula el offset de un dibujo porque la imagen de los nmeros es un fichero grfico que contiene los 10 dgitos posibles, y el contador va seleccionando cada uno de ellos en funcin de la cifra que tiene que representar el cuanta-kilmetros. Tambin podemos comprobar la utilizacin de threads para la implementacin del intervalo de tiempo que tarda el cuenta-kilmetros en pasar de una cifra a la siguiente. En la llamada al applet, podemos indicar el nmero de dgitos que va a contener el cuentakilmetros, el intervalo de tiempo que transcurrir entre los cambios de cifras y el valor incial en que arranca el contador. Adems, como es un applet muy sencillo, lo hemos adornado con algunas opciones, por ejemplo: Con la tecla + aceleramos el contador Con la tecla - disminumos la velocidad con que cambian los nmeros en el cuentakilmetros Con la tecla 0 ponemos a cero el contador Utilizando el ratn, si picamos una vez sobre el cuenta-kilmetros, se detiene la cuenta, y si picamos una segunda vez, vuelve a seguir contando

350

Tutorial de Java

// // Contador.java // Copyright (c) 1996, Agustin Froufe // Todos los derechos reservados. // // No se asume ninguna responsabilidad por el uso o alteracion de este // software. Este software se proporciona COMO ES, sin garantia de ningun // tipo de su funcionamiento y en ningun caso sera el autor responsable de // daos o perjuicios que se deriven del mal uso del software, aun cuando // este haya sido notificado de la posibilidad de dicho dao. // // Compilador: javac 1.0 // Autor: Agustin Froufe // Creacion: 14-Sep-1996 16:33:10 // //-------------------------------------------------------------------------// Esta informacion no es necesariamente definitiva y est sujeta a cambios // que pueden ser incorporados en cualquier momento, sin avisar. //-------------------------------------------------------------------------import java.awt.*; import java.applet.Applet; // Esta clase implementa un contador grfico, tipo cuentakilmetros de // coche. Admite que se le pasen tres parmetros desde la llamada al // applet en la pgina html que lo incorpore, son: // DIGITOS Nmero de dgitos del contador // INTERVALO Tiempo a transcurrir entre cada incremento // VALOR_INICIAL Cifra inicial desde la que empieza a contar // Utiliza un fichero que contiene las imgenes de los dgitos y // de ah va extrayendo cada una de las figuras para componer el // nmero que tenga que representar en pantalla // public class Contador extends Applet implements Runnable { Thread thread = null; MediaTracker tracker = null; Image numeros; int numero_ancho = 15; // Pixels de ancho de cada uno de los numeros int numero_alto = 20; // Pixels de alto de cada uno de los numeros int digitos; int intervalo; String valor; int estado = 0; public void init() { String parametro; // Creamos un objeto mediatracker para soportar el contador tracker = new MediaTracker( this ); try { // Cargamos la imagen que contiene los numeros que vamos a // presentar en el contador numeros = getImage( getCodeBase(),"numeros.gif" ); tracker.addImage( numeros,0 ); // Numero de digitos de que consta el contador parametro = getParameter( "DIGITOS" ); digitos = Integer.parseInt( parametro ); // Intervalo entre cada actualizacion del contador parametro = getParameter( "INTERVALO" ); intervalo = Integer.parseInt ( parametro ); // Valor del contador cuando lo arrancamos valor = getParameter( "VALOR_INICIAL" ); if( valor == null ) valor = String.valueOf( (long)( Math.random() * (long)( Math.pow( 10,digitos ) ) ) ); } catch( Exception e ) { return; } // Ponemos el applet al tamao ms adecuado

Tutorial de Java

351

resize( ( digitos * numero_ancho ),numero_alto ); // Creamos y arrancamos el thread que va a controla al // contador thread = new Thread( this ); thread.start(); } public void stop() { if( thread != null ) { thread.stop(); thread = null; } } // Mtodo principal del thread, donde se llama a la funcin de // pintado, que es donde se componen las imgenes de los nmeros // para reflejar la cifra por donde anda el contador public void run() { // Una vez que se ha cargado la imagen de los nmero, seguimos tracker.checkAll( true ); // repitamos y nos dormimos el intervalo de tiempo que se haya // indicado for( ;; ) { repaint(); try { Thread.sleep( intervalo ); } catch( InterruptedException e ) { ; } } } // Cuando se pulsa el ratn, se para el contador y cuando se // vuelva a pulsar, seguir contando public boolean mouseDown( Event e,int x,int y ) { if( thread != null ) { thread.stop(); thread = null; } else { thread = new Thread( this ); thread.start(); } return true; } // Controlamos algunas teclas, para poder manipular el contador public boolean keyDown( Event e,int key ) { switch( key ) { case '+': // Va ms rpido intervalo -= 100; if( intervalo < 10 ) intervalo = 10; break; case '-': // Va ms lento intervalo += 100; break; case '0': // Lo reseteamos valor = String.valueOf( 0 ); estado = 0; break;

352

Tutorial de Java

default: valor = String.valueOf( (long)( Math.random() * (long)( Math.pow( 10,digitos ) ) ) ); estado = 0; break; } return true; } // Aqu no borramos la pantalla, slo pintamos encima public void update( Graphics g ) { paint( g ); } // Pintamos el nmero public void paint( Graphics g ) { int i; int digit; boolean scroll; if( tracker.checkAll() == false ) { g.setColor( Color.black ); g.fillRect( 0,0,(digitos * numero_ancho),numero_alto ); return; } int zeros = digitos - valor.length(); for( i=0; i < zeros; i++ ) { Graphics gc = g.create( i * numero_ancho,0, numero_ancho,numero_alto ); gc.drawImage( numeros,0,0,this ); gc.dispose(); } scroll = true; for( i=valor.length() - 1; i >= 0;i-- ) { digit = valor.charAt( i ) - '0'; Graphics gc = g.create( (zeros+i) * numero_ancho,0, numero_ancho,numero_alto ); // Tenemos en cuenta cuando se va a cambiar de un nmero // a otro para ir mostrando la mitad del anterior y la primera // mitad del siguiente para dar la sensacin de movimiento del // display del contador if( scroll ) { switch( estado ) { case 0: if( digit == 0 ) gc.drawImage( numeros,0, -( (9 * numero_alto) + numero_alto / 2 ),this ); gc.drawImage( numeros,0, -( (digit * numero_alto) - numero_alto / 2 ),this ); break; case 1: gc.drawImage( numeros,0, -(digit * numero_alto),this ); break; case 2: gc.drawImage( numeros,0,

Tutorial de Java

353

-( (digit * numero_alto) + numero_alto / 2 ),this ); if( digit == 9 ) gc.drawImage( numeros,0, -( (0 * numero_alto) - numero_alto / 2 ),this ); break; } } else gc.drawImage( numeros,0,-(digit * numero_alto),this ); gc.dispose(); if( digit != 9 ) scroll = false; } if( estado >= 2 ) { estado = 0; long l = Long.parseLong( valor ); l++; valor = String.valueOf( l ); } estado++; } } //---------------------------------------- Final del fichero Contador.java

POTENCIOMETRO
El applet EjPotenciometro.java presenta un ejemplo de uso de un Componente nuevo que hemos implementado para incorporar a las posibilidades que ofrece el AWT. Se trata de una Barra de Desplazamiento, pero semejando el potencimetro deslizante de un equipo electrnico. Incorpora una serie de posibilidades nuestro Componente Potenciometro.java, que el ejemplo permite utilizar y comporbar, como es el posicionamiento de la escala indicadora. En el applet ejemplo, hemos utilizado el Componente Grupo, que ya habamos implementado el captulos anteriores.

354

Tutorial de Java

// // // // // // // // // // // // // // // //-------------------------------------------------------------------------// // Esta informacion no es necesariamente definitiva y esta sujeta a cambios que pueden ser incorporados en cualquier momento, sin avisar. Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 16-Dic-1996 17:13:51 No se asume ninguna software. responsabilidad por el uso o alteracion de este EjPotenciometro.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

//--------------------------------------------------------------------------

import java.awt.*; import java.applet.*;

public class EjPotenciometro extends Applet { Button izda; Button dcha; Button todo; TextField inds; TextField inds1; Potenciometro s; Potenciometro s1;

public void init() { setBackground( Color.lightGray ); setLayout( null );

Tutorial de Java

355

// Utilizamos el componente Grupo que ya tenemos desarrollado // Para agrupar los componentes de esta demo. El primer // Grupo agrupa a todos los elementos: barras, botones, textos Grupo g = new Grupo( "Ejemplo de Potencimetro" ); add( g ); g.setFont( new Font( "Dialog",0,12 ) ); g.reshape( insets().left+20,insets().top+20,520,250 ); g.setLayout( null ); // Creamos un potencimetro vertical s = new Potenciometro( 0,150,2 ); g.add( s ); s.reshape( 40,50,42,180 ); s.setValor( 0 ); inds = new TextField( Integer.toString( s.getValor()),4 ); g.add( inds ); inds.reshape( 36,20,45,28 ); // Creamos otro potencimetro, horizontal s1 = new Potenciometro( 0,240,1 ); g.add( s1 ); s1.reshape( 120,35,365,40 ); s1.setValor( 0 ); inds1 = new TextField( Integer.toString( s1.getValor()),4 ); g.add( inds1 ); inds1.reshape( 420,80,50,28 );

// Este otro grupo engloba a los botones que permiten posicionar // las marcas por encima o debajo de la barra Grupo g2 = new Grupo( "Posicin Indicador" ); g.add( g2 ); g2.reshape( 105,150,400,70 ); izda = new Button( "Izquierda/Arriba" ); g2.add( izda ); izda.reshape( 15,25,120,30 ); dcha = new Button( "Derecha/Abajo" ); g2.add( dcha ); dcha.reshape( 145,25,110,30 );

356

Tutorial de Java

todo = new Button( "Ambas Posiciones" ); g2.add( todo ); todo.reshape( 265,25,125,30 ); }

public boolean handleEvent( Event evt ) { System.out.println( "Target "+evt.target ); System.out.println( " ID "+evt.id );

// Controlamos los eventos de los botones para posicionar // las marcas y del propio potencimetro para controlar // la posicion del indicador y el valor en el texto if( evt.target == izda && evt.id == 1001 ) { s.setPosicionMarca( 1 ); s.update(); s1.setPosicionMarca( 1 ); s1.update(); return( true ); } if( evt.target == dcha && evt.id == 1001 ) { s.setPosicionMarca( 2 ); s.update(); s1.setPosicionMarca( 2 ); s1.update(); return( true ); } if( evt.target == todo && evt.id == 1001 ) { s.setPosicionMarca( 3 ); s.update(); s1.setPosicionMarca( 3 ); s1.update(); return( true ); } if( evt.target == s && evt.id == 1001 )

Tutorial de Java

357

{ inds.setText( Integer.toString( s.getValor() ) ); return( true ); } if( evt.target == s1 && evt.id == 1001 ) { inds1.setText( Integer.toString( s1.getValor() ) ); return( true ); } switch( evt.id ) { default: return( false ); } } }

//----------------------------------- Final del fichero EjPotenciometro.java

// // // // // // // // // // // // // // // //-------------------------------------------------------------------------// Esta informacion no es necesariamente definitiva y esta sujeta a cambios Compilador: javac 1.0 Autor: Agustin Froufe Creacion: 16-Dic-1996 16:25:46 No se asume ninguna software. responsabilidad por el uso o alteracion de este Potenciometro.java Copyright (c) 1996, Agustin Froufe Todos los derechos reservados.

Este software se proporciona COMO ES, sin garantia de ningun

tipo de su funcionamiento y en ningun caso sera el autor responsable de daos o perjuicios que se deriven del mal uso del software, este haya sido notificado de la posibilidad de dicho dao. aun cuando

358

Tutorial de Java

//

que pueden ser incorporados en cualquier momento, sin avisar.

//--------------------------------------------------------------------------

import java.awt.*;

public class Potenciometro extends Canvas { public static final int HORIZONTAL = 1; public static final int VERTICAL = 2; public static final int IZQUIERDA = 1; public static final int DERECHA = 2; public static final int SUBIR = 3; public static final int BAJAR = 4; public static final int SUPERIOR = 1; public static final int INFERIOR = 2; public static final int SUP_INF = 3; boolean boolDrag; int valor; int Max; int Min; int x_offset; int y_offset; int hat_high; int espacioMarca; int posicion; int linIncremento; int pagIncremento; int hat_offset; int orientacion; int posicionMarca; Image offscreen; static Font defFuente;

public Potenciometro( int min,int max,int orient ) { this( min,max,orient,3 ); }

Tutorial de Java

359

public Potenciometro( int min,int max,int orient,int posMarca ) { boolDrag = false; Max = 100; hat_high = 12; espacioMarca = 30; linIncremento = 1; pagIncremento = 30; orientacion = 1; posicionMarca = 3; Max = max; Min = min; valor = Min; posicionMarca = posMarca; orientacion = orient; setFont( defFuente ); }

// Mtodo que utilizamos para fijar los marcadores en un valor // determinado // Aqu se encuentran todos los mtodos de get() y set() que se // utilizan para fijar y recuperar los valores de los parmetros // que controlan al potencimetro public void setValor( int v ) { Dimension d = size(); valor = v; if( orientacion == 2 ) { posicion = d.height-hat_high - (valor-Min) * (d.height-hat_high) / (Max-Min); return; } posicion = d.width-hat_high - (valor-Min) * (d.width-hat_high) / (Max-Min); }

public int getValor() {

360

Tutorial de Java

return( valor ); }

public void setMaximo( int v ) { Max = v; }

public int getMaximo() { return( Max ); }

public void setMinimo( int v ) { Min = v; }

public int getMinimo() { return( Min ); }

public void setEspacioMarca( int f ) { espacioMarca = f; }

public int getEspacioMarca() { return( espacioMarca ); }

public void setPosicionMarca( int p ) { posicionMarca = p; }

public int getPosicionMarca() { return( posicionMarca ); }

public void setPagIncremento( int i ) {

Tutorial de Java

361

pagIncremento = i; }

public int getPagIncremento() { return( pagIncremento ); }

public void setLinIncremento( int i ) { linIncremento = i; }

public int getLinIncremento() { return( linIncremento ); }

public void setOrientacion( int o ) { orientacion = o; }

public int getOrientacion() { return( orientacion ); }

// Repintamos el potencimetro, slo una de las posiciones public void paint( Graphics g ) { if( orientacion == 1 ) { printHSlider( g ); return; } printVSlider( g ); }

// Pinta el potencimetro vertical public void printVSlider( Graphics g ) {

362

Tutorial de Java

Dimension d = size(); x_offset = 17; y_offset = 4; g.setColor( Color.lightGray ); g.fill3DRect( x_offset,y_offset,4,d.height-2*y_offset,false ); g.setColor( Color.black ); g.drawLine( x_offset+1,y_offset+1,x_offset+1,d.height-y_offset-2 ); g.setColor( Color.lightGray ); g.fill3DRect( 5,posicion,27,hat_high,true ); g.setColor( getForeground() );

int delta = espacioMarca*d.height/Max; for( int y = hat_high/2; y < d.height-hat_high/2; y += delta ) { if( posicionMarca != 2 ) g.drawLine( 0,y,3,y ); if( posicionMarca != 1 ) g.drawLine( 33,y,36,y ); }

if( posicionMarca != 2 ) g.drawLine( 0,d.height-hat_high/2,3,d.height-hat_high/2 ); if( posicionMarca != 1 ) g.drawLine( 33,d.height-hat_high/2,36,d.height-hat_high/2 ); if( posicionMarca != 2 ) drawTriangulo( g,9,posicion+3,3,1,true ); if( posicionMarca != 1 ) drawTriangulo( g,27,posicion+3,3,2,true ); }

// Pinta el potencimetro horizontal public void printHSlider( Graphics g ) { Dimension d = size(); y_offset = 17; x_offset = 4;

Tutorial de Java

363

g.setColor( Color.lightGray ); g.fill3DRect( x_offset,y_offset,d.width-2*x_offset,4,false ); g.setColor( Color.black ); g.drawLine( x_offset+1,y_offset+1,d.width-x_offset-2,y_offset+1 ); g.setColor( Color.lightGray ); g.fill3DRect( posicion,5,hat_high,27,true ); g.setColor( getForeground() );

int delta = espacioMarca*d.width/Max; for( int x=hat_high/2; x < d.width-hat_high/2; x += delta ) { if( posicionMarca != 2 ) g.drawLine( x,0,x,3 ); if( posicionMarca != 1 ) g.drawLine( x,33,x,36 ); }

if( posicionMarca != 2 ) g.drawLine( d.width-hat_high/2,0,d.width-hat_high/2,3 ); if( posicionMarca != 1 ) g.drawLine( d.width-hat_high/2,33,d.width-hat_high/2,36 ); if( posicionMarca != 2 ) drawTriangulo( g,posicion+3,9,3,3,true ); if( posicionMarca != 1 ) drawTriangulo( g,posicion+3,27,3,4,true ); }

public void update() { Dimension d = size();

if( offscreen == null ) offscreen = createImage( d.width,d.height ); if( offscreen == null ) return;

364

Tutorial de Java

Graphics offg = offscreen.getGraphics(); Graphics g = getGraphics(); offg.clipRect( 0,0,d.width,d.height ); offg.setFont( getFont() ); offg.setColor( getBackground() ); offg.fillRect( 0,0,d.width,d.height ); offg.setColor( g.getColor() ); paint( offg ); g.clipRect( 0,0,d.width,d.height ); g.drawImage( offscreen,0,0,this ); }

// Ahora controlamos los eventos que nos llegan del ratn, que son // el pique y el arrastre public boolean onMouseDown( int x,int y ) { Dimension d = size();

if( orientacion == 2 && y > posicion && y < posicion+hat_high ) { hat_offset = y-posicion; boolDrag = true; return( true ); } if( orientacion == 1 && x > posicion && x < posicion+hat_high ) { hat_offset = x-posicion; boolDrag = true; return( true ); } if( orientacion == 2 ) { if( y < posicion ) posicion = (posicion > pagIncremento) ? (posicion-pagIncremento) : 0; else if( y > posicion )

Tutorial de Java

365

posicion = (posicion < d.height-hat_high-pagIncremento) ? (posicion+pagIncremento) : (d.height-hat_high); valor = Min + (Max-Min) * (d.height-hat_high-posicion) / (d.height-hat_high); } if( orientacion == 1 ) { if( x < posicion ) posicion = (posicion > pagIncremento) ? (posicion-pagIncremento) : 0; else if( x > posicion ) posicion = (posicion < d.width-hat_high-pagIncremento) ? (posicion+pagIncremento) : (d.width-hat_high); valor = Min + (Max-Min) * (d.width-hat_high-posicion) / (d.width-hat_high); } update(); return( true ); }

public boolean onMouseDrag( int x,int y ) { Dimension d = size();

if( boolDrag && orientacion == 2 ) { posicion = y-hat_offset; if( posicion < 0 ) posicion = 0; if( posicion > d.height-hat_high ) posicion = d.height-hat_high; update(); } if( boolDrag && orientacion == 1 ) { posicion = x-hat_offset;

366

Tutorial de Java

if( posicion < 0 ) posicion = 0; if( posicion > d.width-hat_high ) posicion = d.width-hat_high; update(); } return( true ); }

public boolean handleEvent( Event evt ) { Event evnt;

switch( evt.id ) { case 501: return( onMouseDown( evt.x,evt.y ) ); case 502: evnt = new Event( this,1001,new Integer( valor ) ); break; case 506: return( onMouseDrag( evt.x,evt.y ) ); default: return( false ); } postEvent( evnt ); boolDrag = false; return( true ); }

public void reshape( int x,int y,int w,int h ) { super.reshape( x,y,w,h ); offscreen = createImage( w,h ); update(); }

public Dimension preferredSize() {

Tutorial de Java

367

return( minimumSize() ); }

public Dimension minimumSize() { return( new Dimension( 40,40 ) ); }

// Pintamos un triangulito sobre el indicador, que apunta hacia // donde se encuentra la escala que estamos usando private void drawTriangulo( Graphics g,int x,int y, int tam,int orient,boolean habil ) { if( habil ) g.setColor( Color.black ); else g.setColor( Color.gray ); for( int i=0; i < tam; i++ ) { switch( orient ) { case 3: g.drawLine( x+i,y-i,x+2*tam-i-1,y-i ); break; case 4: g.drawLine( x+i,y+i,x+2*tam-i-1,y+i ); break; case 1: g.drawLine( x-i,y+i,x-i,y+2*tam-i-1 ); break; case 2: g.drawLine( x+i,y+i,x+i,y+2*tam-i-1 ); break; } } }

static { defFuente = new Font( "Dialog",0,16);

368

Tutorial de Java

} }

//------------------------------------- Final del fichero Potenciometro.java

CARTEL de Luis Angel Ortega


Este applet, Cartel.java, resenta un texto en una sola lnea que se desplaza a derecha o izquierda y es una contribucin de un lector del Tutorial. Lo siguiente es suyo.
sic

Applet de libre distribucin, puedes enviar tus sugerencias a Luis Angel Ortega para compartir informacin y conocimientos sobre el lenguaje Java y Internet. Datos del autor: Nombre: Luis Angel Ortega LAO 1996 Direccin: lao@arrakis.es Puedes visitar su Pgina Personal
sic

//************************************************************************* // Cartel.java: // // Propiedad LAO 1996 Luis Angel Ortega Todos los derechos reservados // // Permiso para usar copiar, modificar, y distribuir este software // y su documentacion, sin propositos comerciales. // // Para cualquier duda o comentario con el autor lao@arrakis.es // // Pagina WEB http://wwww.arrakis.es/~lao // // El autor no se resposabiliza de posibles daos que el software // pueda efectuar en cualquier equipo informatico. // Applet // // // // // // // // // // // // // //

Tutorial de Java

369

// // Autor: Luis Angel Ortega LAO 25/11/1996

// // // // // // // // // ==================================================================== // PARAMETROS | VALORES DE LOS PARAMETROS //

// Creado con Microsoft Visual J++ Version 1.0 // // El Applet es de libre distribucion, presenta un texto que se // desplaza a derecha o izquierda. // // Los parmetros son los siguientes: // // // // // // // // // // // // // // // // // // // // // // // // // // // //

===================================================================== // TEXTO = Cadena de texto a desplazar. | "Texto a desplazar" //

===================================================================== // DESPLAZAMIENTO = Desplazar el texto a izquierda o derecha. | "IZQUIERDA" | "DERECHA" // //

=================================================================== // COLORFONDO = Color para el fondo del texto. | "ROJO" | "AZUL" | "NEGRO" | "BLANCO" // // // //

=================================================================== // COLORTEXTO = Color para el texto | "ROJO" | "AZUL" | "NEGRO" | "BLANCO" // // // //

=================================================================== // WIDTH = Ancho del Applet | Un numero mayor que 12. //

=================================================================== // HEIGHT = Ancho del Applet | Un numero mayor que 12. //

=================================================================== // VELOCIDAD =Velocidad en milisegundos | Un numero mayor que 1 de la actualizacin en pantalla del applet, depende de la velocidad del ordenador donde se visualiza el applet | | | | // // // // //

370

Tutorial de Java

// // // // // // // // // // // // // // // // // // // //

=================================================================== // // Notas: El parmetro de la VELOCIDAD es opcional, por defecto es 10. El texto se ajusta automticamente al alto del Applet. La velocidad de desplazamiento depende de la velocidad del procesador. // // // // //

Los valores de los parmetros pueden ser en minsculas o maysculas. // // Ejemplo: // // <applet code="Cartel.class" width="200" height="50" id="Cartel"> <param name="COLORFONDO" value="negro"> <param name="COLORTEXTO" value="rojo"> <param name="TEXTO" value="Ejemplo de Applet"> <param name="DESPLAZAMIENTO" value="izquierda"> <param name="VELOCIDAD" value="5"> Tu Visualizador WEB no puede ejecutar cdigo JAVA</applet> // // // // // // // // //

//*************************************************************************

import java.applet.*; import java.awt.*;

public class Cartel extends Applet implements Runnable {

Thread

m_Cartel = null;

// Todas las constantes y Variables excepto Texto, son declaradas // privadas para no poder ser usadas desde otra clase // constantes private final int RESTAFONT = 14;

// Variables

Tutorial de Java

371

private int Velocidad; private int CordY; private int CordX; private int StringAncho; private int SizeDescent; private Font font; private Dimension d;

// Velocidad del thread en milisegundos // Cordenada y donde situar el texto // Cordenada x donde situar el texto // Ancho de la cadena a desplazar

// Tipo de letra del texto // Dimensiones del applet

// La misin de estas tres variables es almacenar un objeto grfico // en memoria, que es una copia del objeto que se crea al pintar el applet. private Dimension offDimension; private Image offImage; private Graphics offGraphics;

private Color ColorTexto; private Color ColorFondo; public String Texto; private boolean Scroll;

// Color del texto // Color del fondo // Texto a desplazar // TRUE=Izquierda FALSE=Derecha

//**********************************************************************// // Metodo Actualizar() // Utilizado para poder cambiar los parametros del applet, mediante // codigo en JavaScript o VBscript. // Nota: // // // Entrada: // // // // // // // // // Salida: void String Veloc String ColTexto String Text = Texto a desplazar. String Desplaza = Desplaza el texto a "IZQUIERDA O "DERECHA" String ColFondo = Cambia el color del fondo Es posible cambiar el texto sin llamar al metodo Actualizar // // // // // // // // // // // // // // // //

"ROJO","AZUL","NEGRO","BLANCO" = Cambia el color del texto

"ROJO","AZUL","NEGRO","BLANCO" = Velocidad del Thread en milisegundos.

//**********************************************************************//

372

Tutorial de Java

public void Actualizar (String Text,String Desplaza,String ColFondo, String ColTexto, String Veloc) {

try {

// Comprueba si los datos pasados al metodo son los correctos ColFondo = ColFondo.toUpperCase(); // Lo convierte a Mayusculas

ColorFondo = (ColFondo.compareTo("ROJO")==0) ? Color.red : ColorFondo; ColorFondo = (ColFondo.compareTo("AZUL")==0) ? Color.blue : ColorFondo; ColorFondo = (ColFondo.compareTo("NEGRO")==0) ? Color.black : ColorFondo; ColorFondo = (ColFondo.compareTo("BLANCO")==0) ? Color.white : ColorFondo;

ColTexto = ColTexto.toUpperCase(); // Lo convierte a Mayusculas ColorTexto = (ColTexto.compareTo("ROJO")==0) ? Color.red : ColorTexto; ColorTexto = (ColTexto.compareTo("AZUL")==0) ? Color.blue : ColorTexto; ColorTexto = (ColTexto.compareTo("NEGRO")==0) ? Color.black : ColorTexto; ColorTexto = (ColTexto.compareTo("BLANCO")==0) ? Color.white : ColorTexto;

Desplaza = Desplaza.toUpperCase(); // Lo convierte a Mayusculas Scroll = (Desplaza.compareTo("DERECHA")==0) ? false : true;

Texto=Text; offGraphics = null; // La proxima vez que pinte crea un nuevo Graphics } catch (NullPointerException e) { // Ejecuta este codigo en el caso que ColorTexto = Color.red; ColorFondo = Color.black; Scroll = true; Texto= "Parametro del applet incorrecto"; Velocidad=10; } } // algunas de las cadenas que preceden // al catch sean un nulo

//**********************************************************************//

Tutorial de Java

373

// Metodo CogerParametros() // Recoge los valores pasados como parametros dentro del codigo HTML // // Entrada: Ninguna // // Salida: void

// // // // // //

//**********************************************************************//

void CogerParametros () {

try { String Parametro=getParameter("VELOCIDAD");// Obtiene el parametro velocidad

Integer Veloc=Integer.valueOf(Parametro);

// Convierte el String en un // objeto tipo Integer

Velocidad=Veloc.intValue(); // Convierte el valor del objeto Integer // en un entero (int)

} catch (NumberFormatException e) { parametro

// La cadena introducida como // no es un numero entero

Velocidad=10;

// Establece la velocidad del Thread // en 10 milisegundos

} catch (NullPointerException e ) { Velocidad=10; }

// Cadena introducida es un nulo

try {

// Coge los parametros del applet, los compara y almacena en variables // el valor adecuado String Parametro=getParameter ("COLORFONDO"); Parametro=Parametro.toUpperCase(); // Lo convierte a Mayusculas

374

Tutorial de Java

ColorFondo = (Parametro.compareTo("ROJO")==0) ? Color.red : ColorFondo; ColorFondo = (Parametro.compareTo("AZUL")==0) ? Color.blue : ColorFondo; ColorFondo = (Parametro.compareTo("NEGRO")==0) ? Color.black : ColorFondo; ColorFondo = (Parametro.compareTo("BLANCO")==0) ? Color.white : ColorFondo;

Parametro=getParameter ("COLORTEXTO"); Parametro=Parametro.toUpperCase(); // Lo convierte a Mayusculas ColorTexto = (Parametro.compareTo("ROJO")==0) ? Color.red : ColorTexto; ColorTexto = (Parametro.compareTo("AZUL")==0) ? Color.blue : ColorTexto; ColorTexto = (Parametro.compareTo("NEGRO")==0) ? Color.black : ColorTexto; ColorTexto = (Parametro.compareTo("BLANCO")==0) ? Color.white : ColorTexto;

Parametro=getParameter ("DESPLAZAMIENTO"); Parametro=Parametro.toUpperCase(); // Lo convierte a Mayusculas Scroll = (Parametro.compareTo("DERECHA")==0) ? false : true;

Texto=getParameter ("TEXTO");

// Coge el texto

} catch (NullPointerException e) { // Excepcion producida por alguna // cadena vacia o nula ColorTexto = Color.red; ColorFondo = Color.black; Scroll = true; Texto = "Parametro del applet incorrecto"; } }

//**********************************************************************// // Metodo update() //

// Es llamado por el metodo repaint(), cada vez que se dispara el thread// // // La primera vez que es llamado crea una copia en memoria // (doble buffer) del objeto Graphics g. // Una vez creado, todos los elementos graficos, son dibujados // sobre el buffer offGraphics, una vez dibujados son mostrados en la // // // // //

Tutorial de Java

375

// pantalla del sistema. // // Entrada: Graphics g // // Salida: void

// // // // //

//**********************************************************************//

public void update (Graphics g) { // Si offGraphics no existe o cambia de tamao lo crea if ( (offGraphics == null) || (d.width != offDimension.width) || (d.height != offDimension.height) ) { offDimension = d; offImage = createImage (d.width, d.height); // Crea la imagen en // memoria offGraphics = offImage.getGraphics(); // Almacena el objeto // grafico en memoria

// Establece el font offGraphics.setFont (font);

// Establece la medida descent del Font FontMetrics fontmetrics = offGraphics.getFontMetrics(); SizeDescent = fontmetrics.getDescent(); // Almacena el ancho del la cadena StringAncho = fontmetrics.stringWidth(Texto);

// Establece las cordenadas de comienzo dependiendo de la variable // Scroll (desplazamiento a derecha o izquierda) CordX = Scroll ? d.width : -StringAncho;

CordY=(d.height-SizeDescent)-7; }

// Borra la imagen previa que tenemos en el buffer

376

Tutorial de Java

offGraphics.setColor(getBackground()); offGraphics.fillRect(0,0, d.width , d.height); offGraphics.setColor (Color.black);

offGraphics.setColor (ColorFondo);

// Establece el color de fondo

offGraphics.fillRect (0,0,d.width,d.height); // Rellena el fondo

offGraphics.setColor(ColorTexto);

// Establece el color de texto // Pinta el texto

offGraphics.drawString (Texto,CordX,CordY);

CordX= Scroll ? --CordX : ++CordX;

//Suma o resta la cordenada X, // dependiendo del valor de la // variable Scroll

// Comprueba si el texto que se desplaza llega al final del margen // izquierdo o derecho del applet if (CordX == d.width ) CordX=-StringAncho; //Derecha CordX=d.width; // Izquierda

else if (CordX == -StringAncho)

// Dibuja el marco del applet offGraphics.setColor (Color.lightGray); offGraphics.fill3DRect (0,0,d.width,5,true); offGraphics.fill3DRect (0,(d.height-5),d.width,5,true); offGraphics.fill3DRect (0,0,5,d.height,true); offGraphics.fill3DRect ((d.width-5),0,5,d.height,true);

g.drawImage (offImage, 0 ,0 ,this); // Muestra la imagen almacenada // en el buffer }

//**********************************************************************// // Metodo Cartel() // Constructor de la clase // // Entrada: Ninguna // // // // // //

Tutorial de Java

377

// Salida: void

//

//**********************************************************************//

public Cartel() { // Sin codigo }

//**********************************************************************// // Metodo getAppletInfo() // // Retorna los datos del applet, autor y sistema de desarrollo // // Entrada: Ninguna // // Salida: String // // // // // // //

//**********************************************************************//

public String getAppletInfo() { return "Name: Cartel\r\n" + "Author: Luis Angel Ortega LAO 25/11/1996\r\n" +

"Created with Microsoft Visual J++ Version 1.0"; }

//**********************************************************************// // Metodo init // // Este metodo es llamado por el AWT cuando el applet es cargado por // primera vez, o necesita ser recargado. // // Entrada: Ninguna // // Salida: Void // // // // // // // //

//**********************************************************************//

378

Tutorial de Java

public void init() { d = size(); // Almacena las dimensiones del applet resize(d.width,d.height); // Redimensiona el applet de nuevo // Es nesario en el caso de que se vuelva a // cargar el applet

Toolkit toolkit = Toolkit.getDefaultToolkit(); String h[] = toolkit.getFontList(); // Almacena en h todos los // tipos de letra

// Crea un nuevo font con con el atributo de normal // y el tamao del ancho del applet menos RESTAFONT (14) font = new Font (h[1],Font.PLAIN,(d.height-RESTAFONT));

CogerParametros();

// Llama al metodo para coger los // parametros del applet

//**********************************************************************// // Metodo destroy() // // Este metodo es llamado cuando el applet es destruido de memoria // // Entrada: Ninguna // // Salida: Void // // // // // // //

//**********************************************************************//

public void destroy() { // Sin codigo }

//**********************************************************************// // Metodo paint() //

Tutorial de Java

379

// // Este metodo es llamado cuando el applet necesita ser pintado. // Si colocamos una ventana que oculte el applet, o parte del mismo, // desplazamos o quitamos la ventana, se produce el evento paint. // // // // // Entrada: Graphics g // // Salida: Void Nota:

// // // // // //

La llamada de repaint no produce necesariamente la llamada al paint// // // // //

//**********************************************************************//

public void paint(Graphics g) { if (offImage != null ) { // Si offImage existe visuliza la imagen

g.drawImage(offImage, 0 ,0 ,this); } }

//**********************************************************************// // Metodo start // // Entrada: Ninguna // // Salida: Void // // // // //

//**********************************************************************// public void start() { if (m_Cartel == null) // Si no existe el Thread lo crea { m_Cartel = new Thread(this); m_Cartel.start(); // Produce la llamada al metodo run() } }

380

Tutorial de Java

//**********************************************************************// // Metodo stop // // Este metodo se produce cuando cambiamos // el visualizador // La llamada al metodo provoca la parada del Thread // // Entrada: Ninguna // // Salida: Void de pagina o cerramos // // // // // // // // //

//**********************************************************************//

public void stop() { if (m_Cartel != null) // Provoca la parada del Thread { m_Cartel.stop(); m_Cartel = null; } }

//**********************************************************************// // Metodo run() // // Este metodo es llamado cuando el applet del Thread es llamado // // Entrada:Ninguna // // Salida: Void // // // // // // //

//**********************************************************************//

public void run() { while (true) // Produce un bucle infinito

Tutorial de Java

381

{ try { repaint(); // Llama al metodo update() Thread.sleep(Velocidad); // Establece la velocidad del Thread } catch (InterruptedException e) // Excepcion { stop(); // Llama al metodo stop() para el Thread } } }

//Final del la clase

382

Tutorial de Java

Final y Agradecimientos
...Y esto ha sido todo, amigos! Espero que todo el esfuerzo que hemos realizado para que este Tutorial de Java pudiese ver la luz haya servido para que, al menos, alguien de los que se hayan atrevido a seguirlo dominen un poco ms el lenguaje que representa Java. Nosotros (yo) s que he aprendido mucho durante estos meses que me he pasado escribiendo y desarrollando ejemplos. Me he encontrado con problemas, que quizs en un uso normal de Java no se hubiesen presentado, sobre todo porque he utilizado tres plataformas diferentes para probar lo que haca: Solaris 2.5, Linux 1.3.20 y Windows 95. El escoger los volcados de Windows 95 para los ejemplos ha sido porque todo el Tutorial se ha escrito en Word para Windows 95.

Agradecimientos

Mi agradecimiento a Juan Antonio Ruz y Ramn M. Gmez ( btico hasta la mdula), webmaster de este site y para la Facultad de Informtica de Sevilla, por haberme permitido utilizar su servidor para que todo el mundo pueda conocer mi trabajo. Y por supuesto a mi familia, sin cuya comprensin no hubiese podido llevar este proyecto a buen trrmino, porque les he robado gran cantidad del tiempo que debera estar con ellos.

Copyrights

Este documento no tiene, en principio, copyright; aunque si se utiliza alguna informacin en l contenida, agradecra que se citase la procedencia. Por supuesto, cualquier tipo de distribucin del Tutorial est absolutamente permitida, aunque tambin me gustara conocerla. Las fuentes principales de las que he recogido informacin para poder desarrollar este Tutorial de Java corresponden a documentacin de Sun, aunque tambin a otras: The Java Tutorial de Sun Microsystems Brewing Java: A Tutorial de Elliot Rusty Harold Revista Electrnica JavaWorld Gamelan, gran fuente de applets

Tutorial de Java

383

Revista Electrnica SunWorld Online Faqs de Java en DigitalFocus WWWiz, lista impresionante de libros sobre Java, Internet, etc. Java Programming Resources, de Marty Hall, infinidad de recursos sobre Java

HotJava (tm), Java (tm) y Duke son Marcas Registradas de Sun Microsystems Microsoft es una marca registrada y el logo del Microsoft Internet Explorer es una marca registrada de Microsoft

384

Tutorial de Java