Professional Documents
Culture Documents
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
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)
macros (#define)
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.
Comprobacin de punteros
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.
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:
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 );
11
Si los byte-codes pasan la verificacin sin generar ningn mensaje de error, entonces
sabemos que:
El acceso a los campos de un objeto se sabe que es legal: public, private, protected
12
13
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.
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.
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.
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
// Fichero: pepe.java
// Fichero: guitarra.java
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
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
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:
Arrays
Se pueden declarar en Java arrays de cualquier tipo:
char s[];
int iArray[];
Para crear un array en Java hay dos mtodos bsicos. Crear un array vaco:
int lista[] = new int[50];
"Juan" );
"Pepe" );
"Pedro" );
"Maria" );
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.)
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
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 );
}
Bucles while
while( Boolean ) {
sentencias;
}
Bucles do/while
do {
sentencias;
}while( Boolean );
Excepciones
try-catch-throw
try {
sentencias;
} catch( Exception ) {
sentencias;
}
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;
}
}
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.
La clase MiClase contiene una variable (i) y dos mtodos, MiClase que es el constructor
de la clase y Suma_a_i( int j ).
// 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 );
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();
}
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 );
}
}
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(){}
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
}
}
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
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>
}
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 stubs, es decir, que contiene la declaracin de
las funciones
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
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.
37
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;
//
//
}
// 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
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;
//
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;
// . . .
}
41
// 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
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.
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 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!" );
Finalmente, se cierran las llaves que limitan el mtodo main() y la clase HolaMundoApp.
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
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
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
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.
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.
El applet se incializa.
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
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
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.
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 ) ;
}
}
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
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.
55
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
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 );
}
}
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>
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">
60
Antonio
fondoColor=red
textocolor=white
Para recuperar los parmetros que estn incluidos en la cadena que contiene el valor
podemos utilizar dos mtodos:
StringTokenizer( string,delimitadores )
treamTokenizer( streamentrada )
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();
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
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
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">
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.
init()
Esta funcin miembro es llamada al crearse el applet. Es llamada slo una vez. La clase
Applet no hace nada en init(). Las clases derivadas deben sobrecargar este mtodo para
cambiar el tamao durante su inicializacin, y cualquier otra inicializacin de los datos que
solamente deba realizarse una vez. Deberan realizarse al menos las siguientes acciones:
Por ejemplo:
public void init() {
if( width < 200 || height < 200 )
resize( 200,200 );
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();
}
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.
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.
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
69
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
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
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>
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 --
de una clase
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]
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
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 )
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 );
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 );
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 );
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
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 );
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 );
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 );
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
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.
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 ?";
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.
No se usan posiciones fijas de los Componentes, sino que estn situados a travs de
una disposicin controlada (layouts)
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
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
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;
}
}
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;
}
}
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;
}
}
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) );
}
}
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" );
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" );
return true;
}
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
1: " +
2: " +
3: " +
4: " +
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
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 );
}
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
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.
103
else
}
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
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 );
}
}
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 );
}
Implementacin
107
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
Fijemos los colores de la ventana para que el fondo sea Blanco y el texto resalte en
Negro
setBackground( Color.white );
setForeground( Color.black );
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 );
110
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
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
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.
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
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
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:
117
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 );
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
=
=
=
=
=
=
new
new
new
new
new
new
boton1
boton2
boton3
boton4
boton5
boton6
Button(
Button(
Button(
Button(
Button(
Button(
"1"
"2"
"3"
"4"
"5"
"6"
);
);
);
);
);
);
);
);
);
);
);
);
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
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.
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:
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 )
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 );
}
134
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 )
Uso de Insets
136
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 );
137
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.
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
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.
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
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
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
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
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();
}
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:
147
LINEAS
Si se necesita dibujar una lnea, se puede utilizar el mtodo
g.drawLine( x1,y1,ancho,alto );
148
g = new Graphics();
RECTANGULOS
Los rectngulos, al igual que los valos, se definen por un punto de inicio de coordenadas x
e y y el ancho y el alto de la caja que circunscribir a la figura.
Para dibujar un rectngulo en pantalla, basta con llamar al mtodo drawRect() indicando la
posicin en que deseamos colocar la esquina superior-izquierda del rectngulo y su ancho y
alto. Por ejemplo, el cdigo siguiente, Rect1.java, pinta un rectngulo alrededor del applet
que lo contiene:
import java.awt.*;
import java.applet.Applet;
public class Rect1 extends Applet {
int ancho,alto;
public void init() {
Dimension d = size();
ancho = d.width;
alto = d.height;
repaint();
}
public void paint( Graphics g ) {
g.drawRect( 0,0,ancho-1,alto-1 );
}
}
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
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
retornoMath = Math.random();
return( (int)( retornoMath * rango ) );
}
151
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
y0 = 0;
apAncho-1;
apAlto-1;
= -10.0;
= 10.0;
= -1.0;
= 1.0;
156
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
158
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
{
aleatorio(
aleatorio(
aleatorio(
aleatorio(
apAncho );
apAlto );
apAncho );
apAlto );
160
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.
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" );
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 );
}
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
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 ) {
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.
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 );
}
164
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.
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 )
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();
170
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++;
}
}
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!
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;
k = i/j;
}
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
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();
}
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.
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
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
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
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.
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 ) {
;
}
183
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.
// No se permite
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() ;
}
}
...
}
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();
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();
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.
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();
}
}
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
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 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();
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()).
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
195
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;
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;
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();
}
}
199
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.
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.
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
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
HolaMundo.h
204
stdio.h
Unix
Teclearemos el siguiente comando:
% cc -G HolaMundo.c HolaImp.c -o libhola.so
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%
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
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
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."
);
}
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()
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" );
Lectura de un FileInputStream
Una vez abierto el FileInputStream, se puede leer de l. El mtodo read() tiene muchas
opciones:
int read();
211
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()
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:
214
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" );
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[] );
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
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();
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 );
. . .
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 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";
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):
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();
=
=
=
=
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
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" );
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:
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:
Los sockets raw proporcionan acceso al Internet Control Message Protocol, ICMP, y se
utiliza para comunicarse entre varias entidades IP.
225
El cliente establece una conexin con la mquina host a travs del puerto que se
designe en puerto#
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 );
CREACION DE STREAMS
Creacin de Streams de Entrada
227
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 );
}
PrintStream salida;
try {
salida = new PrintStream( socketServicio.getOutputStream() );
} catch( IOException e ) {
System.out.println( e );
}
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 );
}
229
SERVIDOR DE ECO
230
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 ) );
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 );
234
235
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 );
}
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.
238
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
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
241
242
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
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.
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.
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:
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
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
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
249
250