You are on page 1of 250

JAVA

1...................................................................................................................
2...................................................................................................................

introduccion
origen de java

3...................................................................................................................
caracteristicas de java
11.................................................................................................................
hot java
11.................................................................................................................
java para aplicaciones
corporativas
17.................................................................................................................
programacion en java
20.................................................................................................................
control de flujo
23.................................................................................................................
clases
24.................................................................................................................
variables y metodos de
instancia
25.................................................................................................................
alcance de objetos y reciclado
de memoria
26.................................................................................................................
herencia
27.................................................................................................................
control de acceso
27.................................................................................................................variables y metodos estaticos
28.................................................................................................................
this y super
29.................................................................................................................clases abstractas
30.................................................................................................................interfaces
31.................................................................................................................metodos nativos
32.................................................................................................................paquetes
33.................................................................................................................referencias
33.................................................................................................................punteros y referencias c++
34.................................................................................................................referencias en java
36.................................................................................................................referencias y arrays
37.................................................................................................................referencias y listas
39.................................................................................................................punteros c/c++ y referencias java
40.................................................................................................................una minima aplicacion en java
41.................................................................................................................compilacion y ejecucion de "hola
mundo"
43.................................................................................................................el visor de applets de sun
44.................................................................................................................arquitectura de appletviewer
44.................................................................................................................metodos de appletviewer
47.................................................................................................................funciones de menu de appletviewer
49.................................................................................................................un applet basico en java
51.................................................................................................................compilacion de un applet
51.................................................................................................................la marca applet de html
53.................................................................................................................atributos de applet
54.................................................................................................................paso de parametros a applet
56.................................................................................................................tokens en parametros de llamada
58.................................................................................................................el parametro archive
59.................................................................................................................depuracion general
60.................................................................................................................ciclo de vida de un applet
60.................................................................................................................proteccion de applets
61.................................................................................................................escribir applets en java
64.................................................................................................................la aplicacion fecha
66.................................................................................................................el depurador de java
71.................................................................................................................la clase math
72.................................................................................................................la clase character
72.................................................................................................................la clase float
73.................................................................................................................la clase double
74.................................................................................................................la clase integer
75.................................................................................................................la clase long
75.................................................................................................................la clase boolean
76.................................................................................................................la clase string
78.................................................................................................................la clase stringbuffer
80.................................................................................................................uso de conversiones
81.................................................................................................................introduccion a awt
81.................................................................................................................interface de usuario
82.................................................................................................................estructura del awt
82.................................................................................................................componentes y contenedores
86.................................................................................................................componentes
86.................................................................................................................botones
90.................................................................................................................etiquetas
91.................................................................................................................listas
93.................................................................................................................campos de texto
94.................................................................................................................areas de texto
96.................................................................................................................canvas
97.................................................................................................................barras de desplazamiento
99.................................................................................................................diseo de componentes propios
103...............................................................................................................creacin de aplicaciones con awt

103...............................................................................................................crear el marco de la aplicacin


105...............................................................................................................inicializar fuentes, colores y
recursos
107...............................................................................................................crear menus y barras de menus
109...............................................................................................................dialogos y ventanas
111...............................................................................................................paneles
113...............................................................................................................layouts
120...............................................................................................................crear un layout
123...............................................................................................................control de eventos
123...............................................................................................................la clase event
124...............................................................................................................tipos de evento
128...............................................................................................................generacin y propagacin de
eventos
130...............................................................................................................metodos de control de eventos
131...............................................................................................................action_event
132...............................................................................................................mejorar el diseo de interfaces
133...............................................................................................................cambio de fuentes de caracteres
134...............................................................................................................colores de fondo y texto
135...............................................................................................................fijar el tamao preferido
136...............................................................................................................uso de inserts
137...............................................................................................................habilitar y desabilitar componentes
138...............................................................................................................boton grafico
140...............................................................................................................contenedores
143...............................................................................................................objetos graficos
144...............................................................................................................lineas
145...............................................................................................................rectangulos
148...............................................................................................................circulos, elipses
151...............................................................................................................funciones graficas
153...............................................................................................................fractales
155...............................................................................................................lineas flotantes
157...............................................................................................................poligonos
157...............................................................................................................metodos para imagenes
158...............................................................................................................doble buffering de graficos
161...............................................................................................................nuevas clases para dibujos
162...............................................................................................................la clase mediatraker
165...............................................................................................................sonido en java
167...............................................................................................................entrada por raton
168...............................................................................................................manejo de excepciones
169...............................................................................................................generar excepciones en java
170...............................................................................................................excepciones predefinidas
172...............................................................................................................crear excepciones propias
172...............................................................................................................capturar excepciones
175...............................................................................................................propagacion de excepciones
178...............................................................................................................threads y multithearding
178...............................................................................................................flujo en programas
180...............................................................................................................creacion y control de threads
183...............................................................................................................arrancar y parar threads
184...............................................................................................................suspender y reanudar threads
185...............................................................................................................estados de un thread
188...............................................................................................................scheduling
189...............................................................................................................prioridades, demonios
190...............................................................................................................ejemplo de animacion
192...............................................................................................................comunicacion entre threads
197...............................................................................................................codigo nativo
197...............................................................................................................escribir codigo java
198...............................................................................................................crear el fichero de cabecera
199...............................................................................................................crear el fichero de stubs
200...............................................................................................................escribir la funcion c
201...............................................................................................................crear la libreria dinamica
201...............................................................................................................ejecutar el programa
203...............................................................................................................entrada y salida standard
205...............................................................................................................ficheros en java
207...............................................................................................................streams de entrada
210...............................................................................................................streams de salida
214...............................................................................................................ficheros de acceso aleatorio
216...............................................................................................................comunicaciones en java
216...............................................................................................................comunicaciones en unix
217...............................................................................................................sockets
218...............................................................................................................uso de sockets

219...............................................................................................................la clase url


220...............................................................................................................dominios de comunicaciones
222...............................................................................................................modelo de comunicaciones con
java
222...............................................................................................................apertura de sockets
223...............................................................................................................creacion de streams
225...............................................................................................................cierre de sockets
225...............................................................................................................minimo cliente smtp
226...............................................................................................................servidor de eco
227...............................................................................................................cliente/servidor tcp/ip
229...............................................................................................................servidor simple de http
233...............................................................................................................ejecutar tcp/ip en windows 95
236...............................................................................................................clases utiles en comunicaciones
240...............................................................................................................arquitectura
modelo/vista/controlador
242...............................................................................................................observador y observable
243...............................................................................................................utilizar observador y observable
246...............................................................................................................ejemplo de aplicacion mvc

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
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
5

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.
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
6

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

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
Java (JVM) y las libreras fundamentales, que tambin nos permitiran acceder
10

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

11

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

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.

12

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

13

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
comportamiento 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.

14

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.

15

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.

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

16

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

17

Costos 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
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:

18

public class pepe {


Guitarra flamenca ;
}
public class guitarra {
}

// Fichero: pepe.java
// Fichero: guitarra.java

% javac -verbose pepe.java


[parsed pepe.java in 720ms]
[loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 220ms]
[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
19

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.

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:
1. 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.
2. Portar o crear un servidor no-crtico en Java, de forma que tanto cliente como
servidor estn escritos en Java.
3. 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.

20

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.

PROGRAMACION 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 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.

21

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
default
do
double
else
extends
false
final
finally
float

for
goto
if
implements
import
instanceof
int
interface
long
native

new
null
package
private
protected
public
return
short
static
super

switch
synchronized
this
threadsafe
throw
transient
true
try
void
while

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

future
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
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

0xDC00

complemento
complemento
complemento
complemento

Reales en coma flotante:


float
double
Por ejemplo:

32 bits
64 bits
3.14
2e12

IEEE 754
IEEE 754
3.1E12

22

a
a
a
a

dos
dos
dos
dos

Booleanos:
true
false

Caracteres:
Por ejemplo:
unicode

\t

\u????

[????] es un nmero

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:


String nombres[];
nombres = new String[4];
nombres[0] = new String(
nombres[1] = new String(
nombres[2] = new String(
nombres[3] = new String(

"Juan" );
"Pepe" );
"Pedro" );
"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;

23

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:
.
++
!
*
+
<<
<
&
&&
? :
=

[]
-~
/
>>
>
^
||

()

op=

(*=

instanceof
%
>>>
<=
|

>=

/=

==

%=

!=

+=

-=

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.

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.

24

, - 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:
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;

25

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


ocurre 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
26

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

27

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 METODOS 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;
}
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;
{
int i = 2;
}
}

// mbito exterior
// crea un nuevo mbito
// error de compilacin

28

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
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:

29

String s;
s = new String( "abc" );
s = "def";

//
//
//
//

no se ha asignado todavia
memoria asignada
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.

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

30

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
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 METODOS ESTATICOS


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;
}

31

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
}
}

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;
}

32

// 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;
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();
mnc.Suma_a_i( 10 );

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->
}
}

33

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.

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.
34

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>
}
void stop() {
<cdigo nuevo>
}

METODOS 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.
35

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;

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.

36

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

37

tareas como la liberacin automtica de memoria, la defragmentacin de memoria, o


realizar programacin distribuida de forma clara y eficiente.

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 ) {

38

numHabitacion = numeroHab;
}
public synchronized int camas() {
return( camas );
}
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;
Habitacion llaveHab2;

//
//
}

llaveHab1 = new Habitacion( 222 );


llaveHab2 = new Habitacion( 1144,3 );
^^^^^^^^^
^^^^^^^^^^^^^^ ^^^^^^
A
B y D
C
}

// paso 1
// 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.

39

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 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[];
llavesMaestras = new Habitacion[ habPorAla ];
^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^

40

// paso 1
// 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();
}
}

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;
// . . .
}

41

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

42

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++.
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.

43

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.

UNA MINIMA APLICACION EN JAVA


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:
//
// Aplicacin HolaMundo de ejemplo
//
class HolaMundoApp {
public static void main( String args[] ) {
System.out.println( "Hola Mundo!" ) ;
}
}

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

44

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.

COMPILACION Y EJECUCION 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.

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 byte-codes, 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.

45

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 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.
46

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,
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.

47

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.

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.

METODOS 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.

48

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

49

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

50

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:

FUNCIONES DE MENU 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.

51

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

52

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 BASICO 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 ) ;
}
}

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)
*/

53

/*
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 {

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 )

54

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).

COMPILACION 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.

Prueba de un Applet
El 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.

55

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

56

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

57

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.

PASO DE PARAMETROS A 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.

58

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

59

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

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

60

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

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

61

EL PARAMETRO 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
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.

62

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

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

63

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.

PROTECCION 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.
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" ) );

64

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 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:

Carga de imgenes y sonido

El resize del applet para que tenga su tamao correcto

Asignacin de valores a las variables globales

Por ejemplo:
public void init() {
if( width < 200 || height < 200 )
resize( 200,200 );
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.

65

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.
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 );
...

66

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.
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.
67

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.

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 APLICACION 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 {

68

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

69

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

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.

Depurar HolaMundo
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

70

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:
%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 del

71

objeto

locals --

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

fuente

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
>

72

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...
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.
73

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]

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]

74

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
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, float y double
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 y float
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 );

75

System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
}

rand );
x );
y );
z );
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.

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.

76

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" );

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 =
boolean
int i =
float f

F.hashCode();
b = F.equals( Object obj );
Float.floatToIntBits( 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;

77

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

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();
boolean I.equals( Object obj );

78

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.

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;

79

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.

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.

80

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
int
int
int
int
int
int
int

indexOf( int ch );
indexOf( int ch,int fromindex );
lastIndexOf( int ch );
lastIndexOf( int ch,int fromindex );
indexOf( String str );
indexOf( String str,int fromindex );
lastIndexOf( String str );
lastIndexOf( String str,int fromindex );

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


un determinado desplazamiento.
String
String
String
String
String
String
String

substring( int beginindex );


substring( int beginindex,int endindex );
concat( String str );
replace( char oldchar,char newchar );
toLowerCase();
toUpperCase();
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 );

81

// Mucho ms correcto

Funciones de Conversin
String
String
String
String
String
String
String
String

valueOf(
valueOf(
valueOf(
valueOf(
valueOf(
valueOf(
valueOf(
valueOf(

boolean b );
int i );
long l );
float f );
double d );
Object obj );
char data[] );
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
objetos 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 );

82

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:
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
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer

append(
append(
append(
append(
append(
append(
append(
append(
append(
append(
insert(
insert(
insert(

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

83

StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer
StringBuffer

insert(
insert(
insert(
insert(
insert(
insert(

int
int
int
int
int
int

offset,boolean b );
offset,int i );
offset,long l );
offset,float f );
offset,double d );
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:


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;

84

Introduccin al 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.
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

85

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 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
86

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.

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
87

Color

Component
o

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
88

Image

Insets

MediaTracker

MenuComponent

MenuBar

MenuItem

Point

Polygon

Rectangle

Toolkit

CheckboxMenuItem

Menu

Interfaces:

LayoutManager

MenuContainer

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.

89

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
o

Panel

Window

Dialog

Frame

Label

List

Scrollbar

TextComponent
o

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");

90

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
b1
b2
b3

void init() {
= new Button( "Botn B1" );
= new Button( "Botn B2" );
= new Button( "Botn B3" );

this.add( b1 );
this.add( b2 );
this.add( b3 );
}
public boolean action( Event evt,Object obj )
if( evt.target.equals( b1 ) )
System.out.println( "Se ha pulsado el
if( evt.target.equals( b2 ) )
System.out.println( "Se ha pulsado el
if( evt.target.equals( b3 ) )
System.out.println( "Se ha pulsado el

{
boton B1" );
boton B2" );
boton B3" );

return true;
}
}

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


que hayamos pulsado.

91

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" );
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.
92

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() );
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.

93

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:

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.

94

El applet Etiqueta.java presenta dos textos en pantalla, tal como aparece en la figura
siguiente:

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 );

95

l.addItem(
l.addItem(
l.addItem(
l.addItem(
l.addItem(
l.addItem(
l.addItem(
l.addItem(
l.addItem(
add( l );
}

"Mercurio" );
"Venus" );
"Tierra" );
"Marte" );
"Jupiter" );
"Saturno" );
"Neptuno" );
"Urano" );
"Pluton" );

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(
lm.addItem(
lm.addItem(
lm.addItem(
lm.addItem(

"Mercurio" );
"Venus" );
"Tierra" );
"Marte" );
"Jupiter" );

96

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.

import java.awt.*;
import java.applet.Applet;

97

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
evt.arg.toString() );
if( evt.target.equals( tf1 ) )
System.out.println( "Campo de Texto
evt.arg.toString() );
if( evt.target.equals( tf1 ) )
System.out.println( "Campo de Texto
evt.arg.toString() );
if( evt.target.equals( tf1 ) )
System.out.println( "Campo de Texto
evt.arg.toString() );
}
return true;
}
}

1: " +
2: " +
3: " +
4: " +

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


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
98

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

99

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.*;
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;
}
}

100

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;
}
}

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
101

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 );
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;

102

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;
}

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(
orientacion =
if( orient ==
sepTama =

int lon,int thick,int orient ) {


orient;
HORIZONTAL )
new Dimension( lon,thick );

103

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

104

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

105

g.setColor(
g.drawRect(
g.setColor(
g.drawRect(
g.setColor(
g.drawLine(

Color.gray );
0, altoTexto/2,d.width-3,d.height-altoTexto/2-3 );
Color.white );
1,altoTexto/2+1,d.width-3,d.height-altoTexto/2-3 );
getBackground() );
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() );
}
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" );

106

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

CREACION 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)

CREAR EL MARCO DE LA APLICACION


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.

107

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 situados.
La ejecucin de la aplicacin mostrar la siguiente ventana en pantalla:

108

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 creado
public void paint( Graphics g ) {
g.setFont( new Font( "TimesRoman",Font.BOLD,24 ) );
g.setColor( Color.blue );
g.drawString( getTitle(),30,50 );
}

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


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

109

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 ) {
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.

110

CREAR MENUS Y BARRAS 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..." ) );
mbarra.add( m );
setMenuBar( mbarra );
}

111

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.

112

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.

113

class AboutDialog extends Dialog {


static int HOR_TAMANO = 300;
static int VER_TAMANO = 150;
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.

114

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 {
public BarraHerram() {
setLayout( new FlowLayout() );
add( new Button( "Abrir" ) );
add( new Button( "Guardar" ) );
add( new Button( "Cerrar" ) );

115

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.

116

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

117

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;
}

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:

118

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.
Con BorderLayout se podran representar botones de direccin:

119

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
botonS
botonE
botonO
botonC

add(
add(
add(
add(
add(
}

=
=
=
=
=

new
new
new
new
new

Button(
Button(
Button(
Button(
Button(

"Norte" );
"Sur" );
"Este" );
"Oeste" );
"Centro" );

"North",botonN );
"South",botonS );
"East",botonE );
"West",botonO );
"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:

import java.awt.*;
import java.applet.Applet;
public class AwtGrid extends Applet {
Button boton1,boton2,boton3,boton4,boton5,boton6;
public void init() {

120

setLayout( new GridLayout( 3,2 ) );


boton1
boton2
boton3
boton4
boton5
boton6
add(
add(
add(
add(
add(
add(
}

=
=
=
=
=
=

new
new
new
new
new
new

boton1
boton2
boton3
boton4
boton5
boton6

Button(
Button(
Button(
Button(
Button(
Button(

"1"
"2"
"3"
"4"
"5"
"6"

);
);
);
);
);
);

);
);
);
);
);
);

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:

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 );

121

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;
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

122

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
addComponente( new
addComponente( new
addComponente( new
addComponente( new
}
}

);
Label( "Nombre:" ),0,0,1,1 );
TextField( 12 ),1,0,2,1 );
TextArea( 5,20 ),0,1,2,2 );
Checkbox( "S?" ),2,1,1,1 );
List(),2,2,1,1 );

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;

123

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 );
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
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.

124

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

125

}
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:

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(
add(
add(
add(
add(

boton1 );
boton2 );
boton3 );
etiqueta );
texto );

boton1.move( 0,10 );
boton2.move( 70,10 );
boton3.move( 30,40 );
etiqueta.move( 75,70 );

126

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.
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
127

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

128

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

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
129

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.
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;

130

}
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;
}
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.

131

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.

GENERACION Y PROPAGACION 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
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.

132

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


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

133

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:
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 );
}

METODOS 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 )

134

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

135

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:
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
136

Habilitar y Deshabilitar Componentes

CAMBIO DE FUENTE 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
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.
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 );

137

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.

COLORES DE FONDO Y 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" );

138

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

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

139

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:

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().
140

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

141

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.

BOTON GRAFICO
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
{

142

// 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 );
}
}
}

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

143

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

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

144

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 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:

145

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.

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

146

OBJETOS GRAFICOS
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 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:

147

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 )
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 )

LINEAS
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:

148

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:

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;

// devuelve el tamao del applet

repaint();
}
public void paint( Graphics g ) {
g.drawRect( 0,0,ancho-1,alto-1 );
}
}

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;

149

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

150

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

151

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 );
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 superiorizquierda 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.
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;

152

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 );
}
// 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 );
}

153

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

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

154

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().

FUNCIONES GRAFICAS
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)) );
}
}

155

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
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 =
xN =
yN =
xmin
xmax
ymin
ymax
}

y0 = 0;
apAncho-1;
apAlto-1;
= -10.0;
= 10.0;
= -1.0;
= 1.0;

156

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

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.

157

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

158

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

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.

LINEAS 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;

159

public class Lineas extends Applet {


int LINEAS = 25;
int gSup = 3;
int gInf = 3;
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] =
gLineas[0][1] =
gLineas[0][2] =
gLineas[0][3] =

{
aleatorio(
aleatorio(
aleatorio(
aleatorio(

apAncho );
apAlto );
apAncho );
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) );
}

160

private void RecalcLinea( int i ) {


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

(gLineas[i][3] > apAlto) )

(gLineas[i][0] > apAncho) )

(gLineas[i][2] > apAncho) )

POLIGONOS
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.

METODOS PARA IMAGENES


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

161

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" );
}
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 GRAFICOS
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:

162

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():
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 );

163

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();
// 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();

164

NUEVAS CLASES PARA DIBUJO


La descomposicin funcional de los mtodos para dibujo proporcionados por el doblebuffering 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;
// 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;

165

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 );
// 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).

166

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
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;

167

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

168

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

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" );

169

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

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

170

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.

ENTRADA POR RATON


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;

171

ratonY = y;
repaint();
return true;

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.

MANEJO DE EXCEPCIONES
Vamos a mostrar como se utilizan las excepciones, reconvirtiendo nuestro applet de saludo
a partir de la versin iterativa de HolaIte.java:
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++;
}
}

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
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:
172

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
explcitamente, 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.

173

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

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

174

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
class A {
native void kk();
}

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


InternalException
175

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.

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

176

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

177

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

178

catch( AlgunaExcepcion e ) {
VentanaError = new DialogoError( this );
VentanaError.show();
}

PROPAGACION 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 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;

179

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

180

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
proporcionar 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.

181

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 {
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

182

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;
}
// 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 );
}

183

public class MultiHola {


public static void main( String args[] ) {
TestTh t1,t2,t3;
//
t1
t2
t3

Creamos los threads


= new TestTh( "Thread 1",(int)(Math.random()*2000) );
= new TestTh( "Thread 2",(int)(Math.random()*2000) );
= 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.

CREACION 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.
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() {
. . .

184

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 {
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.

185

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 (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

186

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.

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 ) {

187

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 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
188

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 );
}
...

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 .

189

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.

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

190

7la 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
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();

191

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

192

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.

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

193

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
realizar en segundo plano, o realizar varios en primer plano (msica y animaciones) sin que
se d apariencia de prdida de rendimiento.

EJEMPLO DE ANIMACION
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" );

194

imagenes[i] = getImage( getDocumentBase(),fichero );


// 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();

195

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.

COMUNICACION 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 ) {
// Mantiene una copia propia del objeto compartido
tuberia = t;
}
public void run() {

196

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

En este caso, como en el del productor, contamos con un mtodo en la clase Tuberia,
tuberia.recoger(), para manejar la informacin.

Monitor

197

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

198

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 );
p.start();
c.start();
}
}

Compilando y ejecutando esta aplicacin, podremos observar nuestro modelo el pleno


funcionamiento.

199

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.

200

CODIGO 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.

ESCRIBIR CODIGO JAVA


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
mtodos nativos deben estar definidos dentro de una clase Java.

201

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,
cuyo contenido ser el siguiente:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <native.h>
/* Header for class HolaMundo */

202

#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.

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.

203

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 FUNCION 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

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

204

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 LIBRERIA DINAMICA


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%

donde %JAVAHOME% es el directorio donde se ha instalado la versin actual del Java


Development Kit.

EJECUTAR EL PROGRAMA
205

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).

206

ENTRADA Y SALIDA STANDARD


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.

La clase System
Java tiene acceso a la entrada/salida estndar a travs de la clase System. En concreto, los
tres ficheros que se implementan son:
Stdin
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.*;
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 );

207

}
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

208

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.

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.

Comprobaciones y Utilidades
Una vez creado un objeto File, se puede utilizar uno de los siguientes mtodos para reunir
informacin sobre el fichero:

209

Nombres de fichero
String getName()
String getPath()
String getAbsolutePath()
String getParent()
boolean renameTo( File nuevoNombre )

Comprobaciones
boolean
boolean
boolean
boolean
boolean
boolean

exists()
canWrite()
canRead()
isFile()
isDirectory()
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
System.out.println( "Debe indicar un fichero." );
}

210

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 );

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.

211

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 {
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

212

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
diseados 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.
213

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.

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

214

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 );

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.

215

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 ) ) )
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
216

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():
// 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

217

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
void
void
void
void
void
void
void
void

writeBoolean( boolean b );
writeByte( int i );
writeShort( int i );
writeChar( int i );
writeInt( int i );
writeFloat( float f );
writeDouble( double d );
writeBytes( String s );
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:
218

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

219

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.

COMUNICACIONES EN UNIX
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

220

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

SOCKETS
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.
Diferencias entre Sockets Stream y Datagrama
221

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:
daytime
ftp
telnet
smtp
http

13/udp
21/tcp
23/tcp
25/tcp
80/tcp

telnet
mail

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
222

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]
direccionIp[1]
direccionIp[2]
direccionIp[3]

=
=
=
=

150
150
112
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. 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.
223

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 URL( String spec ) throws MalformedURLException;
public URL( String protocol,String host,int port,String file ) throws
MalformedURLException;
public URL( String protocol,String host,String file ) throws
MalformedURLException;
public 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.
Dominio Unix
Bajo Unix, hay dos dominios, uno para comunicaciones internas al sistema y otro para
comunicaciones entre sistemas.

224

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.

225

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:

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

226

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

CREACION DE STREAMS
Creacin de Streams de Entrada
227

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.
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:
228

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.

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

MINIMO 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

229

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

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.*;
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
231

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[] ) {
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.*;

232

class minimoCliente {
public static void main( String args[] ) throws IOException {
int c;
Socket s;
InputStream sIn;
// 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.
233

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.*;
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

234

// 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;
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 );
}

235

// Este mtodo analiza gramaticalmente la solicitud enviada con el


// GET y la descompone en sus partes para extraer el nombre del
// 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':

236

if( esCR )
return( new String( buf,0,0,pos ) );
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

237

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
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:
1. En el Panel de Control, seleccionar Red
2. Picar Agregar, luego Protocolo y luego Agregar
3. Seleccionar Microsoft y luego TCP/IP y picar en Aceptar
4. En este momento probablemente se solicite la introduccin de los discos de
Windows '95, o del CD-ROM
5. Seleccionar la pestaa Configuracin de la ventana Red
6. Seleccionar TCP/IP en la lista que aparece y picar Propiedades
7. Seleccionar la pestaa Direccin IP
o

Crearse una direccin IP, como por ejemplo: 102.102.102.102

Crearse una Mscara de subred, como: 255.255.255.0

8. Seleccionar la pestaa Configuracin DNS y desactivar DNS


9. Los valores correspondientes a las otras cuatro pestaas pueden dejarse los que hay
por defecto
10. Picar Aceptar
11. (Opcional) Seleccionar la pestaa Identificacin de la ventana Red
12. (Opcional) Introducir un nombre para el ordenador, como por ejemplo: breogan
13. (Opcional) Picar Aceptar
Crear una entrada en la "red"

238

1. Editar el fichero hosts.sam que est en el directorio de Windows


2. 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
3. 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
4. Salvar el fichero con el nombre "hosts" y reiniciar Windows '95
Comprobacin de la red
1. Abrir una sesin MS-DOS
2. Teclear "ping breogan"
3. Debera aparecer:
4.
5.
6.
7.
8.

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

9. Teclear "tracert 102.102.102.102"


10. Debera aparecer:
11.
12.
13.

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"

239

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. 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 UTILES 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

240

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).
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 );
}

241

// Establecemos el servidor para escuchar en el socket 4322


try {
s = new DatagramSocket( puertoRecep );
} catch( SocketException e ) {
System.out.println( "Error - "+e.toString() );
}
// 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 ) {

242

System.out.println( "Error - "+e.toString() );


}
// Crea una peticin para enviar bytes. Intenta conseguir
// la direccin IP del host
try {
IP = InetAddress.getByName( "depserver" );
} 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

243

ARQUITECTURA MODELO / VISTA / CONTROLADOR


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.

244

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.
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
245

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

246

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.

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.
247

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()
primero realiza una comprobacin para asegurarse de que el Observable que ha anunciado

248

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:
1. 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().
2. 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.

249

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.
3. 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 APLICACION 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.

250

You might also like