Documente Academic
Documente Profesional
Documente Cultură
Una clase Java sólo puede tener una superclase directa. Java no soporta la
herencia múltiple.
Crear una subclase puede ser tan sencillo como incluir la clausula extends
en la declaración de la clase. Sin embargo, normalmente se deberá realizar
alguna cosa más cuando se crea una subclase, como sobreescribir métodos,
etc...
class Super {
Number unNumero;
}
class Sub extends Super {
Float unNumero;
}
La variable unNumero de Sub oculta a la vairable unNumero de Super.
Pero se puede acceder a la variable de la superclase utilizando:
super.unNumero
super es una palabra clave del lenguaje Java que permite a un método
referirse a las variables ocultas y métodos sobreescritos de una superclase.
La regla que especifica los métodos heredados por una subclase es similar a
la de las variables miembro.
Regla: Una subclase hereda todos los métodos de sus superclase que son
accesibles para la subclase (a menos que el método sea sobreescrito por la
subclase).
Sobreescribir Métodos
Sobreescribir Métodos
Una subclase puede sobreescribir completamente la implementación de un método
heredado o puede mejorar el método añadiendole funcionalidad.
Las subclases deben sobreescribir aquellos métodos que hayan sido declarados
como abstract en la superclase, o la propia subclase debe ser abstracta. Escribir
Clases y Métodos Abstractos explica con más detalle los métodos y clases
abstractos.
Ozito
Para declarar que una clase es un clase abstracta, se utiliza la palabra clave
abstract en la declaración de la clase.
abstract class Number {
. . .
}
Si se intenta ejemplarizar una clase abstracta, el compilador mostrará un error
similar a este y no compilará el programa:
AbstractTest.java:6: class AbstractTest is an abstract class. It can't be
instantiated.
new AbstractTest();
^
1 error
Métodos Abstractos
Una clase abstracta puede contener métodos abstractos, esto es, métodos que no
tienen implementación. De esta forma, una clase abstracta puede definir un
interface de programación completo, incluso porporciona a sus subclases la
declaración de todos los métodos necesarios para implementar el interface de
programación. Sin embargo, las clases abstractas pueden dejar algunos detalles o
toda la implementación de aquellos métodos a sus subclases.
Veamos un ejemplo de cuando sería necesario crear una clase abstracta con
métodos abstractos. En una aplicación de dibujo orientada a objetos, se
pueden dibujar círculos, rectángulos, líneas, etc.. Cada uno de esos objetos
gráficos comparten ciertos estados (posición, caja de dibujo) y
comportamiento (movimiento, redimensionado, dibujo). Podemos
aprovecharnos de esas similitudes y declararlos todos a partir de un mismo
objeto padre-ObjetoGrafico.
Ozito
¿Qué es un Interface?
capturar similitudes entre clases no relacionadas sin forzar una relación entre
ellas.
declarar métodos que una o varias clases necesitan implementar.
revelar el interface de programación de un objeto sin recelar sus clases (los
objetos de este tipo son llamados objetos anónimos y pueden ser útiles
cuando compartas un paquete de clases con otros desarrolladores).
En Java, un interface es un tipo de dato de referencia, y por lanto, puede
utilizarse en muchos de los sitios donde se pueda utilizar cualquier tipo
(como en un argumento de métodos y una declaración de variables). Podrás
ver todo esto en: Utilizar un Interface como un Tipo.
Algunas veces se tratra a los interfaces como una alternativa a la herencia múltiple
en las clases. A pesar de que los interfaces podrían resolver algunos problemas de
la herencia múltiple, son animales bastantes diferentes. En particular:
Ozito
Definir un Interface
Para crear un Interface, se debe escribir tanto la delcaración como el cuerpo del
interface:
declaraciondeInterface {
cuerpodeInterface
}
La Declaración de Interface declara varios atributos del interface, como su
nombre o si se extiende desde otro interface. El Cuerpo de Interface contiene las
constantes y las declaraciones de métodos del Interface.
La Declaración de Interface
El cuerpo del interface contiene las declaraciones de métodos para los métodos
definidos en el interface.Implementar Métodos muestra cómo escribir una
declaración de método. Además de las declaraciones del métodos, un interface
puede contener declaraciones de constantes. En Declarar Variables Miembros existe
más información sobre cómo construir una declaración de una variable miembro.
interface coleccion {
int MAXIMO = 500;
Observa que cada declaración de método está seguida por un punto y coma
(;) porque un interface no proporciona implementación para los métodos
declarados dentro de él.
Ozito
Implementar un Interface
Para utilizar un interface se debe escribir una clase que lo implemente. Una clase
declara todos los interfaces que implementa en su declaración de clase. Para
declarar que una clase implementa uno o más interfaces, se utiliza la palabra clave
implements seguida por una lista delimitada por comas con los interfaces
implementados por la clase.
Ozito
Una aproximación podría ser escribir una clase llamada ValordeCelda que
representara los valores que pudiera contener una celda de la hoja de
cálculo. Entonces se podrían crear distintas subclases de ValordeCelda para
las cadenas, los enteros o las ecuaciones. Además de ser mucho trabajo,
esta aproximación arbitraria fuerza una relación entre esas clases que de
otra forma no sería necesaria, y debería duplicar e implementar de nuevo
clases que ya existen.
interface CellAble {
void draw();
void toString();
void toFloat();
}
Ahora, supongamos que existen objetos Línea y Columna que contienen un
conjunto de objetos que implementan el interface CellAble. El método
setObjectAt() de la clase Línea se podría parecer a esto:
class Línea {
private CellAble[] contents;
. . .
void setObjectAt(CellAble ca, int index) {
. . .
}
. . .
}
Observa el uso del nombre del interface en la declaración de la variable miembro
contents y en la declaración del argumento ca del método. Cualquier objeto que
implemente el interface CellAble, sin importar que exista o no en el árbol de clases,
puede estar contenido en el array contents y podría ser pasado al método
setObjectAt().
Ozito
¿Qué es un Thread?
Todos los programadores están familiarizados con la escritura de programas
secuenciales. Tú probablemente hayas escrito un programa que muestre "Hello
World!", o que ordene una lista de nombres, o que calcule la lista de números
primos. Estos son programas secuenciales: cada uno tiene un principio, una
secuencia de ejecución y un final. En un momento dado durante la ejecución del
programa hay un sólo punto de ejecución.
Ozito
Un sencillo Thread de Ejemplo
Este ejemplo define dos clases: SimpleThread y TwoThreadsTest. Empecemos
nuestra exploración de la aplicación con la clase SimpleThread -- una subclase de la
clase Thread, que es proporcionada por el paquete java.lang:
class SimpleThread extends Thread {
public SimpleThread(String str) {
super(str);
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {}
}
System.out.println("HECHO! " + getName());
}
}
El primer método de esta clase es un constructor que toma una cadena como su
único argumento. Este constructor está implementado mediante una llamada al
consturctor de la superclase y es intresante para nosotros sólo porque selecciona el
nombre del Thread, que se usará más adelante en el programa.
class TwoThreadsTest {
public static void main (String[] args) {
new SimpleThread("Jamaica").start();
new SimpleThread("Fiji").start();
}
}
El método main() también arranca cada uno de los threads inmediatamente
después siguiendo su construcción con una llamada al método start(). El programa
daría una salida parecida a esta:
0 Jamaica
0 Fiji
1 Fiji
1 Jamaica
2 Jamaica
2 Fiji
3 Fiji
3 Jamaica
4 Jamaica
4 Fiji
5 Jamaica
5 Fiji
6 Fiji
6 Jamaica
7 Jamaica
7 Fiji
8 Fiji
9 Fiji
8 Jamaica
HECHO! Fiji
9 Jamaica
HECHO! Jamaica
Observa cómo la salida de cada uno de los threads se mezcla con la salida del otro.
Esto es porque los dos threads SimpleThread se están ejecutando de forma
concurrente. Así, los dos métodos run() se stán ejecutando al mismo tiempo y
cada thread está mostrándo su salida al mismo tiempo que el otro.
Ozito
Atributos de un Thread
Por ahora, te has familiarizado con los threads y has visto un sencillo programa
Java que ejecuta dos thread concurrentemente. Esta página presenta varias
características específicas de los threads Java y proporciona enlaces a las páginas
que explican cada característica con más detalle.
Los thredas java están implementados por la clase Thread, que es una parte
del paquete java.lang. Esta clase implementa una definición de threads
independiente del sistema. Pero bajo la campana, la implementación real de
la operación concurrente la proporciona una implementación específica del
sistema. Para la mayoría de las aplicaciones, la implementación básica no
importa. Se puede ignorar la implementación básica y programar el API de
los thread descrito en estas lecciones y en otra documentación
proporcionada con el sistema Java.
Estado de un Thread
La prioridad de un Thread
Threads Daemon
Estos threads son aquellos que porporcionan un servicio para otros threads
del sistema. Cualquier thread Java puede ser un thread daemon.
Grupos de Threads
Ozito
El Cuerpo de un Thread
Toda la acción tiene lugar en el cuerpo del thread, que es el método run() del
thread. Después de crear e inicializar un thread, el sistema de ejecución llama a su
método run(). El código de este método implementa el comportamiento para el
que fue creado el thread. Es la razón de existir del thread.
Puedes elegir una de estas dos formas para proporcionar un método run() a
un thread Java:
Existen varias buenas razones para elegir uno de estas dos opciones. Sin embargo,
para la mayoría de los casos, la mejor opción será seguir está regla del pulgar:
Regla del Pulgar: si tu clase debe ser una subclase de otra clase (el ejemplo más
común son lo applets), deberás utilizar Runnable descrito en la sección Nº 2.
Ozito
El applet del reloj utiliza el interface Runnable para proporcionar el método run()
para su thread. Para ejecutarse dentro de un navegador compatible con Java, la
clase Clock debe derivar de la clase Applet. Sin embargo este applet también
necesita un thread para poder actualizar continuamente la pantalla sin tomar
posesión del proceso en el que se está ejecutando. (Algunos navegadores, pero no
todos, crean un nuevo thread para cada applet para impedir que un applet
descortés tome posesión del thread principal del navegador. Sin embargo, no
deberías contar con esto cuando escribas tus applets; los applets deben crear sus
propios threads cuando hagan un trabajo de calculo intensivo). Como el lenguaje
Java no soporta la herencia múltiple, la clase Class no puede heredarse desde la
clase Thread y de la clase Applet a la vez. Por eso, la clase Clock debe utilizar el
interface Runnable para proporcionar el comportamiento de sus thread.
El Interface Runnable
Crear el Thread
Parar el Thread
El Método Run
Ozito
La siguiente sentencia crea un nuevo thread pero no lo arranca, por lo tanto deja el
thread en el estado : "New Thread" = "Nuevo Thread".
Thread miThread = new MiClaseThread();
Cuando un thread está en este estado, es sólo un objeto Thread vacío. No se han
asignado recursos del sistema todavía para el thread. Así, cuando un thread está en
este estado, lo único que se puede hacer es arrancarlo o pararlo. Llamar a otros
métodos distintos de start() o stop() no tiene sentido y causa una excepción del
tipo IllegalThreadStateException.
Ejecutable
Un thread entra en el estado "No Ejecutable" cuando ocurre uno de estos cuatro
eventos:
Por ejemplo, la línea en negrita del siguiente fragmento de codigo pone a dormir
miThread durante 10 segundos (10.000 milisegundos):
Thread miThread = new MiClaseThread();
miThread.start();
try {
miThread.sleep(10000);
} catch (InterruptedException e){
}
Durante los 10 segundos que miThread está dormido, incluso si el proceso se
vuelve disponible miThread no se ejecuta. Después de 10 segundos, miThread se
convierte en "Ejecutable" de nuevo y, si el procesar está disponible se ejecuta.
Esta lista indica la ruta de escape para cada entrada en el estado "No
Ejecutable":
Muerto
Un thread puede morir de dos formas: por causas naturares o siendo asesinado
(parado). Una muerte natural se produce cuando su método run() sale
normalmente. Por ejemplo, el bucle while en este método es un bucle finito -- itera
100 veces y luego sale.
public void run() {
int i = 0;
while (i < 100) {
i++;
System.out.println("i = " + i);
}
}
Un thread con este método run() moriría natualmente después de que el bucle y el
método run() se hubieran completado.
El método stop() provoca una terminación súbita del método run() del
thread. Si el método run() estuviera realizando cálculos sensibles, stop()
podría dejar el programa en un estado inconsistente. Normalmente, no se
debería llamar al método stop() pero si se debería proporcionar una
terminación educada como la selección de una bandera que indique que el
método run() debería salir.
La Excepción IllegalThreadStateException
El Método isAlive()
Una última palabra sobre el estrado del thread: el interface de programación de la
clase Thread incluye un método llamado isAlive(). Este método devuelve true si el
thread ha sido arrancado y no ha parado. Así, si el método isAlive() devuelve false
sabrás que se trata de un "Nuevo thread" o de un thread "Muerto". Por el contrario
si devuelve true sabrás que el thread esá "Ejecutable" o "No Ejecutable". No se
puede diferenciar entre un "Nuevo thread" y un thread "Muerto", como tampoco se
puede hacer entre un thread "Ejecutable" y otro "No Ejecutable"
La Prioridad de un Thread
Anteriormente en esta lección , hemos reclamado que los applets se ejecuten de
forma concurrente. Mientras conceptualmente esto es cierto, en la práctica no lo es.
La mayoría de las configuraciones de ordenadores sólo tienen una CPU, por eso los
threads realmente se ejecutan de uno en uno de forma que proporcionan una
ilusión de concurrencia. La ejecución de varios threads en una sola CPU, en algunos
órdenes, es llamada programación. El sistema de ejecución de Java soporta un
algoritmo de programación deterministico muy sencillo conocido como
programación de prioridad fija.Este algoritmo programa los threads basándose en
su prioridad relativa a otros threads "Ejecutables".
Luego el segundo thread tiene una oprtunidad para ejecutarse, y así continuamente
hasta que el interprete abandone.
Regla del Pulgar: En un momento dado, el thread con prioridad superior se está
ejecutando. Sin embargo, este no es una garantía. El programador de threads
podría elegir otro thread con prioridad inferior para evitar el hambre. Por esta
razón, el uso de las prioridades sólo afecta a la politica del programador para
propósitos de eficiencia. No dependas de la prioridad de los threads para algoritmos
incorrectos.
La carrera de Threads
Este código fuente Java implementa un applet que anima una carrera entre dos
threads "corredores" con diferentes prioridades. Cuando pulses con el ratón sobre
el applet, arrancan los dos corredores. El corredor superior , llamado "2", tiene una
prioridad 2. El segundo corredor, llamado "3", tiene una prioridad 3.
Además de los dos threads corredores, el applet tiene un tercer thread que
controla el dibujo. El método run() de este thread contiene un bucle infinito;
durante cada iteración del bucle dibuja una línea para cada corredor (cuya
longitud se calcula mediante la variable tick), y luego duerme durante 10
milisegundos. Este thread tiene una prioridad de 4 -- superior que la de los
corredores. Por eso, siempre que se despierte cada 10 milisegundos, se
convierte en el thread de mayor prioridad, prevalece sobre el thread que se
está ejecutando, y dibuja las líneas. Se puede ver cómo las líneas van
atravesando la página.
Como puedes ver, esto no es una carrera justa porque un corredor tiene
más prioridad que el otro. Cada vez que el thread que dibuja abandona la
CPU para irse a dormir durante 10 milisegundos, el programador elige el
thread ejecutable con una prioridad superior; en este caso, siempre será el
corredor llamado "3". Aquí tienes otra versión del applet que implementa
una carrera justa, esto es, los dos corredores tienen la misma prioridad y
tienen las mismas posibilidades para ser elegidos.
En esta carrera, cada vez que el thread de dibujo abandona la CPU, hay dos
threads ejecutables con igual prioridad -- los corredores -- esperando por la
CPU; el programador debe elegir uno de los threads. En esta situación, el
programador elige el siguiente thread en una especie de competición
deportiva.
Threads Egoistas
Tiempo-Compartido
En sistemas, como Windows 95, la lucha contra el comportamiento egoista de los
threads tiene una estrategia conocida como tiempo-compartido. Esta estrategia
entra en juego cuando existen varios threads "Ejecutables" con igual prioridad y
estos threads son los que tienen una prioridad mayor de los que están compitiendo
por la CPU. Por ejemplo, este programa Java (que está basado en la carrera de
Applets anterior) crea dos threads egoistas con la misma prioridad que tienen el
siguiente étodo run():
public void run() {
while (tick < 400000) {
tick++;
if ((tick % 50000) == 0) {
System.out.println("Thread #" + num + ", tick = " + tick);
}
}
}
Este método contiene un bucle ajustado que incrementa el entero tick y cada
50.000 ticks imprime el indentificador del thread y su contador tick.
Sumario
La mayoría de los ordenadores sólo tienen una CPU, los threads deben
compartir la CPU con otros threads. La ejecución de varios threas en un sólo
CPU, en cualquier orden, se llama programación. El sistema de ejecución
Java soporta un algoritmo de programación determinístico que es conocido
como programación de prioridad fija.
A cada thread Java se le da una prioridad numérica entre MIN_PRIORITY y
MAX_PRIORITY (constantes definidas en la clase Thread). En un momento
dato, cuando varios threads están listos para ejecutarse, el thread con
prioridad superior será el elegido para su ejecución. Sólo cuando el thread
para o se suspende por alguna razón, se empezará a ejecutar un thread con
priporidad inferior.
La programación de la CPU es totalmente preemptiva. Si un thread con
prioridad superior que el que se está ejecutando actualmente necesita
ejecutarse, toma inmediatamente posesión del control sobre la CPU.
El sistema de ejecución de Java no hace abandonar a un thread el control de
la CPU por otro thread con la misma prioridad. En otras palabras, el sistema
de ejecución de Java no comparte el tiempo. Sin embargo, algunos sistemas
si lo soportan por lo que no se debe escribir código que esté relacionado con
el tiempo compartido.
Además, un thread cualquiera, en cualquier momento, puede ceder el control
de la CPU llamando al método yield(). Los threads sólo pueden 'prestar' la
CPU a otros threads con la misma priorida que él -- intentar cederle la CPU a
un thread con prioridad inferior no tendrá ningún efecto.
Cuando todos los threads "ejecutables" del sistema tienen la misma
prioridad, el programador elige a uno de ellos en una especie de orden de
competición.
Threads Servidores
Cualquier thread Java puede ser un thread daemon "Servidor". Los threads daemon
proporcionan servicios para otros threads que se están ejecutando en el mismo
proceso que él. Por ejemplo, el navegador HotJava utiliza cuatro threads daemon
llamados "Image Fetcher" para buscar imágenes en el sistema de ficheros en la red
para los threads que las necesiten. El método run() de un thread daemon
normalmente es un bucle infinito que espera una petición de servicio.
Grupos de Threads
Cada thread de Java es miembro de un grupo de threads. Los grupos proporcionan
un mecanismo para la colección de varios threads dentro de un sólo objeto y la
manipulación de esos threads de una vez, mejor que de forma individual. Por
ejemplo, se puede arrancar o suspender todos los threads de un grupo con una sóla
llamada a un método. Los grupos de threads de Java están implementados por la
clase ThreadGroup del paquete java.lang.
Nota: Si se crea un thread dentro de un applet, el grupo del nuevo thread podría
ser distinto de "main" -- depende del navegador o del visualizador donde se esté
ejecutando al applet. Puedes referirse a Threads en Applets para obtener más
información sobre los grupos de threads en los applets.
La Clase ThreadGroup
Una vez que obtenido el Grupo de un thread, puedes pedir más información sobre
el grupo, como cuántos threads más hay en el grupo. También se pueden modificar
todos los threads del grupo (como suspenderlos, pararlos, etc...) con una sola
llamada a un método.
La Clase ThreadGroup
La clase ThreadGroup maneja grupos de threads para las aplicaciones Java. Un
ThreadGroup puede contener cualquier número de threads. Los threads de un
grupo generalmente están; relacionados de alguna forma, como por quién fueron
creados, qué función realizan o cuándo deben arrancar o parar.
getMaxPriority(), y setMaxPriority()
getDaemon(), y setDaemon()
getName()
getParent(), y parentOf()
toString()
La clase ThreadGroup tiene tres métodos que le permiten modificar el estado actual
de todos los threads de un grupo.
resume()
stop()
suspend()
Estos métodos aplican el cambio de estado apropiado a todos los threads del grupo
y sus subgrupos.
Esta es una lista de métodos de la clase Thread que llaman a checkAccess() antes
de proceder:
Una aplicación Java solitaria no tiene un controlador de seguridad por defecto. Esto
es, por defecto, no se imponen restricciones a ningún thread para que pueda
inspeccionar o modificar cualquier otro thread, sin importar el grupo en el que se
encuetra. Se puede definir e implementar propias restricciones de acceso para los
grupos de threads mediante la subclasificación de la clase SecurityManager,
sobreescribiendo los métodos apropiados, e instalandolo como el controlador de
seguridad para su aplicación.
El navegador HotJava es un ejemplo de aplicación que implementa su propio
controlador de seguridad. HotJava necesita asegurarse de que los applets
tengan un buen comportamiento y no hagan cosas sucias a otros applets
que se están ejecutando al mismo tiempo (como bajar la prioridad de otros
threads de otros applets). El controlador de seguridad de HotJava no permite
que un thread modifique threads de otro grupo. Por favor, observa que las
restricciones de acceso basadas en los grupos de threads pueden variar de
un navegador a otro y por eso tus applets pueden tener comportamientos
diferentes en diferentes navegadores.
Ozito
Volatile
Ozito
Sincronización de Threads
Las lecciones anteriores contenían ejemplos con threads asíncronos e
independientes. Esto es, cada thread contenía todos los datos y métodos necesarios
y no requerian recursos externos. Además, los threads de esos ejemplos se
ejecutaban en su propio espacio sin concernir sobre el estado o actividad de otros
threads que se ejecutaban de forma concurrente.
Por ejemplo, puedes imaginar una aplicación Java donde un thread (el
productor) escribe datos en un fichero mientras que un segundo thread (el
consumidor) lee los datos del mismo fichero. O si tecleas caracteres en el
teclado, el thread productor situa las pulsaciones en una pila de eventos y el
thread consumidor lee los eventos de la misma pila. Estos dos ejemplos
utilizan threads concurrentes que comparten un recurso común; el primero
comparte un fichero y el segundo una pila de eventos. Como los threads
comparten un recurso común, deben sincronizarse de alguna forma.
El Ejemplo Productor/Consumidor
. . .
Consumidor #1 obtiene: 3
Productor #1 pone: 4
Productor #1 pone: 5
Consumidor #1 obtiene: 5
. . .
Otro problema podría aparecer si el consumidor fuera más rápido que el Productor y
consumiera el mismo valor dos o más veces. En esta situación el Consumidor
imprimirá el mismo valor dos veces y podría producir una salida como esta:
. . .
Productor #1 pone: 4
Consumidor #1 obtiene: 4
Consumidor #1 obtiene: 4
Productor #1 pone: 5
. . .
De cualquier forma, el resultado es erróneo. Se quiere que el consumidor obtenga
cada entero producido por el Productor y sólo una vez. Los problemas como los
escritos anteriormente,se llaman condiciones de carrera. Se alcanzan cuando varios
threads ejecutados asíncronamente intentan acceder a un mismo objeto al mismo
tiempo y obtienen resultados erróneos.
Monitores
Los objetos, como el CubbyHole que son compartidos entre dos threads y cuyo
acceso debe ser sincronizado son llamados condiciones variables. El lenguaje Java
permite sincronizar threads alrededor de una condición variable mediante el uso de
monitores. Los monitores previenen que dos threads accedan simultáneamente a la
misma variable.
El programa Principal
Aquí tienes una pequeña aplicación Java que crea un objeto CubbyHole, un
Producer, un Consumer y arranca los dos threads.
class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
p1.start();
c1.start();
}
}
La Salida
Introducción al UI de Java
Esta lección ofrece una introducción a todo lo que le proporciona el entorno Java
para ayudarte a crear un interface de usuario (UI). UI es un término que se refiere
a todos los caminos de comunicación entre un programa y sus usuarios. UI no es
sólo lo que ve el usuario, también es lo que el usuario oye y siente. Incluso la
velocidad con la que un programa interactua con el usuario es una parte importante
del UI del programa.
Ejecutar Sonidos
Justo ahora, los applets pueden ejecutar sonidos, pero las aplicaciones no
pueden (al menos de una forma portable). Puedes ver Ejecutar Sonidos para
más información sobre la ejecución de sonidos en los applets.
Los canales de entrada, salida y error estandard son un forma al viejo estilo
de presentar un interface de usuario. Todavía es útil para probar y depurar
programas, así como para alguna funcionalidad no dirigida al usuario típico.
Puedes ver Los Canales de I/O Estandard para obtener información sobre la
utilización de los canales de entrada, salida y error estandards.
Otras clases del AWT incluyen aquellas que trabajan en un contexto gráfico
(incluyendo las operaciones de dibujo básico), imágenes, eventos, fuentes y
colores. Otro grupo importante de clases del AWT son los controladores de
distribución o disposición que controlan el tamaño y la posición de los componentes.
Ozito
Las clases Button, Checkbox, Choice, List, MenuItems, y TextField proporcionan los
controles básicos. Estas son las formas más comunes en que el usuario da
instrucciones al programa Java. Cuando un usuario activa uno de estos controles --
pulsa un botón o presiona la tecla return en un campo de texto, por ejemplo --
envía un evento (ACTION_EVENT). Un objeto que contiene el control puede
reaccionar al evento implementando el método action().
Cuando los controles básicos no son apropiados, puede utilizar las clases Scrollbar y
TextArea para obtener la entrada del usuario. La clase Scrollbar se utiliza tanto
para deslizadores como para barras de desplazamiento. Puedes ver los deslizadores
en La Anatomía de un Programa Basado en GUI. Puedes ver las barras de
desplazamiento en las listas y áreas de texto en el applet de está página.
Etiquetas
La clase Label sólo muestra una línea de texto no editable por el usuario.
Sumario
Esta página representa una visión relámpago sobre los componentes del AWT. Cada
componente mencionado en esta página se describe con más detalle en Utilizar
Componentes, los Bloques de Construcción del GUI.
Ozito
Ozito
Dibujo
Los componentes se dibujan desde la parte superior del árbol -- la ventana del
programa -- hasta los componentes sin contenedor.
Manejo de Eventos
Las acciones del usuario resultan en eventos, que son pasados a través del árbol de
componentes hasta que un objeto responda al evento.
Ozito
Esta página recuenta todos los objetos que son creados en el programa. No
te preocupes -- no esperamos que lo entiendas todo aún. Sólo queremos
explicarte la clase de objetos que un programa GUI podría utilizar.
Ozito
El Árbol de Componentes
El Programa de Ejemplo tiene varios niveles de herencia en el árbol de
componnetes. El padre de cada nivel es un contenedor (que desciende de
Component). Abajo tienes una figura de la herencia. >
un Frame
|
...
|
un Converter
|
----------------------------------
| |
un ConversionPanel (metricPanel) un ConversionPanel (usaPanel)
| |
------------------- -------------------
| | | | | |
un Label | un Choice un Label | un Choice
| |
-------------- ---------------
| | | |
un TextField un Scrollbar un TextField un Scrollbar
Explicación
Debajo del Frame esta un objeto Converter, que desciende de la clase Applet
y que es un Contenedor (especificamente, un Panel). Dependiendo el
visualizador en que se esté mostrando el applet, podría haber uno o más
contenedores entre el objeto Converter y el Frame de la parte superior del
árbol de herencia.
Sumario
Ozito
Dibujo de Componentes
Cuando un programa Java con un GUI necesita dibujarse a sí mismo -- si es la
primera vez, o porque se ha vuelto visible o porque necesita cambiar su apariencia
para reflejar algo que ha sucedido dentro del programa -- empieza con el
componente más alto que necesita ser redibujado (por ejemplo, el componente
superior en el árbol de la herencia) y va bajando hacia los componentes inferiores.
Esto está orquestrado por el sistema de dibujo del AWT.
un Frame
|
...
|
un Converter
|
----------------------------------
| |
un ConversionPanel (metricPanel) un ConversionPanel (usaPanel)
| |
------------------- -------------------
| | | | | |
un Label | un Choice un Label | un Choice
| |
-------------- ---------------
| | | |
un TextField un Scrollbar un TextField un Scrollbar
De esta forma, cada componente se dibuja a sí mismo antes que los componentes
que contiene. Esto asegura que el fondo de un Panel, por ejemplo, sólo sea visible
cuando no está cubierto por ninguno de sus componentes.
Los programas sólo pueden dibujarse cuando el AWT les dice que lo hagan. La
razón es que cada ocurrencia de que un dibujo se dibuje a sí mismo debe
ejecutarse sin interrupción. De otra forma podrían producirse resultados
impredecibles, como que un botón se dibujará sólo por la mitad, cuando fuera
interrumpido por un alguna animación lenta. El AWT ordena las peticiones de
redibujado mediante su ejecución en un thread. Un componente puede utilizar el
método repaint() para pedir que sea programado para redibujarse.
El Objeto Graphics
Para más información sobre cómo dibujar, puedes ver la lección Trabajar con
Gráficos.
Ozito
Manejo de Eventos
Cuando el usuario actúa sobre un componente -- pulsando el ratón o la tecla
Return, por ejemplo -- se crea un objeto Event. El sistema manejador de eventos
del AWT pasa el Evento a través del árbol de componentes, dando a cada
componente una oportunidad para reaccionar ante el evento antes de que el código
dependiente de la plataforma que implementan todos los componentes lo procese.
El Objeto Event
El tipo del evento -- por ejemplo, una pulsación de tecla o un click del ratón,
o un evento más abstracto como "acción" o iconificación de una ventana.
El objeto que fue la "fuente" del evento -- por ejemplo, el objeto Button
correspondiente al botón de la pantalla que pulsó el usuario, o el objeto
TextField en el que el usuario acaba de teclear algo.
Un campo indicando el momento en que ocurrió el evento.
La posición (x,y) donde ocurrió el evento. Esta posición es relativa al origen
del Componente a cuyo manejador de eventos se pasó este evento.
La tecla que fue pulsada (para eventos de teclado).
Un argumento arbitrario (como una cadena mostrada en el Componente)
asociada con el evento.
El estado de las teclas modificadoras, como Shift o Control, cuando ocurrió el
evento.
action() (Event.ACTION_EVENT)
mouseEnter() (Event.MOUSE_ENTER)
mouseExit() (Event.MOUSE_EXIT)
mouseMove() (Event.MOUSE_MOVE)
mouseDown() (Event.MOUSE_DOWN)
mouseDrag() (Event.MOUSE_DRAG)
mouseUp() (Event.MOUSE_UP)
keyDown() (Event.KEY_PRESS or Event.KEY_ACTION)
keyUp() (Event.KEY_RELEASE or Event.KEY_ACTION_RELEASE)
gotFocus() (Event.GOT_FOCUS)
lostFocus() (Event.LOST_FOCUS)
handleEvent() (all event types)
Una Nota sobre el Método action(): Los eventos Action son eventos de
alto nivel. Son causados por uno o más eventos de bajo nivel como una
pulsación de tecla y de ratón. Por esta razón, es correcto devolver true para
parar el evento actión antes de que siga subiendo por el árbol de
componentes después de haberlo manejado -- el código dependiente de la
plataforma ya ha manejado los eventos de teclas o del ratón que ha lanzado
la acción, no necesita ver de nuevo el evento action.
Ozito
Ozito
Cuando leas esta lección, observarás código para añadir componentes a los
contenedores. Esto es así porque para que cualquier objeto Component pueda
dibujarse en la pantalla, excepto una ventana, primero debes añadirlo a un objeto
Container. Este Contenedor es a su vez un Componente, y también debe ser
añadido a otro Contenedor. Las Ventanas, como los Marcos y los Cuadros de
Diálogos son contendores de alto nivel; y son los únicos componentes que no son
añadidos a otros contendores.
Todos los componentes, excepto los menús, están implementados como subclases
de la clase Component . De esta clase heredan una enorme cantidad de
funcionalidades. Por ahora, ya deberías saber que la clase Component proporciona
lo básico para el dibujo y el manejo de eventos. Aquí tienes una lista más completa
sobre las funcionalidades proporcionadas por la clase Component:
Manejo de Eventos.
Manejo de Imágenes.
Ozito
Abajo hay un applet que muestra tres botones. Cuando se pulsa el botón
izquierdo, se desactivará el botón central (y así mismo, ya que ahora no es
útil) y activa el botón derecho. Cuando se pulse el botón derecho, se
activará el botón cental y el botón izquierdo y se desactivará a sí mismo.
Abajo tienes el código que crea los tres botones y reacciona a las
pulsaciones de los botones. (Aquí tienes el programa completo.)
Ozito
Los Canvas también son útiles cuando se quiera un control -- un botón, por
ejemplo -- que se parezca a la implementación por defecto de ese control.
Como no se puede cambiar la apariencia de los controles estandards creando
una subclase de la clase Component correspondiente (Button, por ejemplo),
en su lugar se puede crear una subclase de Canvas que tenga el
comportamiento que se quiera y el mismo comportamiento que la
implementación por defecto del control.
Aquí tienes un applet que utiliza dos ejemplares de una subclase de Canvas:
ImageCanvas.
this.image = image;
pappy = parent;
w = initialWidth;
h = initialHeight;
minSize = new Dimension(w,h);
}
g.drawRect(0, 0, w - 1, h - 1);
g.drawImage(image, 0, 0, this);
}
}
}
Para más información sobre el dibujo de gráficos, puedes ver la lección Trabajar
con Gráficos. Para ver un ejemplo de implementación de la clase Canvas que dibuja
gráficos del cliente y maneja eventos, puedes ver el código del applet
RectangleDemo. Puedes ver RectangleDemo en acción en Dibujar Formas.
Ozito
Cómo Utilizar la Clase Checkbox
La clase Checkbox proporciona cajas de chequeo -- botones con dos estados que
pueden estar "on" o "off". (Quizás podría conocer este elemento UI como botón de
radio.) Cuando el usuario pulsa un botón de radio, cambia su estado y genera un
evento Action. Otras formas de proporcionar grupos de botones de radio que
pueden ser seleccionados son choices, lists, y menus.
Aquí tienes el programa completo. Abajo tienes el código que crea los dos
grupos de botones de radio. Observa que sólo elsegundo, el grupo
mutuamente exclusivo de botones está controlado por un CheckboxGroup.
Junto con los métodos de la clase Checkbox mostrados arriba, esta clase
tiene dos métodos adiconales que podrías querer utilizar:
getCheckboxGroup() y setCheckboxGroup(). Junto con el sencillo
constructor CeckboxGroup utilizado en el código de ejemplo, CheckboxGroup
también define los siguientes métodos: getCurrent() y setCurrent().
Estos métodos obtienen y cambian (respectivamente) el botón de radio
seleccionado actualmente.
Ozito
Abajo tienes un applet que tiene una lista desplegable y una etiqueta.
Cuando el usuario elegie un ítem de la lista, la etiqueta cambia para reflejar
el ítem elegido. Observa que el índice del primer ítem de una lista
desplegable es 0.
Abajo tienes el código que crea la lista desplegable y maneja los eventos.
(Aquí tienes el programa completo.) Observa que el segundo parámetro del
método action() (que es el mismo que e.arg), es la cadena del ítem
seleccionado.
. . .
int countItems()
String getItem(int)
void select(int)
void select(String)
Ozito
Los cuadros de diálogo pueden ser modales. Los cuadros de diálogo modales
requieren la atención del usuario, para evitar que el usuario haga nada en la
aplicación del cuadro de diálogo hasta que se haya finalizado con él. Por
defecto, los cuadros de diálogo no son modales -- el usuario puede
mantenerlos y seguir trabajando con otras ventanas de la aplicación.
Aquí tienes el código para la ventana que muestra el applet anterior. Este
código puede ser ejecutado como una aplicación solitaria o, con la ayuda de
la clase AppletButton, como un applet. Aquí sólo está el código que
implementa el objeto Dialog:
if (dialog == null) {
dialog = new SimpleDialog(this, "A Simple Dialog");
}
dialog.show();
Dialog(Frame, boolean)
boolean isModal()
Ozito
Las páginas Cómo utilizar la Clase Menu y Cómo Utilizar la Clase Dialog son
dos de las muchas de este tutorial que utilizan un Frame.
public MenuWindow() {
...//Este constructor llama implícitamente al
constructor sin argumentos
//de la clase Frame y añade los componentes de la
ventana
}
window.setTitle("MenuWindow Application");
window.pack();
window.show();
}
}
El método pack(), que es llamado desde el método main(), está definido por la
clase Windows. Puedes ver Cómo Utilizar la Clase Dialog para más información
sobre pack().
void remove(MenuComponent)
Ozito
Las etiquetas se utilizan en todos los ejemplos de este tutorial. Por ejemplo
el applet de Cómo Utilizar la Clase Choice, utiliza una etiqueta que muestra
información sobre el ítem que está actualmente seleccionado.
El applet crea tres etiquetas, cada una con una alineación diferente. Si el
área de display de cada etiqueta fuera igual a la anchura del texto de la
etiqueta, no se vería ninguna diferencia en la alineación de las etiquetas. El
texto de cada etiqueta sólo se mostrará en el espacio disponible. Sin
embargo, este applet hace que cada etiqueta sea tan ancha como el applet,
que es mayor que cualquiera de las etiquetas. Como resultado, puede ver
una posición horizontal diferente en el dibujo del texto de las tres etiquetas.
Aquí tienes el programa completo.
Abajo tienes el código que utiliza el applet para crear las etiquetas y
seleccionar su alineación. Para própositos de enseñanza este applet utiliza
los tres constructores de la clase Label.
Label label1 = new Label();
label1.setText("Right");
Label label2 = new Label("Center");
label2.setAlignment(Label.CENTER);
Label label3 = new Label("RIGHT", Label.RIGHT);
Ozito
Abajo tienes un applet que muestra dos listas, junto con un área de texto
que muestra información sobre los eventos. La lista superior (que son
números en español) permite múltiples selecciones. La inferior (que son
numeros italianos) sólo permite una selección. Observa que el primer ítem
de cada lista tiene índice 0.
Abajo tienes el código que crea las listas y maneja sus eventos. (Aquí tienes
el programa completo.) Observa que el dato e.arg para los enventos Action
(que es pasado dentro del método action() como su segundo argumento)
es el nombre del ítem activado, similar a los argumentos para los eventos
Action de otros componentes como los botones e incluso los menús. Sin
embargo, el dato e.arg para los eventos que no son Action de la lista es el
índice del ítem seleccionado.
. . .
int countItems()
String getItem(int)
int getSelectedIndex()
int[] getSelectedIndexes()
String getSelectedItem()
Igual getSelectedIndex(), pero devuelve el string con el texto de la opción
en lugar de su índice. Develve null si no se ha seleccionado ninguna opción o
si se ha seleccionado más de una.
String[] getSelectedItems()
boolean isSelected(int)
int getRows()
Ozito
La razón por la que el applet trae una ventana para demostrar los menús es
que el AWT límita donde se pueden utilizar los menús. Los menús sólo
pueden existir en las barras de menú, y las barras de menú sólo están
disponibles en las ventanas (especificamente, en los Frames).
MenuItem
CheckboxMenuItem
Menu
Cada menú está representado por un objeto Menu. Esta clase está
implementada como una subclase de MenuItem para se puedan crear
submenús fácilmente añadiendo un menú a otro.
MenuBar
Las barras de menú están implementadas por la clase MenuBar. Esta clase
representa una noción dependiente de la plataforma de un grupo de manús
adheridos a una ventana. Las barras de menú no no pueden utilizarse con la
clase Panel.
Ozito
Cómo Utilizar la Clase Panel
La clase Panel es una subclase de Container para propósitos generales. Se puede
utilizar tal y como es para contener componentes, o se puede definir una subclase
para realizar alguna función específica, como el manejo de eventos para los objetos
contenidos en el Panel.
g.setColor(bg);
g.draw3DRect(0, 0, d.width - 1, d.height - 1, true);
g.draw3DRect(3, 3, d.width - 7, d.height - 7, false);
}
}
Muchos de los ejemplos de esta lección utilizan una subclase de Applet para
manejar los eventos de los componentes que cotienen. Por ejemplo, puedes
verlos en la página Cómo utilizar la Clase Dialog. Puede utilizar el manejo de
eventos de estos y otros ejemplos como modelo para el manejo de los
eventos de sus propias subclases de Applet o Panel.
Ozito
int orientation
int value
int minimum
int maximum
Aquí tienes el código del applet anterior. Este código define dos clases. La
primera es una sencilla subclase de Canvas (ScrollableCanvas) que dibuja
una imagen. La segunda es una subclase de Panel (ImageScroller, que
realmente desciende de Applet) que crea y conteniene un ScrollableCanvas y
dos Scrollbars. Este programa ilustra unos pocos detalles importantes sobre
el manejo de un área despalzable:
g.translate(-tx, -ty);
Ozito
Cómo Utilizar las Clases TextArea y TextField
Las clases TextArea y TextField muestran texto seleccionable y, opcionalmente,
permite al usuario editar ese texto. Se pueden crear subclases de TextArea y
TextField para realizar varias tareas cómo comprobar los errores de la entrada.
Como con cualquier componente, puede especificar los colores de fondo y de primer
plano y la fuente utilizada en los campos y área de texto. Sin embargo no se puede
cambiar su apariencia básica.
Aquí tienes el programa. Aquí tienes el código que crea, inicializa, y maneja
eventos del Campo y el Área de texto:
int getColumns()
setEchoChar()
Reemplaza el texto desde la posición inicial indicada (el primer entero) hasta
la posición final indicada.
Ozito