Sunteți pe pagina 1din 269

RECOPILADO POR: ING.

HERBERT ORLANDO MONGE

ACLARATORIO
Este manual ha sido recopilado con el unico objetivo de ser utilizado, en la materia programacin III, Universidad de el Salvador, Facultad Multidisciplinaria Paracentral.

Se han utilizado como apuntes bases los siguientes manuales: 1. El lenguaje de programacin java TM. Java Sun. 2. Java2, Abraham Otero. 2003 3. Fundamentos de Java. Mc GrawHill. En este manual no estan contemplados todos los metodos y clases de Java, se recomienda consultar la documentacin de java Sdk en www.sun.com.

INDICE 1.0 INTRODUCCIN. ............................................. 9


1.1 Historia del lenguaje. ................................................................................................ 9 1.2 Qu es Java? ........................................................................................................... 9 1.3 Qu lo hace distinto de los dems lenguajes? .......................................................... 9 1.3.1 Qu se puede programar con Java?................................................................. 10 1.3.2 Es fcil de aprender? ...................................................................................... 10 1.3.3 Caractersticas del lenguaje. ............................................................................. 10 1.4 La Mquina Virtual Java (JVM). ............................................................................ 12 1.5 El entorno de desarrollo JDK. ................................................................................. 13 1.5.1 Dnde conseguirlo? ....................................................................................... 13 1.6 CARACTERSTICAS DE JAVA ........................................................................... 13 1.6.1 Simple ............................................................................................................. 13 1.6.2 Orientado a Objetos ......................................................................................... 14 1.6.3 Distribuido ...................................................................................................... 14 1.6.4 Robusto ........................................................................................................... 14 1.6.5 Seguro ............................................................................................................. 14 1.6.6 Portable ........................................................................................................... 14 1.6.7 Arquitectura Neutral ........................................................................................ 14 1.6.8 Rendimiento medio.......................................................................................... 14

2.0 CONCEPTOS DE P.O.O ................................. 15


2.1 Qu es p.O.O? ...................................................................................................... 15 2.2 Qu es un objeto? ................................................................................................. 15 2.2.1 Caractersticas de los Objetos .......................................................................... 15 2.2.2 Definicin de objetos. ...................................................................................... 15 2.3 CLASES ................................................................................................................ 16 2.4 Mensaje .................................................................................................................. 16 2.5 Herencia ................................................................................................................. 16 2.6 Polimorfismo.......................................................................................................... 17 2.7 Encapsulacion ........................................................................................................ 18

3.0 VISIN GENERAL Y ELEMENTOS BSICOS ................................................................. 18 DEL LENGUAJE. .................................................. 18


3.1 Mi primer programa. .............................................................................................. 19 3.2 Comentarios. .......................................................................................................... 20 3.3 Variables y Tipos de Datos ..................................................................................... 21 3.3.1 Tipos de Variables ........................................................................................... 21 3.3.2 Nombres de Variables ...................................................................................... 22 3.4 Operadores de Java................................................................................................. 23 3.4.1 Operadores Aritmticos ................................................................................... 23 3.4.2 Operadores Relacionales y Condicionales ........................................................ 25
3

3.4.3 Operadores de Desplazamiento ........................................................................ 25 3.4.4 Operadores de Asignacin ............................................................................... 27 3.4.5 Expresiones ..................................................................................................... 27 3.5 CADENAS DE CARACTERES ............................................................................ 29 3.5.1 Concatenacin ................................................................................................. 29 3.5.2 Subcadenas ...................................................................................................... 29 3.5.3 Comparacin de cadenas.................................................................................. 30

4.0 CONTROL DE FLUJO EN JAVA ................ 31


4.1 SENTENCIAS CONDICIONALES ....................................................................... 31 4.1.1 If then Else ...................................................................................................... 31 4.2 BUCLES ................................................................................................................ 33 4.2.1 Bucle while ..................................................................................................... 33 4.2.2 Bucle do while ................................................................................................. 34 4.2.3 Bucle for.......................................................................................................... 34 4.2.4 Break y continue .............................................................................................. 35 4.3 RETURN ............................................................................................................... 35

5.0 OBJETOS Y CLASES ..................................... 36


5.1 INTRODUCCIN ................................................................................................. 36 5.2 CLASES Y HERENCIA ........................................................................................ 36 5.2.1 Definicin de una clase .................................................................................... 37 5.2.1.1 Modificadores de clases ................................................................................ 37 5.2.2 Modificadores de mtodos y variables ............................................................. 39 5.2.2.1 Modificadores de variables ........................................................................... 39 5.2.2.2 Modificadores de un mtodo ......................................................................... 39 5.2.3 Herencia .......................................................................................................... 39 5.2.4 Creacin y referencia a objetos ........................................................................ 40 5.2.5 this .................................................................................................................. 41 5.2.6 super ................................................................................................................ 42 5.3 INTERFACES ....................................................................................................... 42 5.5 Clases Abstractas: .................................................................................................. 44 5.6.1 Tamao de un arreglo .......................................................................................... 47 5.7 La clase String ........................................................................................................ 47 5.8 Argumentos de la linea de ordenes. ........................................................................ 48

6.0 Applets ............................................................... 49


6.1 Introduccin ........................................................................................................... 49 6.2 Ciclo de vida .......................................................................................................... 49 6.3 Argumentos............................................................................................................ 51 6.4 Grficas .................................................................................................................. 53 6.5 Tipo de letra ........................................................................................................... 55 6.6 Color ...................................................................................................................... 56 6.7 Imgenes ................................................................................................................ 56

7.0 Abstract Window Toolkit y Eventos .............. 58


7.1 Etiquetas............................................................................................................ 58 7.2 Gestion de Botones ............................................................................................. 58
4

7.3 Checkboxes ........................................................................................................ 60 7.5 TextField ........................................................................................................... 63 7.7 TextArea ............................................................................................................ 66 7.8 Componentes avanzados ..................................................................................... 68 7.8.1 Paneles ............................................................................................................ 68 7.8.2 Canvas (Lienzos) ............................................................................................. 70 7.8.3 reas de texto .................................................................................................. 72 7.8.4 Listas ............................................................................................................... 75 7.8.5 Frames ............................................................................................................. 78 7.8.6 Dilogos .......................................................................................................... 80

8.0 Gestores de diseo ............................................ 83


8.1 Gestores de diseo .................................................................................................. 83 8.1.1 FlowLayout ..................................................................................................... 85 8.1.2 BorderLayout .................................................................................................. 86 8.1.3 CardLayout...................................................................................................... 88 8.1.4 GridLayout ...................................................................................................... 89 8.1.5 GridBagLayout ................................................................................................ 90

9.0 EVENTOS ......................................................... 98


Qu es un evento? ...................................................................................................... 98 El modelo de delegacin de eventos ............................................................................. 98 Gestin de eventos en Java. .......................................................................................... 99

10.0 EXCEPCIONES ........................................... 112


10.1- QU SON LAS EXCEPCIONES? .................................................................. 112 10.2.- DETECCIN DE ERRORES .......................................................................... 113 10.3.- MANEJO DE EXCEPCIONES ........................................................................ 113 10.4.- FINALLY ........................................................................................................ 115 10.5.- LANZAMIENTO DE EXCEPCIONES ........................................................... 117 10.6.- JERARQUA DE EXCEPCIONES .................................................................. 117 10.7.- CREACIN DE EXCEPCIONES .................................................................... 119 10.8.- OBTENCIN DE INFORMACIN DE UNA EXCEPCIN .......................... 120 10.9.- RESUMEN EXCEPCIONES EXPLCITAS-IMPLCITAS ............................. 122 10.10.- EXCEPCIONES Y HERENCIA .................................................................... 122 10.11.- NORMAS EN EL USO DE EXCEPCIONES ................................................. 123 10.12.- EJEMPLOS.................................................................................................... 124

11.0 THREADS. .................................................... 127


11.1 Qu son los threads? ......................................................................................... 127 11.1.1 Un ejemplo, mejor que mil palabras. ............................................................ 127 11.1.2 Otro ejemplo ................................................................................................ 128 11.1.3 Y otro ejemplo ms. ..................................................................................... 129 11.2 Estado de un thread. ........................................................................................... 130 11.3 Creacin de threads. ........................................................................................... 131 11.4.1 currentThread(). ........................................................................................... 132 11.4.2 isAlive(). ..................................................................................................... 132 11.4.3 sleep(). ......................................................................................................... 133
5

11.4.4 suspend(). .................................................................................................... 133 11.4.5 wait(). .......................................................................................................... 134 11.4.6 yield(). ......................................................................................................... 134 11.4.7 join()............................................................................................................ 135 11.5 Threads y sincronismo. ....................................................................................... 136 11.5.1 El paradigma productor/consumidor ............................................................ 136 11.5.2 Seccin crtica. ............................................................................................ 136 11.5.3 Monitores. ................................................................................................... 139 11.6 Prioridad de un thread......................................................................................... 144 11.7 Threads Daemon. ............................................................................................... 145 11.8 Grupos de threads. .............................................................................................. 146 11.8.1 Creacin de grupos de threads...................................................................... 146

12.0 Swing .............................................................. 150


JFC y Swing ............................................................................................................... 150 Componentes Swing frente a componentes AWT ....................................................... 151 Contenedores de alto nivel ......................................................................................... 153 JFrame ................................................................................................................... 157 JDialog, JOptionPane ............................................................................................. 159 JApplet ................................................................................................................... 167 Contenedores intermedios .......................................................................................... 167 JPanel..................................................................................................................... 168 JTabbedPane .......................................................................................................... 171 JToolBar ................................................................................................................ 175 JLayeredPane ......................................................................................................... 176 Interfaces de usuario en Java: ..................................................................................... 180 componentes atmicos de Swing ................................................................................ 180 Componentes atmicos ........................................................................................... 180 Componentes para obtener informacin ..................................................................... 181 JButton................................................................................................................... 181 JCheckbox.............................................................................................................. 183 JRadioButton ......................................................................................................... 185 JComboBox ........................................................................................................... 187 JMenu .................................................................................................................... 188 JSlider .................................................................................................................... 193 Componentes para mostrar informacin ..................................................................... 196 JLabel .................................................................................................................... 196 JToolTip................................................................................................................. 198 JProgressBar .......................................................................................................... 198 Componentes que muestran informacin estructurada ................................................ 200 JColorChooser........................................................................................................ 200 JFileChooser .......................................................................................................... 205

Interfaces de usuario en Java: otras ................... 208 caractersticas de Swing ....................................... 208
Introduccin ............................................................................................................... 208 El gestor de diseo BoxLayout ................................................................................... 209
6

Estableciendo el Look & Feel..................................................................................... 213

Ejemplos de Swing ................................................ 219 1. INTRODUCCION JDBC ................................ 227


1.1 Qu es JDBC?............................................................................................... 227 1.1.1 Qu hace JDBC? .......................................................................................... 228 1.1.2 JDBC es un API de bajo nivel y una base para APIs de alto nivel. ............... 228 1.1.3 JDBC frente a ODBC y otros APIs .............................................................. 229 1.1.5 SQL Conforme ............................................................................................. 231 1.2 Productos JDBC. ................................................................................................. 232 1.2.1 JavaSoft Framework ..................................................................................... 233 1.2.2 Tipos de drivers JDBC .................................................................................. 234

2. CONEXIN ..................................................... 235


2.1 Vista Preliminar .................................................................................................. 235 2.1.1 Apertura de una conexin ............................................................................. 235 2.1.2 Uso general de URLs................................................................................... 235 2.1.3 JDBC y URLs ............................................................................................. 236 2.1.4 El subprotocolo odbc ................................................................................. 238 2.1.5 Registro de subprotocolos ............................................................................. 238 2.1.6 Envo de Sentencias SQL .............................................................................. 238 2.1.7 Transacciones ............................................................................................... 239 2.1.8 Niveles de aislamiento de transacciones ........................................................ 240

3. LA CLASE DriverManager ........................... 241


3.1 Vista preliminar ................................................................................................... 241 3.1.1 Mantenimiento de la lista de drivers disponibles. .......................................... 241 3.1.2 Establecer una conexin ............................................................................... 243

4. LA CLASE Statement ..................................... 243


4.1 Vista Preliminar .................................................................................................. 243 4.1.1 Creacin de objetos Statement ...................................................................... 244 4.1.2 Ejecucin de sentencias usando objetos Statement. ....................................... 244 4.1.3 Realizacin de Statement .............................................................................. 245 4.1.4 Cerrar objetos Statement. .............................................................................. 245

5.0 CONEXIONES A BASES DE DATOS CON JAVA ...................................................................... 246

El lenguaje de Programacin Java


1.0 INTRODUCCIN.
1.1 Historia del lenguaje.
El lenguaje Java fue creado por Sun Microsystems Inc. en un proceso por etapas que arranca en 1990, ao en el que Sun cre un grupo de trabajo, liderado por James Gosling, para desarrollar un sistema para controlar electrodomsticos e incluso PDAs o Asistentes Personales (pequeos ordenadores) que, adems, permitiera la conexin a redes de ordenadores. Se pretenda crear un hardware polivalente, con un Sistema Operativo eficiente (SunOS) y un lenguaje de desarrollo denominado Oak (roble), el precursor de Java. El proyecto finaliz en 1992 y result un completo fracaso debido al excesivo coste del producto, con relacin a alternativas similares, tras lo cual el grupo se disolvi. Por entonces aparece Mosaic y la World Wide Web. Despus de la disolucin del grupo de trabajo, nicamente quedaba del proyecto el lenguaje Oak. Gracias a una acertada decisin de distribuir libremente el lenguaje por la Red de Redes y al auge y la facilidad de acceso a Internet, propiciado por la WWW, el lenguaje se populariz y se consigui que una gran cantidad de programadores lo depurasen y terminasen de perfilar la forma y usos del mismo. A partir de este momento, el lenguaje se difunde a una velocidad vertiginosa, aadindosele numerosas clases y funcionalidad para TCP/IP. El nombre del lenguaje tuvo que ser cambiado ya que exista otro llamado Oak. El nombre Java surgi en una de las sesiones de brainstorming celebradas por el equipo de desarrollo del lenguaje. Buscaban un nombre que evocara la esencia de la tecnologa (viveza, animacin, rapidez, interactividad ). Java fue elegido de entre muchsimas propuestas. No es un acrnimo, sino nicamente algo humeante, caliente y que a muchos programadores les gusta beber en grandes cantidades: una taza de caf (Java en argot Ingls americano2). De esta forma, Sun lanz las primeras versiones de Java a principios de 1995. Desde entonces, Sun ha sabido manejar inteligentemente el xito obtenido por su lenguaje, concedindose licencias a cualquiera sin ningn problema, fomentando su uso entre la comunidad informtica y extendiendo las especificaciones y funcionalidad del lenguaje.

1.2 Qu es Java?
Java es un lenguaje de desarrollo de propsito general, y como tal es vlido para realizar todo tipo de aplicaciones profesionales. Entonces, es simplemente otro lenguaje ms? Definitivamente no. Incluye una combinacin de caractersticas que lo hacen nico y est siendo adoptado por multitud de fabricantes como herramienta bsica para el desarrollo de aplicaciones comerciales de gran repercusin.

1.3 Qu lo hace distinto de los dems lenguajes?


Una de las caractersticas ms importantes es que los programas ejecutables, creados por el compilador de Java, son independientes de la arquitectura. Se ejecutan indistintamente en una gran variedad de equipos con diferentes microprocesadores y sistemas operativos. De momento, es pblico. Puede conseguirse un JDK (Java Developer's Kit) o Kit de desarrollo de aplicaciones Java gratis. No se sabe si en un futuro seguir sindolo. Permite escribir Applets (pequeos programas que se insertan en una pgina HTML) y se ejecutan en el ordenador local.
9

Se pueden escribir aplicaciones para intraredes, aplicaciones cliente/servidor, aplicaciones distribuidas en redes locales y en Internet. Es fcil de aprender y est bien estructurado. Las aplicaciones son fiables. Puede controlarse su seguridad frente al acceso a recursos del sistema y es capaz de gestionar permisos y criptografa. Tambin, segn Sun, la seguridad frente a virus a travs de redes locales e Internet est garantizada. Aunque al igual que ha ocurrido con otras tecnologas y aplicaciones, se han descubierto, y posteriormente subsanado, agujeros en la seguridad3 de Java.

1.3.1 Qu se puede programar con Java?


Si tena preconcebida la idea de que con Java slo se programan applets para pginas web, est completamente equivocado. Ya que Java es un lenguaje de propsito general, puede programarse en l cualquier cosa: Aplicaciones independientes. Como con cualquier otro lenguaje de propsito general. Applets. Pequeas aplicaciones que se ejecutan en un documento HTML, siempre y cuando el navegador soporte Java, como ocurre con los navegadores HotJava y las ltimas versiones de Netscape y el explorador de Internet de Microsoft.

1.3.2 Es fcil de aprender?


Para el colectivo de programadores que conocen la programacin orientada a objetos, el cambio a Java puede ser realmente sencillo. Es un lenguaje bien estructurado, sin punteros y sin necesidad de tener que controlar la asignacin de memoria a estructuras de datos u objetos. Para los programadores en C++ tambin es sencillo el cambio, ya que la sintaxis es prcticamente la misma que en este lenguaje. Para todo aquel que no conozca la programacin orientada a objetos, este lenguaje es ideal para aprender todos sus conceptos, ya que en cada paso de su aprendizaje se va comprobando que las cosas se hacen en la forma natural de hacerlas, sin sorpresas ni comportamientos extraos de los programas. A medida que se va aprendiendo, se va fomentando en el programador, y sin esfuerzo, un buen estilo de programacin orientada a objetos. En realidad, no puede ser de otra forma, ya que Java impide hacer cosas extraas y, adems, no permite abandonar la programacin orientada a objetos, como ocurre con otros lenguajes de programacin. Esto es bastante conveniente, de lo contrario, un programador que est aprendiendo puede sentir la tentacin de volver a lo que conoce (la programacin tradicional). A medida que se van comprobando las ventajas de la programacin orientada a objetos, para aquellos que las desconocen, y la facilidad y naturalidad del lenguaje Java, ste va atrapando a quien se acerca a l, y despus de algn tiempo trabajando con Java, hay pocos programadores que no lo consideren como su favorito.

1.3.3 Caractersticas del lenguaje.


Es intrnsecamente orientado a objetos. Funciona perfectamente en red. Aprovecha caractersticas de la mayora de los lenguajes modernos evitando sus inconvenientes. En particular los del C++. Tiene una gran funcionalidad gracias a sus libreras (clases). NO tiene punteros manejables por el programador, aunque los maneja interna y transparentemente.

10

El manejo de la memoria no es un problema, la gestiona el propio lenguaje y no el programador. Genera aplicaciones con pocos errores posibles. Incorpora Multi-Threading (para permitir la ejecucin de tareas concurrentes dentro de un mismo programa). El lenguaje Java puede considerarse como una evolucin del C++. La sintaxis es parecida a la de este lenguaje, por lo que en este libro se har referencia a dicho lenguaje frecuentemente. A pesar de que puede considerarse como una evolucin del C++ no acarrea los inconvenientes del mismo, ya que Java fue diseado partiendo de cero, es decir, no necesitaba ser compatible con versiones anteriores de ningn lenguaje como ocurre con C++ y C. Gracias a que fue diseado partiendo de cero ha conseguido convertirse en un lenguaje orientado a objetos puro, limpio y prctico. No permite programar mediante otra tcnica que no sea la programacin orientada a objetos (POO en adelante) y, una vez superado el aprendizaje de la programacin orientada a objetos, es realmente sencillo aprender Java. El lenguaje es Compilado o Interpretado? Ni una cosa ni la otra. Aunque estrictamente hablando es interpretado, necesita de un proceso previo de compilacin. Una vez compilado el programa, se crea un fichero que almacena lo que se denomina bytecodes o j_code (pseudocdigo prcticamente al nivel de cdigo mquina). Para ejecutarlo, es necesario un intrprete, la JVM (Java Virtual Machine) mquina virtual Java. De esta forma, es posible compilar el programa en una estacin UNIX y ejecutarlo en otra con Windows95 utilizando la mquina virtual Java para Windows95. Esta JVM se encarga de leer los bytecodes y traducirlos a instrucciones ejecutables directamente en un determinado microprocesador, de una forma bastante eficiente.

Que el programa deba ser interpretado no hace que sea poco eficiente en cuanto a velocidad, ya que la interpretacin se hace prcticamente al nivel de cdigo mquina. Por ejemplo, es
11

mucho ms rpido que cualquier otro programa interpretado como por ejemplo Visual Basic, aunque es ms lento que el mismo programa escrito en C++. Esta deficiencia en cuanto a la velocidad, puede ser aminorada por los compiladores Just-InTime (JIT). Un compilador JIT transforma los bytecodes de un programa o un applet en cdigo nativo de la plataforma donde se ejecuta, por lo que es ms rpido. Suelen ser incorporados por los navegadores, como Netscape o Internet Explorer. El lenguaje Java es robusto. Las aplicaciones creadas en este lenguaje son susceptibles de contener pocos errores, principalmente porque la gestin de memoria y punteros es realizada por el propio lenguaje y no por el programador. Bien es sabido que la mayora de los errores en las aplicaciones vienen producidos por fallos en la gestin de punteros o la asignacin y liberacin de memoria. Adems, el lenguaje contiene estructuras para la deteccin de excepciones (errores de ejecucin previstos) y permite obligar al programador a escribir cdigo fiable mediante la declaracin de excepciones posibles para una determinada clase reutilizable.

1.4 La Mquina Virtual Java (JVM).


La mquina virtual Java es la idea revolucionaria 4 del lenguaje. Es la entidad que proporciona la independencia de plataforma para los programas Java compilados en byte-code.

12

Un mismo programa fuente compilado en distintas plataformas o sistemas operativos, genera el mismo fichero en byte-code. Esto es lgico, ya que se supone que el compilador de Java traduce el fichero fuente a cdigo ejecutable por una mquina que nicamente existe en forma virtual (aunque se trabaja en la construccin de microprocesadores que ejecuten directamente el byte-code). Evidentemente, si un mismo programa en byte-code puede ser ejecutado en distintas plataformas es porque existe un traductor de ese byte-code a cdigo nativo de la mquina sobre la que se ejecuta. Esta tarea es realizada por la JVM. Existe una versin distinta de esta JVM para cada plataforma. Esta JVM se carga en memoria y va traduciendo al vuelo, los byte-codes a cdigo mquina. La JVM no ocupa mucho espacio en memoria, pinsese que fue diseada para poder ejecutarse sobre pequeos electrodomsticos como telfonos, televisores, etc.

1.5 El entorno de desarrollo JDK.


La herramienta bsica para empezar a desarrollar aplicaciones o applets en Java es el JDK (Java Developers Kit) o Kit de Desarrollo Java, que consiste, bsicamente, en un compilador y un intrprete (JVM) para la lnea de comandos. No dispone de un entorno de desarrollo integrado (IDE), pero es suficiente para aprender el lenguaje y desarrollar pequeas aplicaciones.

1.5.1 Dnde conseguirlo?


El Kit de desarrollo puede obtenerse en las direcciones siguientes: http://www.sun.com http://www.javasoft.com El entorno para Windows est formado por un fichero ejecutable que realiza la instalacin, creando toda la estructura de directorios. El kit contiene bsicamente: El compilador: javac.exe El depurador: jdb.exe El intrprete: java.exe y javaw.exe El visualizador de applets: appletviewer.exe El generador de documentacin: javadoc.exe Un desensamblador de clases: javap.exe El generador de archivos fuentes y de cabecera (.c y .h) para clases nativas en C: javah.exe

1.6 CARACTERSTICAS DE JAVA


A continuacin haremos una pequea redaccin de las caractersticas del lenguaje, que nos ayudarn a ver para que tipo de problemas est pensado Java:

1.6.1 Simple
Es un lenguaje sencillo de aprender. Su sintaxis es la de C++ simplificada. Los creadores de Java partieron de la sintaxis de C++ y trataron de eliminar de este todo lo que resultase complicado o fuente de errores en este lenguaje.

13

1.6.2 Orientado a Objetos


Posiblemente sea el lenguaje ms orientado a objetos de todos los existentes; en Java todo, a excepcin de los tipos fundamentales de variables (int, char, long...) es un objeto.

1.6.3 Distribuido
Java est muy orientado al trabajo en red, soportando protocolos como TCP/IP, UDP, HTTP y FTP. Por otro lado el uso de estos protocolos es bastante sencillo comparandolo con otros lenguajes que los soportan.

1.6.4 Robusto
El compilador Java detecta muchos errores que otros compiladores solo detectaran en tiempo de ejecucin o incluso nunca. (ej: if(a=b) then ... el compilador Java no nos dejara compilar este cdigo.

1.6.5 Seguro
Sobre todo un tipo de desarrollo: los Applet. Estos son programas diseados para ser ejecutados en una pgina web. Java garantiza que ningn Applet puede escribir o leer de nuestro disco o mandar informacin del usuario que accede a la pgina a travs de la red (como, por ejemplo, la direccin de correo electrnico). En general no permite realizar cualquier accin que pudiera daar la mquina o violar la intimidad del que visita la pgina web.

1.6.6 Portable
En Java no hay aspectos dependientes de la implementacin, todas las implementaciones de Java siguen los mismos estndares en cuanto a tamao y almacenamiento de los datos. Esto no ocurre as en C++, por ejemplo. En ste un entero, por ejemplo, puede tener un tamao de 16, 32 o ms bits, siendo lo nica limitacin que el entero sea mayor que un short y menor que un long int. As mismo C++ bajo UNIX almacena los datos en formato little endian, mientas que bajo Windows lo hace en big endian. Java lo hace siempre en little edian para evitar confusiones.

1.6.7 Arquitectura Neutral


El cdigo generado por el compilador Java es independiente de la arquitectura: podra ejecutarse en un entorno UNIX, Mac o Windows. El motivo de esto es que el que realmente ejecuta el cdigo generado por el compilador no es el procesador del ordenador directamente, sino que este se ejecuta mediante una mquina virtual. Esto permite que los Applets de una web pueda ejecutarlos cualquier mquina que se conecte a ella independientemente de que sistema operativo emplee (siempre y cuando el ordenador en cuestin tenga instalada una mquina virtual de Java).

1.6.8 Rendimiento medio


Actualmente la velocidad de procesado del cdigo Java es semejante a la de C++, hay ciertos pruebas estndares de comparacin (benchmarks) en las que Java gana a C++ y viceversa. Esto es as gracias al uso de compiladores just in time, compiladores que traduce los bytecodes de Java en cdigo para una determinada CPU, que no precisa de la mquina virtual para ser ejecutado, y guardan el resultado de dicha conversin, volvindolo a llamar en caso de volverlo a necesitar, con lo que se evita la sobrecarga de trabajo asociada a la interpretacin del bytecode. No obstante por norma general el programa Java consume bastante ms memoria que el programa C++, ya que no slo ha de cargar en memoria los recursos necesario para la ejecucin del programa, sino que adems debe simular un sistema operativo y hardware virtuales (la mquina virtual). Por otro lado la programacin grfica empleando las libreras Swing es ms lenta que el uso de componentes nativos en las interfaces de usuario. En general en Java se ha sacrificado el rendimiento para facilitar la programacin y sobre todo para conseguir la caracterstica de neutralidad arquitectural, si bien es cierto que los avances en las mquinas virtuales remedian cada vez ms estas decisiones de diseo.
14

1.6.9 Multithread
Soporta de modo nativo los threads, sin necesidad del uso de de libreras especficas (como es el caso de C++). Esto le permite adems que cada Thread de una aplicacin java pueda correr en una CPU distinta, si la aplicacin se ejecuta en una mquina que posee varias CPU. Las aplicaciones de C++ no son capaces de distribuir, de modo transparente para el programador, la carga entre varias CPU.

2.0 CONCEPTOS DE P.O.O


2.1 Qu es p.O.O?
Tcnica de programacin que utiliza objetos como bloque esencial de construccin. Los objetos son como los tipos abstractos de datos. Un tipo abstracto de datos es un tipo de dato definido por el programador junto con un conjunto de operaciones que se pueden realizar sobre ellos. Ejemplo: listas, colas, etc.

2.2 Qu es un objeto?
Un objeto es una coleccin de datos, junto con las funciones asociadas, utilizadas para operar sobre esos datos. La potencia real de los objetos reside en las propiedades que soportan: herencia, encapsulacin (o encapsulamiento) y polimorfismo junto con los conceptos de objetos, clases, mtodos y mensajes.

2.2.1 Caractersticas de los Objetos


Se agrupan en tipos denominados clases. Contienen datos internos que definen su estado actual. Soportan ocultacin de datos. Pueden heredar propiedades de otros objetos. Pueden comunicarse con otros objetos enviando o pasando mensajes. Tienen mtodos que definen su comportamiento.

2.2.2 Definicin de objetos.


Un objeto es una unidad que contiene datos y las funciones que operan sobre esos datos. Encapsulacin (encapsulamiento) de datos y ocultacin de datos. Los datos y las funciones se encapsulan en una nica entidad: objeto. Grficamente:

15

2.3 CLASES
Una clase es un tipo definido por el usuario que determina las estructuras de datos y las operaciones asociadas con ese tipo. Son como plantillas o modelos. Cuando se construye un objeto de una clase, se crea una instancia de clase. Instancia de clase = objeto

2.4 Mensaje
Un mensaje es simplemente la peticin de un objeto a otro objeto para que se se comporte de una determinada manera, ejecutando uno de sus mtodos. Paso de mensajes: invocacin o llamada de un mtodo de un objeto.

2.5 Herencia
Es la propiedad que permite a los objetos construirse a partir de otros objetos. Ejemplo:

El principio de este tipo de divisin es que cada subclase comparte caractersticas comunes con la clase de la que se deriva. Adems de las caractersticas compartidas con otros miembros de la clase, cada subclase tiene sus propias caractersticas particulares.

16

La herencia permite definir nuevas clases a partir de clases ya existentes. La herencia impone una relacin jerrquica entre clases en la cual una clase hija hereda de su clase padre. Herencia simple: la clase solo puede recibir caractersticas de otra clase base. Herencia mltiple: la clase recibe propiedades de mas de una clase base.

2.6 Polimorfismo
Polimorfismo: significa la cualidad de tener ms de una forma. Polimorfismo es la capacidad del cdigo de un programa para ser utilizado con diferentes tipos de datos u objetos. Tambin se puede aplicar a la propiedad que poseen algunas operaciones de tener un comportamiento diferente dependiendo del objeto (o tipo de dato) sobre el que se aplican. Ejemplo: Operacin sumar. En un lenguaje de programacin el operador + representa la suma de dos nmeros ( x + y ) de diferentes tipos: enteros, flotantes. O adems, se puede definir la operacin de sumar dos cadenas: concatenacin. De modo similar: suponiendo un nmero de figuras geomtricas que responden todas al mensaje Calcular _ superficie. Cada objeto reacciona a este mensaje haciendo el clculo correspondiente de la superficie, el cual difiere de una figura a otra.

17

El concepto de polimorfismo se puede aplicar tanto a funciones como a tipos de datos. As nacen los conceptos de funciones polimrficas y tipos polimrficos. Las funciones polimrficas son aquellas funciones que pueden evaluarse y/o ser aplicadas a diferentes tipos de datos de forma indistinta. Los tipos polimrficos, por su parte, son aquellos tipos de datos que contienen al menos un elemento cuyo tipo no est especificado.

2.7 Encapsulacion
El encapsulamiento consiste en la propiedad que tienen los objetos de ocultar sus atributos, e incluso los mtodos, a otras partes del programa u otros objetos. La forma natural de construir una clase es la de definir una serie de atributos que, en general, no son accesibles fuera del mismo objeto, sino que nicamente pueden modificarse a travs de los mtodos que son definidos como accesibles desde el exterior de esa clase.

3.0 VISIN GENERAL Y ELEMENTOS BSICOS DEL LENGUAJE.


En Java, prcticamente todo son clases (objetos). El lenguaje obliga a la programacin orientada a objetos y no permite la posibilidad de programar mediante ninguna otra tcnica que no sea sta. Por esta razn, un programa estar formado por uno o varios ficheros fuente y en cada uno de ellos habr definida una o varias clases. En un fichero fuente puede declararse una o ms clases y tendr un aspecto similar a este:
class Clase1 { } class Clase2 { } class ClaseN { }

Una clase est formada por una parte correspondiente a la declaracin, y otra correspondiente al cuerpo de la misma:
Declaracin de clase { Cuerpo de clase }

En la plantilla anterior se ha simplificado el aspecto de la Declaracin de clase, pero s que puede asegurarse que la misma contendr, como mnimo, la palabra reservada class y el nombre que recibe la clase. El cuerpo de las clases comienza con una llave abierta({) y termina con una llave cerrada (}). Dentro del cuerpo de la clase se declaran los atributos y los mtodos de la clase. Para que un programa se pueda ejecutar, debe contener una clase que tenga un mtodo main con la siguiente declaracin: public static void main( String args [] )

18

3.1 Mi primer programa.


En primer lugar, vamos a ver qu estructura debera tener el tpico programa con el que se empieza en cualquier lenguaje de programacin y que proporciona una primera visin de la sintaxis del lenguaje y que comienza a transmitir una serie de sentimientos de aceptacin o de rechazo en el programador. El programa simplemente mostrar en pantalla el mensaje hola, mundo y terminar. Este ejemplo habr que crearlo mediante un editor cualquiera y guardar el fichero con el nombre Hola.java (ATENCIN: Es importante que tenga el mismo nombre que la clase Hola, distinguiendo entre maysculas y minsculas).
class Hola { public static void main(String args[]) { System.out.println("Hola, este es mi primer programa"); } }

El lenguaje Java, al igual que C, distingue entre maysculas y minsculas, por lo que es importante transcribirlo literalmente. Tambin hay que comentar que en el nombre del fichero fuente tambin se hace distincin entre maysculas y minsculas a la hora de compilarlo, aunque el sistema operativo empleado no haga esta distincin. La extensin del mismo debe ser .java Para compilar el programa, hay que teclear: javac Hola.java El compilador crear un fichero que se llama Hola.class que contiene el cdigo bytecode (pseudoejecutable) Para ejecutarlo: java Hola Se escribe nicamente el nombre de la clase Hola y no Hola.class u Hola.java El resultado de la ejecucin ser la visualizacin en pantalla del mensaje:
Hola, este es mi primer programa

Explicacin del programa Hola.java lnea a lnea: Este simple programa, a pesar de no ser muy extenso, contiene muchos de los conceptos de la programacin orientada a objetos en Java. No se pretende que, a partir del mismo, se aprendan y comprendan la totalidad de los aspectos de la POO y la sintaxis del Java, pero s que puede afirmarse que una vez comprendida y asimilada su filosofa, se estar en un punto bastante cercano a los conocimientos bsicos necesarios para entenderlos. La primera lnea del programa declara una clase llamada Hola, que es descendiente, al no indicar nosotros que herede de otra clase, de la clase Object. Entre las llaves de la clase Hola, se declaran los atributos y los mtodos de la clase. Todo lo que se encuentre entre la llave abierta ( { ) y la llave cerrada ( } ) pertenece a la clase Hola. En nuestro ejemplo, nicamente tiene un mtodo: main. Las llaves no sirven nicamente para marcar el inicio y el fin de una clase. Se utilizan para marcar principio y final de bloques de cdigo y se interpretan mediante el mtodo LIFO (Last In First Out) donde el ltimo en entrar es el primero en salir. En el ejemplo existen, adems de las llaves abierta y cerrada de la clase, otras llaves abierta y cerrada dentro del mtodo main(). Que se interpreten mediante el mtodo LIFO significa que la llave cerrada ( } ) del mtodo main() parentiza el bloque abierto por la ltima llave ( { ) antes de sta.
19

Todo programa independiente escrito en Java empieza a ejecutarse(como en C) a partir del mtodo main(). Se pueden compilar clases que no posean mtodo main(), pero el intrprete Java no podr ejecutarlas inicialmente, aunque s pueden ejecutarse si son llamadas desde otro mtodo en ejecucin. Veremos en posteriores captulos que la forma de crear los applets es distinta y no necesita declarar este mtodo. Declaracin del mtodo main(): public: indica que el mtodo main() es pblico y, por tanto, puede ser llamado desde otras clases. Todo mtodo main() debe ser pblico para poder ejecutarse desde el intrprete Java (JVM). static: indica que la clase no necesita ser instanciada para poder utilizar el mtodo al que califica. (No se crea ninguna instancia u objeto de la clase Hola). Tambin indica que el mtodo es el mismo para todas las instancias que pudieran crearse. void: indica que la funcin main no devuelve ningn valor. El mtodo main debe aceptar siempre, como parmetro, un vector de strings, que contendr los posibles argumentos que se le pasen al programa en la lnea de comandos, aunque como es nuestro caso, no se utilice. Si no se han comprendido muy bien todos estos conceptos no importa, de momento. Hay una regla sencilla que nunca falla: El mtodo main() siempre se declara de la misma forma:
public static void main(String args[])

La instruccin que realmente realiza el trabajo efectivo de nuestro programa es la siguiente:


System.out.println("hola, este es mi primer programa");

Este lnea indica que se va a ejecutar el mtodo println(), encargado de mostrar un valor a travs de la salida estndar (en nuestro caso, un String) y despus realizar un retorno de carro y nueva lnea. Este mtodo pertenece al atributo out. Este atributo se encuentra incluido dentro de la clase System. Como curiosidad (de momento), cabe mencionar que esta clase es static (no hemos declarado ningn objeto de la clase System). Un poco complicado para simplemente mostrar un mensaje en pantalla? Es posible que s lo sea para aquellos que an no conocen la programacin orientada a objetos, pero una vez conocidos y asimilados los conceptos de la POO (Programacin Orientada a Objetos) el programa es bastante sencillo y lgico. Para aquellos que conozcan C++ pensarn que es bastante parecido a lo que habran escrito en su lenguaje favorito. Este colectivo comprobar que es mucho ms fcil que C y tiene algunas ventajas.

3.2 Comentarios.
Los comentarios en los programas fuente son muy importantes en cualquier lenguaje. Sirven para aumentar la facilidad de comprensin del cdigo y para recordar ciertas cosas sobre el mismo. Son porciones del programa fuente que no sern compiladas, y, por tanto, no ocuparn espacio en el fichero ejecutable. nicamente sirven para documentar. Si un comentario debe ocupar ms de una lnea, hay que anteponerle /* y al final */. Ejemplo:
/* Esto es un comentario que
20

ocupa tres lneas */

Si el comentario que se desea escribir es de una sola lnea, basta con poner dos barras inclinadas: // Ejemplo:
for (i=0; i<20;i++) // comentario de bucle { System.out.println(Adis); }

No puede ponerse cdigo despus de un comentario introducido por // en la misma lnea, ya que desde la aparicin de las dos barras inclinadas // hasta el final de la lnea es considerado como comentario e ignorado por el compilador. Existe otro tipo de comentario que sirve para generar documentacin automticamente en formato HTML mediante la herramienta javadoc .

3.3 Variables y Tipos de Datos


Las variables son las partes importantes de un lenguaje de programacin: ellas son las entidades (valores, datos) que actan y sobre las que se acta. Una declaracin de variable siempre contiene dos componentes, el tipo de la variable y su nombre: tipoVariable nombre;

3.3.1 Tipos de Variables


Todas las variables en el lenguaje Java deben tener un tipo de dato. El tipo de la variable determina los valores que la variable puede contener y las operaciones que se pueden realizar con ella. Existen dos categorias de datos principales en el lenguaje Java: los tipos primitivos y los tipos referenciados. Los tipos primitivos contienen un slo valor e incluyen los tipos como los enteros, coma flotante, los caracteres, etc... La tabla siguiente muestra todos los tipos primitivos soportados por el lenguaje Java, su formato, su tamao y una breve descripcin de cada uno:

Los tipos referenciados se llaman as porque el valor de una variable de referencia es una referencia (un puntero) hacia el valor real. En Java tenemos los arrays, las clases y los interfaces como tipos de datos referenciados.
21

3.3.2 Nombres de Variables


Un programa se refiere al valor de una variable por su nombre. Por convencin, en Java, los nombres de las variables empiezan con una letra minscula (los nombres de las clases empiezan con una letra mayscula). Un nombre de variable Java: 1. Debe ser un identificador legal de Java comprendido en una serie de caracteres Unicode. Unicode es un sistema de codificacin que soporta texto escrito en distintos lenguajes humanos.Unicode perminte la codificacin de 34.168 caracteres. Esto le permite utilizar en sus programas Java varios alfabetos como el Japons, el Griego, el Ruso o el Hebreo. Esto es importante para que los programadores pueden escribir cdigo en su lenguaje nativo. 2. No puede ser el mismo que una palabra clave o el nombre de un valor booleano (true or false) 3. no deben tener el mismo nombre que otras variables cuyas declaraciones aparezcan en el mismo mbito. La regla nmero 3 implica que podra existir el mismo nombre en otra variable que aparezca en un mbito diferente. Por convencin, los nombres de variables empiezan por un letra minscula. Si una variable est compuesta de ms de una palabra, como 'nombreDato' las palabras se ponen juntas y cada palabra despus de la primera empieza con una letra mayscula.

3.3 CONVERSIN ENTRE TIPOS NUMRICOS


Las normas de conversin entre tipos numricos son las habituales en un lenguaje de programacin: si en una operacin se involucran varios datos numricos de distintos tipos todos ellos se convierten al tipo de dato que permite una mayor precisin y rango de representacin numrica; as, por ejemplo: -Si cualquier operando es double todos se convertirn en double. -Si cualquier operando es float y no hay ningn double todos se convertirn a float. -Si cualquier operando es long y no hay datos reales todos se convertirn en long. Del mismo modo estas normas se extendern para int, short y byte. La jerarqua en las conversiones de mayor a menor es: double <- float <- long <- int <- short <byte. Estas conversiones slo nos preocuparn a la hora de mirar en que tipo de variable guardamos el resultado de la operacin; esta ha de ser, al menos, de una jerarqua mayor o igual a la jerarqua de la mxima variable involucrada en la operacin. Si es de rango superior no habr problemas. Hay ciertas excepciones a la norma aqu descrita: Java solo tiene dos tipos de operadores enteros: uno que aplica para operar datos de tipo long, y otro que emplea para operar datos de tipo int. De este modo cuando operemos un byte con un byte, un short con un short o un short con un byte Java emplear para dicha operacin el operador de los datos tipo int, por lo que el resultado de dicha operacin ser un int siempre. Es posible convertir un dato de jerarqua superior a uno con jerarqua inferior, arriesgndonos a perder informacin en el cambio. Este tipo de operacin (almacenar el contenido de una variable de jerarqua superior en una de jerarqua inferior) se denomina cast.

Veamos una ejemplo para comprender su sintaxis: class Cast { public static void main(String[] args) { int i = 9,k;
22

float j = 47.9F; System.out.println("i: "+i + " j: " +j); k = (int)j; //empleo de un cast System.out.println("j: " + j + " k: " +k); j = k;//no necesita cast System.out.println("j: " + j + " k: " +k); float m = 2.3F; //float m = 2.3; dara error al compilar. System.out.println("m: "+m); } }

3.4 Operadores de Java


Los operadores realizan algunas funciones en uno o dos operandos. Los operadores que requieren un operador se llaman operadores unarios. Por ejemplo, ++ es un operador unario que incrementa el valor su operando en uno. Los operadores que requieren dos operandos se llaman operadores binarios. El operador = es un operador binario que asigna un valor del operando derecho al operando izquierdo. Los operadores unarios en Java pueden utilizar la notacin de prefijo o de sufijo. La notacin de prefijo significa que el operador aparece antes de su operando: operador operando La notacin de sufijo significa que el operador aparece despus de su operando: operando operador Todos los operadores binarios de Java tienen la misma notacin, es decir aparecen entre los dos operandos: op1 operator op2 Adems de realizar una operacin tambin devuelve un valor. El valor y su tipo dependen del tipo del operador y del tipo de sus operandos. Por ejemplo, los operadores aritmticos (realizan las operaciones de aritmtica bsica como la suma o la resta) devuelven nmeros, el resultado tpico de las operaciones aritmticas. El tipo de datos devuelto por los operadores aritmticos depende del tipo de sus operandos: si sumas dos enteros, obtendrs un entero. Se dice que una operacin evala su resultado. Es muy til dividir los operadores Java en las siguientes categoras: aritmticos, relacionales y condicionales. lgicos y de desplazamiento y de asignacin.

3.4.1 Operadores Aritmticos


El lenguaje Java soporta varios operadores aritmticos - incluyendo + (suma), - (resta), * (multiplicacin), / (divisin), y % (mdulo). En todos los nmeros enteros y de coma flotante. Por ejemplo, puedes utilizar este cdigo Java para sumar dos nmeros: sumaEsto + aEsto O este cdigo para calcular el resto de una divisin: divideEsto % porEsto

23

Esta tabla sumariza todas las operaciones aritmticas binarias en Java:

Operador Uso + op1 + op2 op1 - op2 * op1 * op2 / op1 / op2 % op1 % op2

Descripcin Suma op1 y op2 Resta op2 de op1 Multiplica op1 y op2 Divide op1 por op2 Obtiene el resto de dividir op1 por op2

Nota: El lenguaje Java extiende la definicin del operador + para incluir la concatenacin de cadenas. Los operadores + y - tienen versiones unarias que seleccionan el signo del operando:

Adems, existen dos operadores de atajos aritmticos, ++ que incrementa en uno su operando, y -- que decrementa en uno el valor de su operando.

Ejemplo:
class Incremento{ public static void main(String[] args) { int i = 1; prt("i : " + i); prt("++i : " + ++i); // Pre-incremento, primero //incrementa y luego imprime por consola prt("i++ : " + i++); // Post-incremento, primero imprime //2 por consola y luego incrementa i. prt("i : " + i);//i por lo tanto vale 3 prt("--i : " + --i); // Pre-decremento, primero //decrementa i y luego lo imprime por consola prt("i-- : " + i--); // Post-decremento, primero imprime //i por consola y luego de decrementa. prt("i : " + i);//Ahora i vale 1 } //esto nos ahorrara teclear. Invocando a prt podremos //imprimir por consola la cadena de caracteres que le pasemos static void prt(String s) { System.out.println(s); } }
24

3.4.2 Operadores Relacionales y Condicionales


Los valores relacionales comparan dos valores y determinan la relacin entre ellos. Por ejemplo, != devuelve true si los dos operandos son distintos. Esta tabla sumariza los operadores relacionales de Java:

Frecuentemente los operadores relacionales se utilizan con otro juego de operadores, los operadores condicionales, para construir expresiones de decisin ms complejas. Uno de estos operadores es && que realiza la operacin Y booleana . Por ejemplo puedes utilizar dos operadores relacionales diferentes junto con && para determinar si ambas relaciones son ciertas. La siguiente lnea de cdigo utiliza esta tcnica para determinar si un ndice de un array est entre dos lmites- esto es, para determinar si el ndice es mayor que 0 o menor que NUM_ENTRIES (que se ha definido prviamente como un valor constante):

0 < index && index < NUM_ENTRIES


Observa que en algunas situaciones, el segundo operando de un operador relacional no ser evaluado. Consideremos esta sentencia:

((count > NUM_ENTRIES) && (System.in.read() != -1))


Si count es menor que NUM_ENTRIES, la parte izquierda del operando de && evala a false. El operador && slo devuelve true si los dos operandos son verdaderos. Por eso, en esta situacin se puede determinar el valor de && sin evaluar el operador de la derecha. En un caso como este, Java no evala el operando de la derecha. As no se llamar a System.in.read() y no se leer un carcter de la entrada estndar. Aqu tienes tres operadores condicionales:

El operador & se puede utilizar como un sinnimo de && si ambos operadores son booleanos. Similarmente, | es un sinonimo de || si ambos operandos son booleanos .

3.4.3 Operadores de Desplazamiento


Los operadores de desplazamiento permiten realizar una manipulacin de los bits de los datos. Esta tabla sumariza los operadores lgicos y de desplazamiento disponibles en el lenguaje Java:

25

Los tres operadores de desplazamiento simplemente desplazan los bits del operando de la izquierda el nmero de posiciones indicadas por el operador de la derecha. Los desplazamientos ocurren en la direccin indicada por el propio operador. Por ejemplo: 13 >> 1 Desplaza los bits del entero 13 una posicin a la derecha. La representacin binaria del nmero 13 es 1101. El resultado de la operacin de desplazamiento es 110 o el 6 decimal. Observe que el bit situado ms a la derecha desaparece. Un desplazamiento a la derecha de un bit es equivalente, pero ms eficiente que, dividir el operando de la izquierda por dos. Un desplazamiento a la izquierda es equivalente a multiplicar por dos. Los otros operadores realizan las funciones lgicas para cada uno de los pares de bits de cada operando. La funcin "y" activa el bit resultante si los dos operandos son 1.

Si quieres evaluar los valores 12 "and" 13: 12 & 13 El resultado de esta operacin es 12. Por qu? Bien, la representacin binaria de 12 es 1100 y la de 13 es 1101. La funcin "and" activa los bits resultantes cuando los bits de los dos operandos son 1, de otra forma el resultado es 0. Entonces si colocas en lnea los dos operandos y realizas la funcin "and", puedes ver que los dos bits de mayor peso (los dos bits situados ms a la izquierda de cada nmero) son 1 as el bit resultante de cada uno es 1. Los dos bits de menor peso se evalan a 0 porque al menos uno de los dos operandos es 0:

1101 & 1100 -----1100


El operador | realiza la operacin O inclusiva y el operador ^ realiza la operacin O exclusiva. O inclusiva significa que si uno de los dos operandos es 1 el resultado es 1.

26

O exclusiva significa que si los dos operandos son diferentes el resultado es 1, de otra forma el resultado es 0:

Y finalmente el operador complemento invierte el valor de cada uno de los bites del operando: si el bit del operando es 1 el resultado es 0 y si el bit del operando es 0 el resultado es 1.

3.4.4 Operadores de Asignacin


Puedes utilizar el operador de asignacin =, para asignar un valor a otro. Adems del operador de asignacin bsico, Java proporciona varios operadores de asignacin que permiten realizar operaciones aritmticas, lgicas o de bits y una operacin de asignacin al mismo tiempo. Especficamente, supn que quieres aadir un nmero a una variable y asignar el resultado dentro de la misma variable, como esto: i = i + 2; Puedes ordenar esta sentencia utilizando el operador += as i += 2; Las dos lneas de cdigo anteriores son equivalentes. Esta tabla lista los operadores de asignacin y sus equivalentes:

3.4.5 Expresiones
Las expresiones realizan el trabajo de un programa Java. Entre otras cosas, las expresiones se utilizan para calcular y asignar valores a las variables y para controlar el flujo de un programa Java. El trabajo de una expresin se divide en dos partes: realizar los clculos indicados por los elementos de la expresin y devolver algn valor. Definicin: Una expresin es una serie de variables, operadores y llamadas a mtodos (construida de acuerdo a la sintaxis del lenguaje) que evala a un valor sencillo.

27

El tipo del dato devuelto por una expresin depende de los elementos utilizados en la expresin. La expresin count++ devuelve un entero porque ++ devuelve un valor del mismo tipo que su operando y count es un entero. Otras expresiones devuelven valores booleanos, cadenas, etc... Una expresin de llamada a un mtodo devuelve el valor del mtodo; as el tipo de dato de una expresin de llamada a un mtodo es el mismo tipo de dato que el valor de retorno del mtodo. El mtodo System.in.read() se ha declarado como un entero, por lo tanto, la expresin System.in.read() devuelve un entero. La segunda expresin contenida en la sentencia System.in.read() != -1 utiliza el operador !=. Recuerda que este operador comprueba si los dos operandos son distintos. En esta sentencia los operandos son System.in.read() y -1. System.in.read() es un operando vlido para != porque devuelve un entero. As System.in.read() != -1 compara dos enteros, el valor devuelto por System.in.read() y -1. El valor devuelto por != es true o false dependiendo de la salida de la comparacin. Como has podido ver, Java te permite construir expresiones compuestas y sentencias a partir de varias expresiones pequeas siempre que los tipos de datos requeridos por una parte de la expresin correspondan con los tipos de datos de la otra. Tambin habrs podido concluir del ejemplo anterior, el orden en que se evalan los componentes de una expresin compuesta. Por ejemplo, toma la siguiente expresin compuesta: x * y * z En este ejemplo particular, no importa el orden en que se evale la expresin porque el resultado de la multiplicacin es independiente del orden. La salida es siempre la misma sin importar el orden en que se apliquen las multiplicaciones. Sin embargo, esto no es cierto para todas las expresiones. Por ejemplo, esta expresin obtiene un resultado diferente dependiendo de si se realiza primero la suma o la divisin: x + y / 100 Puedes decirle directamente al compilador de Java cmo quieres que se evale una expresin utilizando los parntesis ( y ). Por ejemplo, para aclarar la sentencia anterior, se podra escribir: (x + y)/ 100. Si no le dices explcitamente al compilador el orden en el que quieres que se realicen las operaciones, l decide basndose en la precedencia asignada a los operadores y otros elementos que se utilizan dentro de una expresin. Los operadores con una precedencia ms alta se evalan primero. Por ejemplo. El operador divisin tiene una precedencia mayor que el operador suma, por eso, en la expresin anterior x + y / 100, el compilador evaluar primero y / 100. As x + y / 100 es equivalente a: x + (y / 100) Para hacer que tu cdigo sea ms fcil de leer y de mantener deberas explicar e indicar con parntesis los operadores que se deben evaluar primero. La tabla siguiente muestra la precedencia asignada a los operadores de Java. Los operadores se han listado por orden de precedencia de mayor a menor. Los operadores con mayor precedencia se evalan antes que los operadores con un precedencia relativamente menor. Lo operadores con la misma precedencia se evalan de izquierda a derecha.

28

3.5 CADENAS DE CARACTERES


En Java no hay un tipo predefinido para cadenas de caracteres, en su lugar hay una clase, String, que es la que soporta las distintas operaciones con cadenas de caracteres. La definicin de un String es: String e ; //no inicializado String e =; //cadena vaca String e = Hola; //inicializacin y asignacin juntas. A continuacin veremos algunas operaciones bsicas soportadas por la clase String:

3.5.1 Concatenacin
La concatenacin en Java es increblemente sencilla: se realiza con el operador +, es decir sumando cadenas de caracteres obtenemos la concatenacin de estas. Lo ilustraremos con un ejemplo: String saludo = hola; String nombre = Pepe; String saluda_pepe = ; saluda_pepe = saludo + nombre;// saluda_pepe toma el valor holaPepe La sencillez de Java en el manejo de cadenas de caracteres llega incluso ms all: si una cadena la intentamos encadenar con otro tipo de variable automticamente se convierte la otra variable a String, de tal modo que es perfectamente correcto: String saludo = hola; int n = 5; saludo = saludo + + n;// saludo toma el valor hola 5

3.5.2 Subcadenas
En la clase String hay un mtodo que permite la extraccin de una subcadena de caracteres de otra. Su sintaxis es: Nombre_String.substring((int)posicin_inicial,(int)posicin_final);

29

Donde posicin_inicial y posicin_final son respectivamente la posicin del primer carcter que se desea extraer y del primer carcter que ya no se desea extraer. String saludo = hola; String subsaludo = ; Subsaludo = saludo.substring(0,2);// subsaludo toma el valor ho Puede extraerse un char de una cadena, para ello se emplea el mtodo charAt(posicin), siendo posicin la posicin del carcter que se desea extraer.

3.5.3 Comparacin de cadenas


Se empleo otro mtodo de String: equals. Su sintaxis es: cadena1.equals(cadena2); Devuelve true si son iguales y false si son distintos. El siguiente ejemplo permitir ilustrar estas operaciones con Strings:

class Cadena { public static void main(String[] args) { String saludo = "Hola"; String saludo2 ="hola"; int n = 5; //Imprime por consola la subcadena formada por los caracteres //comprendidos entre el caractero 0 de saludo y hasta el //carcter 2, sin incluir el ltimo prt(saludo.substring(0,2)); //Concatena saludo con un espacio en blanco y con el valor de //la variable n prt(saludo +" " + n); //Imprime el resultado del test de igualdad entre saludo y //saludo2. Son distintos, en Java se distingue entre //maysculas y minsculas. prt("saludo == saludo2 "+ saludo.equals(saludo2)); } static void prt(String s) { System.out.println(s); } }

3.5.4 Exponenciacin
En Java a diferencia que en otros lenguajes no existe el operador exponenciacin. Tampoco existen los operadores Seno o Coseno. Si se desea realizar una operacin de exponenciacin se deber invocar el mtodo correspondiente de la clase Math de Java.lang. Estos mtodos son estticos, por lo que no es necesario crear un objeto de dicha clase. Su sintaxis general es: Math.metodo(argumentos); En el siguiente cdigo aparecen ejemplos. Si alguna vez deseamos realizar algn otro tipo de operacin y queremos ver si la soporta Java podemos hacerlo consultando la ayuda on-line de la clase Math. Ejemplo:
30

class Exponente { public static void main(String[] args) { int i = 45,j=2; //Imprime por consola la cadena de caracteres Cos i : //concatenado con el resultado de calcular el coseno de i prt("Cos i : " + Math.cos(i)); prt("Sen i : " + Math.sin(i)); prt("j^i : " + Math.pow(j,i)); } //esto nos ahorrara teclear static void prt(String s) { System.out.println(s); } }

4.0 CONTROL DE FLUJO EN JAVA


El modo de ejecucin de un programa en Java en ausencia de elementos de control de flujo essecuencial, es decir una instruccin se ejecuta detrs de otra y slo se ejecuta una vez. Esto nos permite hace programas muy limitados; para evitarlo se introducen estructuras de control de flujo.Las estructuras de control de flujo de Java son la tpicas de cualquier lenguaje de programacin, por lo que supondremos que todos estis familiarizados con ellas y se explicaran con poco detenimiento.

4.1 SENTENCIAS CONDICIONALES


Ejecutan un cdigo u otro en funcin de que se cumplan o no una determinada condicin. Pasemos a ver sus principales tipos.

4.1.1 If then Else


Su modo ms simple de empleo es: If(condicion) { Grupo de sentencias} Condicin es una valor tipo boolean. El grupo de sentencias se ejecuta solo si la condicin toma un valor true. En caso contrario se sigue ejecutando ignorando el Grupo de sentencias. If(condicion) { Grupo de sentencias} else{ Grupo2 de sentencias} Si condicin toma el valor true se ejecuta Grupo de sentencias, en caso contrario se ejecuta Grupo2 de sentencias. En ambos casos se contina ejecutando el resto del cdigo. If(condicion) { Grupo de sentencias} else if (condicion2){ Grupo2 de sentencias} else if (condicion3){ Grupo3 de sentencias} . . . else{ Grupo_n de sentencias}
31

Si condicin toma el valor true se ejecuta Grupo de sentencias, si condicion2 toma el valor true se ejecuta Grupo2 de sentencias... y as sucesivamente hasta acabarse todas las condiciones. Si no se cumple ninguna se ejecuta Grupo_n de sentencias. Este ltimo else es opcional. En ambos casos se contina ejecutando el resto del cdigo. Ilustraremos esto con el siguiente ejemplo:

class Ejemplo1 { // Mtodo que podremos invovar como test(int a, int b) y que // devolver -1 si a < b, +1 si a > b y 0 si a == b. static int test(int val, int val2) { int result = 0; if(val > val2) result = +1; else if(val < val2) result = -1; else result = 0; return result; } public static void main(String[] args) { //Imprimimos por consola el resultado de realizar unos //cuantos test. System.out.println(test(10, 5)); System.out.println(test(5, 10)); System.out.println(test(5, 5)); } }

4.1.2 Switch
Los creadores de Java trataron de hacer de este lenguaje una versin simplificada y mejorada del lenguaje de C++. Su trabajo fue bastante bueno, pero no perfecto. Prueba de ello es esta sentencia: est tan poco flexible como en C++. Expliquemos su sintaxis antes de dar los motivos de esta crtica: switch(selector) { case valor1 : Grupo de sentencias1; break; case valor2 : Grupo de sentencias2; break; case valor3 : Grupo de sentencias3; break; case valor4 : Grupo de sentencias4; break; case valor5 : Grupo de sentencias5; break; // ... default: statement; } Se compara el valor de selector con sentencias_n. Si el valor coincide se ejecuta su respectivo grupo de secuencias. Si no se encuentra ninguna coincidencia se ejecutan las sentencias de default. Si no se pusieran los break una vez que se encontrase un valor que coincida con el selector se ejecutaran todos los grupos de sentencias, includa la del default. Ha llegado el momento de justificar la crtica hecha a los creadores de Java. Este tipo de estructura tiene sus posibilidades muy limitadas, ya que en las condiciones slo se admite la igualdad, no ingn otro tipo de condicin (sera fcil pensar ejemplos dnde, por poner un caso, se le sacara partido a esta sentencia si aceptase desigualdades). Adems para colmo esta comparacin de igualdad slo admite valores tipo char o cualquier tipo de valores enteros menos long. No podemos comparar contra reales, strings....
32

Tambin se le podra criticar el hecho de que una vez cumplidas una condicin se ejecuten todas las sentencias si no hay instrucciones break que lo impidan. Esto es en muchas ocasiones fuente de errores, aunque tambin hay que reconocer que a veces se le puede sacar partido, de hecho en el ejemplo que empleamos para ilustrar esta sentencia aprovechamos esta caracterstica:

class Ejemplo2 { public static void main(String[] args) { //Bucle for. Ejecutar 100 veces el cdigo que tiene dentro. for(int i = 0; i < 100; i++) { //Math.random() es un mtod esttico que genera un nmero real //aleatorio entre 0 y 1. //Math.random()*26 ser un nmero real aleatorio entre 0 y //26. Al sumarle un carcter, a el carcter se transforma a //un enteroy se le suma. a = 97. //Se transforma el nmero aleatorio entre 97y 97 + 26 en el //carcter correspodiente a su parte entera. Ser un carcter //aleatorio, que por la disposicin de los caracteres Unicode //ser un letra del abecedario. char c = (char)(Math.random() * 26 + 'a'); System.out.print(c + ": "); switch(c) { case 'a': case 'e': case 'i': case 'o': case 'u': //Si el carcter es a, e, i, o o u imprimimos //vocal. System.out.println("vocal"); break; default: //Si no era ninguna de las anteriores imprimimos consonante. System.out.println("consonante"); } } } }

4.2 BUCLES
Son instrucciones que nos permiten repetir un bloque de cdigo mientras se cumpla una determinada condicin. Pasemos a ver sus tipos.

4.2.1 Bucle while


Cuando en la ejecucin de un cdigo se llega a un bucle while se comprueba si se verifica su condicin, si se verifica se continua ejecutando el cdigo del bucle hasta que esta deje de verificarse. Su sintaxis es: while(condicin){ Grupo de sentencias} Ilustramos su funcionamiento con un ejemplo:

33

class Ejemplo3 { public static void main(String[] args) { double r = 0; //Mientras que r < 0.99 sigue ejecutando el cuerpo del bucle. //La d significa double. No es necesaria while(r < 0.99d) { //Genera un nuevo r aleatario entre 0 y 1. r = Math.random(); //Lo imprime por consola. System.out.println(r); }}}

4.2.2 Bucle do while


Su comportamiento es semejante al bucle while, slo que aqu la condicin va al final del cdigo del bucle, por lo que tenemos garantizado que el cdigo se va a ejecutar al menos una vez. Depender del caso concreto si es ms conveniente emplear un bucle while o do while. La sintaxis de do while es: do { Grupo de sentencias; }while(condicin); Obsrvese como el ejemplo anterior implementado mediante un bucle independientemente del valor de r ejecutar al menos una vez el cdigo de su cuerpo: do while

class Ejemplo4 { public static void main(String[] args) { double r; //Idntico al ejemplo anterior, solo que ahora la condicin //est al final del bucle. do { r = Math.random(); System.out.println(r); } while(r < 0.99d); } }

4.2.3 Bucle for


Su formato es el siguiente: for(expresion1;expresion2;expresion3){ Grupo de sentecias;} Expresion1 es una asignacin de un valor a una variable, la variable-condicin del bucle. Expresion2 es la condicin que se le impone a la variable del bucle y expresion3 indica una operacin que se realiza en cada iteracin a partir de la primera (en la primera iteracin el valor de la variable del bucle es el que se le asigna en expresion1) sobre la variable del bucle.

class Ejemplo5 { public static void main(String[] args) { for( char c = 0; c < 128; c++) //Imprime los caracteres correspondientes a los nmeros //enteros comprendidos entre 0 y 128. (int)c es el entero //correspondiente al carcter c.
34

System.out.println("valor: " + (int)c +" caracter: " + c); } }

4.2.4 Break y continue


No se tratan de un bucle, pero s de una sentencia ntimamente relacionada con estos. El encontrarse una sentencia break en el cuerpo de cualquier bucle detiene la ejecucin del cuerpo del bucle y sale de este, continundose ejecutando el cdigo que hay tras el bucle. Esta sentencia tambin se puede usar para forzar la salida del bloque de ejecucin de una instruccin condicional (esto ya se vio con switch). Continue tambin detiene la ejecucin del cuerpo del bucle, pero en esta ocasin no se sale del bucle, sino que se pasa a la siguiente iteracin de este. Observaremos el funcionamiento de ambos en un mismo ejemplo:

class Ejemplo6 { public static void main(String[] args) { for(int i = 0; i < 100; i++) { if(i == 74) break; // teminamos aqui el bucle //Salto a la siguiente iteracin si i no es divisible entre 9 if(i % 9 != 0) continue; //Si I es divisible entre 9 se imprime System.out.println(i); } int i = 0; // Lazo infinito del cual se sale con break: while(true) { i++; int j = i * 27; if(j == 1269) break; // Salimos del lazo if(i%10 != 0) continue;//Salto a la siguiente iteracin System.out.println(i); } } }

4.3 RETURN
Sus funciones son las mismas que en C++. Cuando se llama a un procedimiento ( que en POO se denomin mtodo) al encontrarse con una sentencia return se pasa el valor especificado al cdigo que llam a dicho mtodo y se devuelve el control al cdigo invocador. Su misin tiene que ver con el control de flujo: se deja de ejecutar cdigo secuencialmente y se pasa al cdigo que invoc al mtodo. Esta sentencia tambin est profundamente relacionada con los mtodos, ya que es la sentencia que le permite devolver al mtodo un valor. Podamos haber esperado ha hablar de mtodos para introducir esta sentencia, pero hemos decidido introducirla aqu por tener una funcin relacionada, entre otras cosas, con control de flujo. En el ejemplo 1 ya se ha ejemplificado su uso.

35

5.0 OBJETOS Y CLASES


Como ya hemos comentado Java es un lenguaje totalmente orientado a objetos, mucho ms que, por ejemplo, C++. En Java todo es un objeto, a excepcin de los tipos bsicos de variables enteras, reales y char. Pero bien, qu es un objeto? y qu es la programacin orientada a objetos?. Responder a estas preguntas no es en absoluto trivial, hay libros enteros escritos sobre objetos y metodologas de programacin orientada a objetos sin abordar ningn lenguaje de programacin en concreto . Aqu simplemente daremos unas nociones muy bsicas de programacin orientada a objetos que nos permitan empezar a adentrarnos en el mundo de Java.

5.1 INTRODUCCIN
En los aos 60 la programacin se realizaba de un modo clsico (no orientado a objetos). Un programa era un cdigo que se ejecutaba, los trozos de cdigo que se podan emplear en varias ocasiones a lo largo del programa (reusar) se escriban en forma de procedimientos que se invocaban desde el programa, y esta era la nica capacidad de reuso de cdigo posible. Segn los cdigos se fueron haciendo ms grandes y complejos este estilo de programacin se haca ms inviable: es difcil programar algo de grandes dimensiones con este estilo de programacin. La nica posibilidad de repartir trozos de cdigo relativamente independientes entre programadores son los procedimientos, y al final hay que juntar todos estos con el programa central que los llama, siendo frecuente encontrar problemas al unir estos trozos de cdigo. En los aos 70 se empez a imponer con fuerza otro estilo de programacin: POO, programacin orientada o objetos (en la literatura suele aparecer como OOP, Object Oriented Programing). Aqu un programa no es un cdigo que llama a procedimientos, aqu un programa es un montn de objetos, independientes entre si, que dialogan entre ellos pasndose mensajes para llegar a resolver el problema en cuestin. A un objeto no le importa en absoluto como est implementado otro objeto, que cdigo tiene o deja de tener, que variables usa.... slo le importa a que mensajes es capaz de responder. Un mensaje es la invocacin de un mtodo de otro objeto. Un mtodo es muy semejante a un procedimiento de la programacin clsica: a un mtodo se le pasan uno, varios o ningn dato y nos devuelve un dato a cambio. Si hay que repartir un programa de grandes dimensiones entre varios programadores a cada uno se le asignan unos cuantos objetos, y en lo nico que tendrn que ponerse de acuerdo entre ellos es en los mensajes que se van a pasar; la forma en que un programador implemente sus objetos no influye en absoluto en lo que los dems programadores hagan. Esto es as gracias a que los objetos son independientes unos de otros (cuanta mayor sea la independencia entre ellos de mayor calidad sern). Si analizamos lo que hemos dicho hasta aqu de los objetos veremos que estos parecen tener dos partes bastante diferenciadas: la parte que gestiona los mensajes, que ha de ser conocida por los dems, y que no podremos cambiar en el futuro sin modificar los dems objetos (s es posible aadir nuevos mtodos para dar nuevas funciones al objetos sin modificar los mtodos ya existentes). La otra parte es el mecanismo por el cual se generan las acciones requeridas por los mensajes el conjunto de variables que se emplean para lograr estas acciones. Esta segunda parte es, en principio, totalmente desconocida para los dems objetos (a veces no es as, pero es lo ideal en un buena OOP). Por ser desconocida para los dems objetos podemos en cualquier momento modificarla sin que a los dems les importe, y adems cada programador tendr total libertad para llevarla a cabo como l considere oportuno. La OOP permite abordar con ms posibilidades de xito y con un menor coste temporal grandes proyectos de software, simplificndole adems la tarea al programador.

5.2 CLASES Y HERENCIA


Una clase es la plantilla que usamos para crear los objetos. Todos los objetos pertenecen a una determinada clase. Un objeto que se crea a partir de una clase se dice que es una instancia de esa clase. Las distintas clases tienen distintas relaciones de herencia entre si: una clase puede
36

derivarse de otra, en ese caso la clase derivada o clase hija hereda los mtodos y variables de la clase de la que se deriva o clase padre. En Java todas las clases tienen como primer padre una misma clase: la clase Object. Por motivos de simplicidad y dada la corta duracin de este curso ignoraremos la existencia del concepto de package en la explicacin de los siguientes conceptos, o haremos breves referencias a este concepto sin dar demasiadas explicaciones. En general en lo que al alumno respecta se recomienda ignorar, al menos durante este curso, toda referencia al trmino package. Vamos a continuacin a profundizar en todos estos conceptos y a explicar su sintaxis en Java.

5.2.1 Definicin de una clase


La forma ms general de definicin de una clase en Java es: [Modificador] class nombre_clase [extends nombre_clase_padre] [implements interface] { Declaracin de variables; Declaracin de mtodos; } Los campos que van entre corchetes son optativos. nombre_clase es el nombre que le queramos dar a nuestra clase, nombre_clase_padre es el nombre de la clase padre, de la cual hereda los mtodos y variables. En cuanto al contenido del ltimo corchete ya se explicar ms adelante su significado. Los modificadores indican las posibles propiedades de la clase. Veamos que opciones tenemos:

5.2.1.1 Modificadores de clases


public: La clase es pblica y por lo tanto accesible para todo el mundo. Slo podemos tener una clase public por unidad de compilacin, aunque es posible no tener ninguna. Ninguno: La clase es amistosa. Ser accesible para las dems clases del package. Por motivos de tiempo no abordaremos en este curso que es un package, sin embargo mientras todas las clases con las que estemos trabajando estn en el mismo directorio pertenecern al mismo package y por ello sern como si fuesen pblicas. Como nunca trabajaremos en varios directorios asumiremos que la ausencia de modificador es equivalente a que la clase sea pblica. final: Indicar que esta clase no puede tener hijo, no se puede derivar ninguna clase de ella. abstract: Se trata de una clase de la cual no se puede instanciar ningn objeto. Ejemplo: class Animal{ int edad; String nombre; public void nace(){ System.out.println("Hola mundo"); } public void get_nombre(){ System.out.println(nombre); } public void get_edad(){ System.out.println(edad); } }

5.2.1.2 Sobrecarga de mtodos


Java admite lo que se llama sobrecarga de mtodos: puede haber varios mtodos con el mismo nombre pero a los cuales se les pasan distintos parmetros. Segn los parmetros que se le pasen se invocar a uno u otro mtodo: Ejemplo: Modificaremos un poco la clase animal anterior, observar que tiene mtodos con el mismo nombre:
37

class Animal{ int edad; String nombre; public void nace(){ System.out.println("Hola mundo"); } public void get_nombre(){ System.out.println(nombre); } public void get_nombre(int i){ System.out.println(nombre +" " +edad); } public void get_edad(){ System.out.println(edad); } }

5.2.1.3 Constructores
Constructores son mtodos cuyo nombre coincide con el nombre de la clase y que nunca devuelven ningn tipo de dato, no siendo necesario indicar que el tipo de dato devuelto es void. Los constructores se emplean para inicializar los valores de los objetos y realizar las operaciones que sean necesarias para la generacin de este objeto (crear otros objetos que puedan estar contenidos dentro de este objeto, abrir un archivo o una conexin de internet.....). Como cualquier mtodo, un constructor admite sobrecarga. Cuando creamos un objeto (ya se ver ms adelante como se hace) podemos invocar al constructor que ms nos convenga.

Ejemplo: Veamos la clase Animal y sus constructores. class Animal{ int edad; String nombre; public Animal(){ //constructor por defecto } public Animal(int _edad, String _nombre){ //constructor con 2 argumentos edad = _edad; nombre = _nombre; } public void nace(){ System.out.println("Hola mundo");} public void get_nombre(){ System.out.println(nombre); } public void get_nombre(int i){ System.out.println(nombre +" " +edad); } public void get_edad(){ System.out.println(edad); } }

38

5.2.2 Modificadores de mtodos y variables


Antes de explicar herencia entre clases comentaremos cuales son los posibles modificadores que pueden tener mtodos y variables y su comportamiento:

5.2.2.1 Modificadores de variables


public: Pblica, puede acceder todo el mundo a esa variable. Ninguno: Es amistosa, puede ser accedida por cualquier miembro del package. Para nosotros er equivalente a que fuese pblica. protected: Protegida, slo pueden acceder a ella las clases hijas de la clase que posee la variable y las que estn en el mismo package. private: Privada, nadie salvo la clase misma puede acceder a estas variables. Pueden acceder a ella todas las instancias de la clase (cuando decimos clase nos estamos refiriendo a todas sus posibles instancias) static: Esttica, esta variable es la misma para todas las instancias de una clase, todas comparten ese dato. Si una instancia lo modifica todas ven dicha modificacin.

5.2.2.2 Modificadores de un mtodo


public: Pblica, puede acceder todo el mundo a este mtodo. Ninguno: Es amistoso, puede ser accedida por cualquier miembro del package. Para nosotros ser equivalente a que fuese pblico. protected: Protegido, slo pueden acceder a ella las clases hijas de la clase que posea el mtodo y las que estn en el mismo package. private: Privada, nadie salvo la clase misma puede acceder a estos mtodos. static: Esttica, es un mtodo al cual se puede invocar sin crear ningn objeto de dicha clase. Math.sin, Math.cos son dos ejemplos de mtodos estticos. Desde un mtodo esttico slo podemos invocar otros mtodos que tambin sean estticos. final: Final, se trata de un mtodo que no podr ser cambiado por ninguna clase que herede de la clase donde se defini. Es un mtodo que no se puede sobrescribir. Ms adelante se explicar que es esto.

5.2.3 Herencia
Cuando en Java indicamos que una clase extends otra clase estamos indicando que es una clase hija de esta y que, por lo tanto, hereda todos sus mtodos y variables. Este es un poderoso mecanismo para la reusabilidad del cdigo. Podemos heredar de una clase, por lo cual partimos de su estructura de variables y mtodos, y luego aadir lo que necesitemos o modificar lo que no se adapte a nuestros requerimientos. Veamos un ejemplo:

class Animal{ protected int edad; String nombre; public Animal(){ } public Animal(int _edad, String _nombre){ edad = _edad; nombre = _nombre; }
39

public void nace(){ System.out.println("Hola mundo"); } public void get_nombre(){ System.out.println(nombre); } public void get_nombre(int i){ System.out.println(nombre +" " +edad); } public void get_edad(){ System.out.println(edad); } } public class Perro extends Animal{ Perro(){ //constructor vacio edad = 0; nombre ="Tobi"; } Perro(int edad, String nombre){ //Esta sentencia invoca a un constructor de la clase padre. super(edad,nombre); } //Mtodo esttico que recibe un objeto de tipo perro e //invoca a su mtodo get_edad() static void get1(Perro dog){ //El mtodo get_edad() no est definido en perro, lo ha //heredado de animal. dog.get_edad(); } public static void main (String[] args){ //Creamos un objeto de tipo perro Perro dog = new Perro(8,"Bambi"); //Invocamos al mtodo esttioco get1 pasndole el objeto // Perro.get1(dog); } }

5.2.4 Creacin y referencia a objetos


Aunque ya hemos visto como se crea un objeto vamos a formalizarlo un poco. Un objeto en el ordenador es esencialmente un bloque de memoria con espacio para guardar las variables de dicho objeto. Crear el objeto es sinnimo de reservar espacio para sus variables, inicializarlo es darle un valor a estas variables. Para crear un objeto se utiliza el comando new. Vemoslo sobre un ejemplo:

class Fecha{ int dia,mes,ano; Fecha(){ dia=1; mes = 1; anio = 1900; }


40

Fecha (int _dia, int _mes, int _anio){ dia= _dia; mes = _mes; ano = _anio; } }
Fecha hoy; hoy = new Fecha(); Con el primer comando hemos creado un puntero que apunta a una variable tipo fecha, como est sin inicializar apuntara a null. Con el segundo inicializamos el objeto al que apunta hoy, reservando espacio para sus variables. El constructor las inicializar, tomando el objeto hoy el valor 1-1-1900. Fecha un_dia = new Fecha(8,12,2005); Con esta sentencia creamos una variable que se llama un_dia con valor 8-12-2005. Una vez creado un objeto ser posible acceder a todas sus variables y mtodos pblicos, as por ejemplo en el ejemplo anterior un_dia.dia = 8. Si la variable fuese privada solo podran acceder a ella sus instancias:

class Fecha{ private int dia,mes,ano; Fecha(){ dia=1; mes = 1; ano = 1900; } Fecha (int ndia, nmes, nano){ dia= ndia; mes = nmes; ano = nano; } }
De este modo no podramos acceder a las variables de la clase fecha, para acceder a ella tendramos que hacerlo mediante mtodos que nos devolviesen su valor. A esto se le denomina encapsulacin. Esta es la forma correcta de programar OOP: no debemos dejar acceder a las variables de los objetos por otro procedimiento que no sea paso de mensajes entre mtodos.

5.2.5 this
Es una variable especial de slo lectura que proporciona Java. Contiene una referencia al objeto en el que se usa dicha variable. A veces es til que un objeto pueda referenciarse a si mismo:

class Cliente{ public Cliente(String n){ //Llamamos al otro constructor. El empleo de this ha de ser //siempre en la primera lnea dentro del constructor. this(n, Cuenta.nuevo_numero()); ..... } public Cliente (String n, int a){ nombre = n; numero_cuenta = a;
41

} ..... }
Otro posible uso de this, que ya se ha visto en ejemplos anteriores es diferenciar entre variables locales de un mtodo o constructor y variables del objeto.

public Animal(int edad, String nombre){ //this.edad = variable del objeto perro //edad = variable definida slo dentro del constructor this.edad =edad; this.nombre=nombre; }

5.2.6 super
Del mismo modo que this apunta al objeto actual tenemos otra variable super que apunta a la clase de la cual se deriva nuestra clase :

class Gato { void hablar(){ System.out.println("Miau"); } } class GatoMagico extends Gato { boolean gente_presente; void hablar(){ if(gente_presente) //Invoca al mtodo sobreescrito de la clase padre super.hablar(); else System.out.println("Hola"); } }
Uno de los principales usos de super es, como ya se emple en un ejemplo, llamar al constructor de la clase padre.

5.3 INTERFACES
En Java no est soportada la herencia mltiple, esto es, no est permitido que una misma clase pueda heredar las propiedades de varias clases padres. En principio esto pudiera parecer una propiedad interesante que le dara una mayor potencia al lenguaje de programacin, sin embargo los creadores de Java decidieron no implementar la herencia mltiple por considerar que esta aade al cdigo una gran complejidad (lo que hace que muchas veces los programadores que emplean programas que s la soportan no lleguen a usarla). Sin embargo para no privar a Java de la potencia de la herencia mltiple sus creadores introdujeron un nuevo concepto: el de interface. Una interface es formalmente como una clase, con dos diferencias: sus mtodos estn vacos, no hacen nada, y a la hora de definirla en vez de emplear la palabra clave class se emplea inteface. Vemoslo con un ejemplo:

interface Animal1{ public int edad = 10; public String nombre = "Bob";
42

public void nace(); public void get_nombre(); void get_nombre(int i); }


Cabe preguntarnos cual es el uso de una interface si sus mtodos estn vacos. Bien, cuando una clase implementa una interface lo que estamos haciendo es una promesa de que esa clase va a implementar todos los mtodos de la interface en cuestin. Si la clase que implementa la interface no sobrescribiera alguno de los mtodos de la interface automticamente esta clase se convertira en abstracta y no podramos crear ningn objeto de ella. Para que un mtodo sobrescriba a otro ha de tener el mismo nombre, se le han de pasar los mismos datos, ha de devolver el mismo tipo de dato y ha de tener el mismo modificador que el mtodo al que sobrescribe. Si no tuviera el mismo modificador el compilador nos dara un error y no nos dejara seguir adelante. Vemoslo con un ejemplo de una clase que implementa la anterior interface:

interface Animal1{ public int edad = 10; public String nombre = "Bob"; public void nace(); public void get_nombre(); void get_nombre(int i); } ***************************************************** public class Perro1 implements Animal1{ Perro3(){ get_nombre(); get_nombre(8); } //Comprubese como si cambiamos el nombre del mtodo a nac() //no compila ya que no henos sobreescrito todos los mtodos //de la interfaz. public void nace(){ System.out.println("hola mundo"); } public void get_nombre(){ System.out.println(nombre ); } public void get_nombre(int i){ System.out.println(nombre +" " +i); } public static void main (String[] args){ Perro1 dog = new Perro1(); //Comprubese como esta lnea da un error al compilar debido //a intentar asignar un valor a una variable final // dog.edad = 8; } }
Las variables que se definen en una interface llevan todas ellas el atributo final, y es obligatorio darles un valor dentro del cuerpo de la interface. Adems no pueden llevar modificadores private ni protected, slo public. Su funcin es la de ser una especie de constantes para todos los objetos que implementen dicha interface.
43

Por ltimo decir que aunque una clase slo puede heredar propiedades de otra clase puede implementar cuantas interfaces se desee, recuperndose as en buena parte la potencia de la herencia mltiple. Veamos un ejemplo de mltiples interfaces

interface Animal1{ public int edad = 10; public String nombre = "Bob"; public void nace(); } ********************************* interface Animal2{ public void get_nombre(); } ********************************** interface Animal3{ void get_nombre(int i); } ********************************* public class Perro2 implements Animal1,Animal2,Animal3{ Perro2(){ get_nombre(); get_nombre(8); //edad = 10; no podemos cambiar este valor } public void nace(){ System.out.println("hola mundo"); } public void get_nombre(){ System.out.println(nombre ); } public void get_nombre(int i){ System.out.println(nombre +" " +i); } public static void main (String[] args){ Perro2 dog = new Perro2(); }}

5.5 Clases Abstractas:


Cuando se quiere definir una superclase en la que se declare la estructura de una determinada abstraccin sin implementar completamente cada mtodo, es decir, en la que solo s defina una forma generalizada que ser compartida por todas las subclases, dejando que cada subclase complete los detalles necesarios.

// Un ejemplo sencillo de una clase abstracta. abstract class A { abstract void callme(); // en las clases abstractas se permiten mtodos concretos void callmetoo() {
44

System.out.println("Este es un mtodo concreto"); }} class B extends A { void callme() { System.out.println("implementacin de mtodo callme en B."); }} class AbstractApp { public static void main(String args[]) { B b = new B(); b.callme(); b.callmetoo(); }} observacin: No se declaran objetos de la clase A, ya que no es posible crear una instancia de una clase abstracta. Las clase abstractas pueden incluir tanta implementacin como sea necesario. Aunque no es posible crear instancia de las clases abstractas, si es posible crear referencias a objetos, ya que el polimorfismo de java en tiempo de ejecucin se implementa mediante la referencia a las superclases. // Mejora de la clase figura con una clase abstracta. abstract class Figura { double dim1; double dim2; Figura(double a, double b) { dim1 = a; dim2 = b; } // area es ahora un mtodo abstracto abstract double area(); } ************************ class Rectangulo extends Figura { Rectangulo(double a, double b) { super(a, b); } // se sobreescribe area para un rectangulo double area() { System.out.println("Dentro del mtodo area para un Rectangulo."); return dim1 * dim2; }} *********************************** class Triangulo extends Figura {
45

Triangulo(double a, double b) { super(a, b); } // sobreescribiendo el area para un triangulo double area() { System.out.println("Dentro del mtodo area para un Triangulo."); return dim1 * dim2 / 2; }} ************************************** class AbstractaApp { public static void main(String args[]) { // Figura f = new Figure(10, 10); // esto no es correcto ahora Rectangulo r = new Rectangulo(9, 5); Triangulo t = new Triangulo(10, 8); Figura figref; // esto es correcto no se crea ningun objeto figref = r; System.out.println("Area es: " + figref.area()); figref = t; System.out.println("Area es :" + figref.area()); }}

5.6 Utilizacion de final con herencia


La palabra clave final impide que su contenido sea modificado, tiene tres utilizaciones. 1. Para crear el equivalente de un constante con nombre. Ej. Final int Nuevo=1; 2. Para impedir la sobreescritura (con herencia) class A { final void meth() { System.out.println("Este es un mtodo final."); }} **************************** class B extends A { void meth() { // ERROR! No esta permitida la sobreescritura. System.out.println("Ilegal!"); }} 3. utilizacion para impedir la herencia. final class A { // ... } // La siguiente classe es ilegal. class B extends A { // ERROR! No puede haber subclase de A // ... } static: Cuando se declara un miembro como static, se puede acceder al mismo antes de que cualquier de su clase sea, y sin referencia a ningn objeto. Se pueden declarar tanto

46

mtodos como a atributos como static. El ejemplo mas habitual de un miembro static es main(), este se declara static, ya que debe ser llamado antes de que exista cualquier objeto. class StaticDemo { static int a = 42; static int b = 99; static void callme() { System.out.println("a = " + a); }} class StaticApp { public static void main(String args[]) { StaticDemo.callme(); System.out.println("b = " + StaticDemo.b); }} Salida: a=42; b=99; En este ejemplo dentro de main() se accede al metodo static callme() y a la variable b fuera de su clase.

5.6.1 Tamao de un arreglo


El tamao de una matriz, el numero de elementos se encuentra en su variable de instancia length. // Demostracin de la longitud de una matriz de datos. class Length { public static void main(String args[]) { int a1[] = new int[10]; int a2[] = {3, 5, 7, 1, 8, 99, 44, -10}; int a3[] = {4, 3, 2, 1}; System.out.println("longitud de a1 es " + a1.length); System.out.println("longitud de a2 es " + a2.length); System.out.println("longitud de a3 es " + a3.length); }}

5.7 La clase String


La clase String tiene varios mtodos que pueden ser utilizados entre ellos estn: Se puede comprobar la igualdad de dos cadenas mediante equals(). Se puede obtener la longitud de una cadena mediante length(). Se puede obtener el carcter que ocupa una posicin determinada dentro de una cadena con charAt(). La forma general de estos objetos es : boolean equals (objeto String) int length() char charAt(int indice) class StringApp { public static void main(String args[]) { String strOb1 = "Primera Cadena"; String strOb2 = "Segunda Cadena"; String strOb3 = strOb1; System.out.println("Longitud de strOb1: " + strOb1.length()); System.out.println("El caracter de la posicion 3 de strOb1: " + strOb1.charAt(3));
47

if(strOb1.equals(strOb2)) System.out.println("strOb1 == strOb2"); else System.out.println("strOb1 != strOb2"); if(strOb1.equals(strOb3)) System.out.println("strOb1 == strOb3"); else System.out.println("strOb1 != strOb3"); }} ********************************** // Ejemplo de matrices de cadena de caracteres. class StringApp1 { public static void main(String args[]) { String matriz[] = { "uno", "dos", "tres" }; for(int i=0; i<matriz.length; i++) System.out.println("matriz[" + i + "]: " + matriz[i]); }} ****************************************

5.8 Argumentos de la linea de ordenes.


En ocasiones, es necesario pasar informacin a un programa que se esta ejecutando. Esto se lleva a cabo pasando los argumentos de la lnea de ordenes a main().

// Presentacin de todos los argumentos de la linea de ordenes. class LineaApp { Java LineaApp esto es una prueba 100 public static void main(String args[]) { 1 for(int i=0; i<args.length; i++) Salida: System.out.println("args[" + i + "]: " +args[i]); args[0]:esto .... args[5]:-1 }} jgrasp opcion run ..... run arguments

48

6.0 Applets
6.1 Introduccin
Un applet es un programa en Java que se incrusta en una pgina HTML para agregarle funcionalidad. Como por seguridad los applets estn limitados a no poder accesar el medio ambiente de la computadora donde corren su principal utilidad es para hacer interfaces grficas de aplicaciones de bases de datos. Por supuesto la base de datos debe residir en la computadora de donde vino el applet. Tambin se pueden programar juegos, programas interactivos que complementen un texto en lnea o que ilustren un concepto en particular, simulaciones, etc.

6.2 Ciclo de vida


Los applets tienen un ciclo de vida durante el cual realizan algunas actividades. Cada actividad tiene asignado un mtodo que el navegador llama en el momento adecuado. Las principales actividades son: Inicio. Se hace cuando se carga el applet por primera vez y corresponde al mtodo init(). Comienzo. Se hace cuando el applet comienza a correr, ya sea despus del inicio o si fu detenido. Corresponde al mtodo start(). Detencin. Un applet se detiene cuando el usuario accesa otra pgina en su navegador. Corresponde al mtodo stop(). Destruccin. Permite liberar recursos al finalizar el applet. Corresponde al mtodo destroy(). Dibujo. Actualiza la salida del applet en la pantalla. Corresponde al mtodo paint(Graphics g). Se invoca cada vez que se necesita que el applet sea redibujado, ya sea al saltar al primer plano, al moverse la pantalla o cuando vuelve a correr despus de estar detenido. Es importante recalcar que ninguno de estos mtodos es invocado en forma explcita. El navegador los invoca en forma automtica dependiendo de si el applet necesita inicializarse o si est comenzando o si necesita volver a pintar lo que el applet haya dibujado. Un applet debe extender a la clase Applet. Esta clase proporciona implementaciones vacas de los 5 mtodos. Es responsabilidad del programador de applets sobreponer los mtodos que vaya a utilizar. No es necesario sobreponer todos, en la prctica casi siempre solo se sobreponen o el mtodo init() o el paint(), rara vez los dos a la vez. Ejemplo: import java.awt.Graphics; import java.awt.Font; import java.awt.Color; import java.applet.*; public class EjemploApplet extends Applet { Font f = new Font ("TimesRoman", Font.BOLD, 36); public void paint (Graphics g) { g.setFont (f);
49

g.setColor (Color.red); g.drawString ("Mi primer Applet !!!", 5, 40); } }

Codigo html: <html> <head> <title>EjemploApplet Applet</title> </head> <body> <h1>EjemploApplet Applet</h1> <hr> <applet code="EjemploApplet.class" width=300 height=300 codebase="." archive="" alt="Your browser understands the &lt;APPLET&gt; tag but isn't running the applet, for some reason." > Your browser is ignoring the &lt;APPLET&gt; tag! </applet> <hr> </body> </html>

50

Como se puede ver en el listado anterior la etiqueta <APPLET> es la que incrusta el applet en la pgina HTML. Esta etiqueta puede tener varios argumentos, los principales son: CODE Indica el nombre del archivo compilado. Si el archivo de clase est en la misma carpeta que la pgina HTML (lo ms recomendable) basta con poner el nombre, pero se pueden ejecutar applets remotos poniendo la direccin http completa. WIDTH Indica el ancho en pixeles del rea dentro de la pgina HTML que el navegador apartar para uso del applet. HEIGHT Es la altura de dicha rea en pixeles. ALIGN Permite alinear el applet a la izquierda (Left), al centro (Center) o a la derecha (Right).

6.3 Argumentos
Los applets pueden recibir argumentos desde el archivo HTML con la etiqueta <PARAM>, la cual tiene un atributo para el nombre del argumento y otro para el valor. La etiqueta <PARAM> va dentro de la etiqueta <APPLET>. Los argumentos se pasan cuando el applet se inicializa. Dentro del mtodo init() se puede recuperar los valores de los argumentos usando el mtodo getParameter(). Este mtodo recibe como argumento un string con el nombre del argumento y devuelve un string con el valor correspondiente a ese argumento o el valor null si el argumento no existe. Ejemplo:Un applet que lee tres argumentos y escribe un mensaje en la pantalla..
import java.awt.Graphics; import java.awt.Font; import java.awt.Color; import java.applet.*; public class EjemploArgumento extends Applet { Font f = new Font ("TimesRoman", Font.BOLD, 36); String desde, para, mssg; public void init () {
51

desde = getParameter ("desde"); if (desde == null) desde = "aqui"; para = getParameter ("para"); if (para == null) para = "alla"; mssg = getParameter ("mensaje"); if (mssg == null) mssg = "no hay mensaje"; } public void paint (Graphics g) { g.setFont (f); g.setColor (Color.red); g.drawString ("Desde " + desde + " para " + para, 5, 40); g.setColor (Color.green); g.drawString ("El mensaje es: " + mssg, 5, 80); } } Codigo html: <HTML> <HEAD> <TITLE>Ejemplo con Argumentos !!!</TITLE> </HEAD> <BODY> <P><H2>Bienvenido a Programacion III:</H2><P> <APPLET CODE="EjemploArgumento.class" WIDTH=600 HEIGHT=400> <PARAM NAME=desde VALUE="San Vicente"> <PARAM NAME=para VALUE="todos"> <PARAM NAME=mensaje VALUE="Este es Java applet !!!"> </APPLET> </BODY> </HTML>

52

6.4 Grficas
La librera de Java incluye la clase Graphics para hacer dibujos sencillos. Esta clase tiene mtodos para dibujar lneas, rectngulos, crculos, polgonos y arcos. En los applets el mtodo paint recibe un objeto de tipo Graphics. Al dibujar sobre este objeto se dibuja dentro del applet y los resultados se desplegarn en la pantalla. El sistema de coordenadas est en pixeles. El origen, es decir, el punto (0,0) est en la esquina superior izquierda del rea reservada para el applet en la pgina HTML. La X crece hacia la derecha en forma horizontal y la Y crece hacia abajo en forma vertical. La clase Graphics tiene los siguientes mtodos. Hay que aclarar que todos los argumentos son de tipo int. drawString (s, x, y) Escribe el texto almacenado en el string s. El punto (x, y) indica la coordenada donde comienza el texto. drawLine (x1, y1, x2, y2) Dibuja una lnea recta entre los puntos (x1, y1) y (x2, y2). drawRect (x, y, ancho, alto) Dibuja un rectngulo hueco. La esquina superior izquierda est dada por el punto (x, y) y la anchura y altura por ancho y alto respectivamente. fillRect (x, y, ancho, alto) - Dibuja un rectngulo lleno con color actual. drawRoundRect (x, y, ancho, alto, r_hor, r_ver) Dibuja un rectngulo hueco con las esquinas redondeadas. Los primeros 4 argumentos tienen el mismo sentido que en los dos mtodos anteriores. Los argumentos r_hor y r_ver determinan que tan lejos de los limites del rectngulo debe comenzar el redondeo en las esquinas. fillRoundRect (x, y, ancho, alto, r_hor, r_ver) Dibuja un rectngulo lleno con el color actual y redondeado en las esquinas. Los argumentos son los mismos que en el mtodo anterior. drawPolygon (x[], y[], n) Dibuja un polgono hueco. Los arreglos x y y guardan respectivamente las coordenadas x y y de cada una de las esquinas del polgono. El argumento n es el nmero de puntos (el tamao de los arreglos x y y). El polgono se cierra en forma automtica porque siempre se dibuja una lnea entre el ltimo punto y el primero. fillPolygon (x[], y[], n) Dibuja un polgono lleno con el color actual. Los argumentos son los mismos que en el mtodo anterior. drawPolyline (x[], y[], n) Dibuja una lnea quebrada. El resultado se puede pensar que es un polgono sin cerrar. drawOval (x, y, ancho, alto) Dibuja un valo hueco. La forma ms fcil de entender el resultado de este mtodo es imaginar que se dibuja un rectngulo imaginario con una anchura de ancho pixeles y una altura de alto pixeles, con la esquina superior izquierda en el punto (x, y) y que el valo se dibuja inscrito en el rectngulo. Por lo tanto, el punto (x, y) est fuera del valo. Si ancho es igual que alto, el rectngulo se convierte en cuadrado y el valo se convierte en crculo. fillOval (x, y, ancho, alto) Dibuja un valo lleno con el color actual. Los argumentos son los mismos que en el mtodo anterior. drawArc (x, y, ancho, alto, grados_ini, largo_grados) Dibuja un arco hueco. Los primeros 4 argumentos tienen el mismo sentido que en los dos mtodos anteriores. El argumento grados_ini indica desde que grado se comienza a dibujar el arco. Si pensamos en los puntos cardinales el grado 0 est al este, el 90 al norte, el 180 al oeste y el 270 al sur. Por ltimo largo_grados indica la longitud del arco y la direccin de dibujo. Un valor de 90 significa que se dibujar un cuarto de arco en contra de las manecillas del reloj, en cambio un valor de 90 tambin indica un cuarto de arco pero en el sentido de las manecillas del
53

reloj. Igualmente un valor de 180 significa medio arco y dependiendo del signo es la direccin. fillArc (x, y, base, altura, grados_ini, largo_grados) Dibuja un arco lleno con el color actual. Los argumentos son los mismos que en el mtodo anterior.
Ejemplo: import java.awt.Graphics; import java.applet.*; public class Dibujo extends Applet { public void paint (Graphics g) { // la plataforma g.fillRect (0, 250, 290, 290); // La base g.drawLine (125, 250, 125, 160); g.drawLine (175, 250, 175, 160); // La cubierta g.drawArc (85, 157, 130, 50, -65, 312); g.drawArc (85, 87, 130, 50, 62, 58); g.drawLine (85, 177, 119, 89); g.drawLine (215, 177, 181, 89); // Las motas g.fillArc (78, 120, 40, 40, 63, -174); g.fillOval (120, 96, 40, 40); g.fillArc (173, 100, 40, 40, 110, 180); } }

54

6.5 Tipo de letra


Para cambiar el tipo de letra hay que crear una instancia de la clase Font. Font f = new Font ("TimesRoman", Font.BOLD, 24); El primer argumento es el nombre del tipo de letra. Los tipos de letra disponibles se pueden saber usando esta instruccin: String fontlist[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames (); El segundo argumento es el estilo, puede valer Font.PLAIN (estilo normal), Font.BOLD (estilo en negritas) o Font.ITALIC (estilo en cursiva o itlico). Se pueden mezclar dos o ms estilos con un el signo +, por ejemplo se puede indicar Font.BOLD + Font.ITALIC. El tercer argumento es el tamao en puntos del tipo de letra y depende de cada tipo de letra. Para cambiar el tipo de letra se invoca al mtodo: setFont (f) donde f es un objeto de tipo Font.
Ejemplo: import java.awt.Font; import java.awt.Graphics; import java.applet.*; public class Fondo extends Applet { public void paint (Graphics g) { Font f1 = new Font ("Times Roman", Font.PLAIN, 18); Font f2 = new Font ("Times Roman", Font.BOLD, 18); Font f3 = new Font ("Times Roman", Font.ITALIC, 18); Font f4 = new Font ("Times Roman", Font.BOLD + Font.ITALIC, 18); g.setFont (f1); g.drawString ("Este es texto normal", 10, 25); g.setFont (f2); g.drawString ("Este es texto en negrita", 10, 50); g.setFont (f3); g.drawString ("Este es texto en cursiva", 10, 75); g.setFont (f4); g.drawString ("Este es texto en negrita y cursiva", 10, 100); } }

55

6.6 Color
Para cambiar el color hay que crear una instancia de la clase Color: Color c = new Color (r, g, b); donde r, g y b es la cantidad de rojo, verde y azul respectivamente, en el rango de 0 a 255. El (0,0,0) representa el negro y el (255, 255, 255) el blanco. En total hay 256 3 = 16,777,216 colores distintos. Se puede usar alguno de los colores ya definidos en la clase Color como Color.white, Color.red, Color.black, Color.orange, Color.blue, Color.gray, Color.yellow, etc. Hay 3 funciones para cambio de color: setColor (c). Cambia el color actual para las operaciones de dibujo a partir de este momento. El argumento c es un objeto de tipo Color. setBackground (c). Cambia el color del fondo al color c. setForeground (c). Cambia el color de lo que se haya dibujado sin importar el color en el que se dibuj. Ejemplo:
import java.awt.Graphics; import java.applet.*; import java.awt.Color; public class CambioColor extends Applet { public void paint (Graphics g) { Color c1 = new Color (120, 30, 220); Color c2 = new Color (40, 240, 200); setBackground (Color.lightGray); g.setColor (c1); g.drawString ("Ovalo hueco", 5, 20); g.drawOval (5, 30, 100, 60); g.drawString ("Circulo hueco", 150, 20); g.drawOval (150, 30, 60, 60); g.setColor (c2); g.drawString ("Ovalo lleno", 5, 120); g.fillOval (5, 130, 100, 60); g.setColor (Color.red); g.drawString ("Circulo lleno", 150, 120); g.fillOval (150, 130, 60, 60); } }

6.7 Imgenes
Un applet pueden desplegar imgenes en formato GIF y JPEG. Lo primero es crear una instancia de la clase Image. Image mi_imagen = getImage (URL, nombre); donde URL es la direccin donde est la imagen y nombre es el nombre del archivo que contiene la imagen. Para la variable URL hay 3 opciones:

56

a) Poner la direccin absoluta. Si la imagen esta en: http://www.foo.com/images/foo.gif, entonces poner: Image imagen = getImage (new URL ("http://www.foo.com/images", "foo.gif"); b) Usar la direccin relativa con respecto a donde est la pgina HTML. Si el directorio images est abajo de donde est la pgina, entonces poner: Image imagen = getImage (getDocumentBase (), "images/foo.gif"); c) Usar la direccin relativa a donde est el applet compilado (archivo .class). Si el directorio images est abajo de donde est el cdigo objeto, entonces poner: Image imagen = getImage (getCodeBase (), "images/foo.gif"); Para desplegar la imagen: drawImage (imagen, x, y, ancho, alto, donde); donde imagen es un objeto de tipo Image, x y y son las coordenadas de la esquina superior izquierda de la imagen. Las variables ancho y alto indica el ancho y el alto en pixeles con que se va a desplegar la imagen. Por ltimo, la variable donde es el nombre del objeto que va a actuar como observador de la imagen que en el caso de los applets es el mismo applet.
Ejemplo: Para una imagen que se llama disco.gif import java.awt.Graphics; import java.applet.*; import java.awt.Image; public class Imagenes extends Applet { Image img; public void init () { img = getImage (getCodeBase (), "disco.gif"); } public void paint (Graphics g) { int ancho = img.getWidth (this); int alto = img.getHeight (this); int xpos = 10, ypos = 10; // 25 % g.drawImage (img, xpos, ypos, ancho / 4, alto / 4, this); // 50 % xpos += (ancho / 4) + 10; g.drawImage (img, xpos, ypos, ancho / 2, alto / 2, this); // 100 % xpos += (ancho / 2) + 10; g.drawImage (img, xpos, ypos, this); // 150 % xpos += ancho + 10; g.drawImage (img, xpos, ypos, (int) (ancho * 1.5), (int) (alto * 1.5), this); // distorsionado xpos += (int) (ancho * 1.5 + 10); g.drawImage (img, xpos, ypos, ancho / 2, (int) (alto * 1.5), this); } }

57

7.0 Abstract Window Toolkit y Eventos


7.1 Etiquetas
Una etiqueta es un texto no editable que despliega un letrero en la pantalla. Para definir una etiqueta hay que crear un objeto de tipo Label indicando el texto de la etiqueta y opcionalmente una alineacin: Constructores: Label(), Label(String str), Label(String str, int how) El valor de how debe ser cualquiera de estas constantes: Label.LEFT, Label.RIGHT, Label.CENTER Se puede establecer o cambiar el texto de la etiqueta con el mtodo setText(), si queremos obtener el texto de una etiqueta se utiliza el metodo getText(). Se puede establecer el alineamiento del string dentro de la etiqueta utilizando el mtodo setAlignment(). Para obtener el alineamiento de la etiqueta se utiliza el mtodo getAlignment()
Ejemplo: import java.awt.*; import java.applet.*; public class LabelApplet extends Applet { Label lbl = new Label ("Etiqueta uno"); Label lbl2 = new Label ("Etiqueta dos", Label.RIGHT); Label lbl3 = new Label(); public void init () { lbl3.setText("Etiqueta tres"); lbl3.setAlignment(Label.CENTER); add (lbl); add (lbl2); add (lbl3); }}

7.2 Gestion de Botones


Un botn es un componente grfico que activa algn evento cuando se le oprime usando el ratn. Para definir un botn hay que crear un objeto de tipo Button indicando el texto que desplegar el botn: Constructores: Button(), Button(String str) Una vez creado el boton se le puede asignar un Label llamando a setLabel(), tambien se puede obtener su etiqueta con getLabel() Gestionar Botones: Cada vez que se pulsa un botn, se genera un evento de accin que se enva a cualquier
58

auditor que previamente haya registrado un inters por recibir informacin de eventos de accin que generan cada componente. Cada auditor implementa la interfaz ActionListener. Esta interfaz define el mtodo actionPerformed(), que es al que se llama cuando ocurre un evento. Como argumento a este mtodo se pasa un objeto ActionEvent. Cada vez que se pulsa un botn, se muestra un mensaje que indica que boton se ha pulsado. La etiqueta se obtiene llamando al mtodo getActionCommand() sobre el objeto ActionEvent que se pasa a actionPerformed(). Ejemplo:
import java.awt.*; import java.awt.event.*; import java.applet.*; public class Boton1Applet extends Applet implements ActionListener { String msg=""; Button btn1,btn2; public void init (){ btn1 = new Button ("Boton 1"); btn2 = new Button ("Boton 2"); add (btn1); add (btn2); btn1.addActionListener(this); btn2.addActionListener(this); } public void actionPerformed(ActionEvent ae){ String str=ae.getActionCommand(); if (str.equals("Boton 1")){ msg="Presiono el Boton 1"; } else { msg="Presiono el Boton 2"; } repaint(); } public void paint(Graphics g ){ g.drawString(msg,6,50); } }

59

7.3 Checkboxes
Un checkbox es un componente que tiene dos estados: marcado o no marcado. Los checkboxes se pueden agrupar de tal forma que solo un checkbox de ese grupo pueda estar activado a la vez. Para definir un checkbox hay que crear un objeto de tipo Checkbox indicando un texto como etiqueta y opcionalmente un booleano para indicar si de entrada est marcada o no. Los checkbox se pueden utilizar individualmente o como parte de grupo: Constructores: Checkbox(), Checkbox(String str), Checkbox(String str, boolean on), Checkbox(String str, boolean on, CheckboxGroup cbgroup), CheckboxGroup (String str, CheckboxGroup cbGroup, boolean on). Para obtener el estado inicial de un checkbox, hay que llamar a getState(). Para establecer un determinado estado, se puede llamar a setState(). Se puede obtener la etiqueta asociada al checkbox llamando al mtodo getLabel(). Llamado al mtodo setLabel() se establece la etiqueta. Para marcar (o desmarcar) desde un programa un checkbox se usa el mtodo setState() pasando un argumento booleano que indique el nuevo estado, cb1.setState (false); // desmarca el checkbox cb1. Ejemplo: dos grupos de checkbox
import java.awt.*; import java.applet.*; public class Checkbox1Applet extends Applet { Label lb1 = new Label ("Estado civil"); Label lb2 = new Label ("Nacionalidad"); CheckboxGroup cbg1 = new CheckboxGroup (); CheckboxGroup cbg2 = new CheckboxGroup (); Checkbox cbs = new Checkbox ("soltero", false, cbg1); Checkbox cbc = new Checkbox ("casado", true, cbg1); Checkbox cbv = new Checkbox ("viudo", false, cbg1); Checkbox cbsal = new Checkbox ("salvadoreo", true, cbg2); Checkbox cbext = new Checkbox ("extranjero", false, cbg2); public void init (){ add (lb1); add (cbs); add (cbc); add (cbv); add (lb2); add (cbsal); add (cbext); } }

60

Gestin de Checkbox Cada vez que se selecciona o se deselecciona un checkbox, se genera un evento que se enva a cualquier auditor que previamente se haya registrado para recibir informacin de los eventos que se producen desde ese componente. Cada auditor implementa la interfaz ItemListener. Esta interfaz define el metodo itemStateChanged(). Como argumento de este metodo se pasa un objeto ItemEvent, que encapsula la inforarmacion sobre el evento. Ejemplo:
import java.awt.*; import java.awt.event.*; import java.applet.*; public class Checkbox2Applet extends Applet implements ItemListener{ String msg=""; Checkbox win98, linux, solaris; CheckboxGroup cbg; public void init (){ cbg=new CheckboxGroup(); win98=new Checkbox("Window98",cbg,true); linux=new Checkbox("Linux",cbg,false); solaris=new Checkbox("Solaris",cbg,false); add (win98); add (linux); add (solaris); win98.addItemListener(this); linux.addItemListener(this); solaris.addItemListener(this); } public void itemStateChanged(ItemEvent ie){ repaint(); } public void paint(Graphics g){ msg="Seleccion Actual : "; msg+=cbg.getSelectedCheckbox().getLabel(); g.drawString(msg,6,50); } }

61

7.4 Choices Un choice se utiliza para crear una lista pop-up de elementos para qu el usuario pueda elegir uno de ellos. Por tanto un control choice es un men del cual se puede escoger solo una opcin. Para usar un choice hay que declarar un objeto de tipo Choice y luego usar el mtodo add para agregar la lista de opciones. Choice solo define un constructor por defecto que crea una lista vacia. Para aadir una seleccion a la lista, hay que llamar a addItem() o a add() que tienen los siguientes formatos: void addItem(String nombre) void add(String nombre) donde nombre corresponde al nombre del elemento que se aade. Los elementos se aaden a la lista en el orden en que se llama a add() o a addItem(). Para saber que opcin fue seleccionada se usa el mtodo getSelectedIndex() o al mtodo getSelectedItem(). El metodo getSelectedItem() devuelve un string que contiene el nombre del elemento, y getSelectedIndex() devuelve la posicin del elemento considerando que el primer elemento esta en la posicin 0. Por defecto se selecciona el primer elemento que se aade a la lista. Para obtener el numero de elementos que hay en la lista, hay que llamar al mtodo getItemcount(). Se puede establecer el elemento que tiene que estar seleccionado utilizando el mtodo select() tanto con un entero que indique la posicin del elemento (empezando a contar desde cero) como con un string que tenga el nombre que aparece en la lista estos mtodos son: int getItemCount(), void select(int index), void select (String nombre) Dada una posicin se puede obtener el nombre del elemento que esta en esa posicin llamando al mtodo getItem(), que tiene el siguiente formato: String getItem(int index) Gestin de listas Choice Cada vez que se hace una seleccin, se genera un evento que se enva a todo auditor que se haya registrado para recibir la informacin de los eventos que ese componente. Cada auditor debe implementar la interfaz ItemListener, que define el mtodo itemStateChanged(). Se pasa como argumento a este metodo un objeto de la clase ItemEvent. Ejemplo:
import java.awt.*; import java.awt.event.*; import java.applet.*;
62

public class Choice1Applet extends Applet implements ItemListener { Choice ch_deportes; String msg=""; public void init (){ ch_deportes=new Choice(); ch_deportes.addItem ("Ftbol"); ch_deportes.addItem ("Besbol"); ch_deportes.addItem ("Hockey"); ch_deportes.addItem ("Natacion"); add (ch_deportes); //se registran para recibir eventos de elemento ch_deportes.addItemListener(this); } public void itemStateChanged(ItemEvent ie){ repaint(); } public void paint(Graphics g){ msg="Deporte seleccionado : "; msg+=ch_deportes.getSelectedItem(); g.drawString(msg,6,100); } }

7.5 TextField
Un campo de texto es un componente que permite capturar y editar una lnea de texto. Para usar un campo de texto hay que crear un objeto de tipo TextField que es una subclase de TextComponent indicando el nmero de caracteres de ancho y opcionalmente un texto inicial, proporciona los siguientes constructores: TextField(), TextField(int numc), TextField(String str), TextField(String str, int numc) Donde: numc determina el nmero de caracteres del campo de texto. Para establecer un determinado texto se llama al mtodo setText() y para obtener un string del campo de texto se utiliza getText(). Se puede seleccionar una parte del texto con select(), y para obtener el texto completo seleccionado se llama s getSelectText().

63

Forma general: String getSelectText(), void select(int inicio, int fin) El usuario tambin puede modificar el contenido del campo de texto llamando a setEditable(). Tambin puede determinar si es editable o no llamando a isEditable(). Forma general: Booleam isEditable(), void setEditable(boolean valor) Puede que haya ocasiones en que interese que no se vea el texto que introduce, esto se hace llamando a setEdhoChar(). Se puede chequear un campo de texto para ver si esta en este modo con el metodo echoCharIsSet() y ver que caracter se va a ver llamando al metodo getEchoChar().
Ejemplo: import java.awt.*; import java.applet.*; public class TextFieldApplet extends Applet { TextField tf1 = new TextField (10); TextField tf2 = new TextField ("Nombre de la ciudad",30); TextField tf3 = new TextField (15); public void init (){ add (tf1); add (tf2); tf3.setEchoChar ('*'); add (tf3); }}

Gestin de un TextField Como los campos de texto gestionan sus propias funciones de edicion, generalmente el programa no tendra que ocuparse de los eventos de teclas individuales que ocurran dentro de un campo de texto. Pero puede que se quiera responder cuado el usuario pulse la tecla ENTER; y si esto ocurre, se genera un evento de accion. Ejemplo:
import java.awt.*; import java.awt.event.*; import java.applet.*; public class TextField1Applet extends Applet implements ActionListener { TextField tf1,tf2,tf3; public void init(){ Label lbl1=new Label("Departamento : ",Label.RIGHT); Label lbl2=new Label("Ciudad :",Label.RIGHT); Label lbl3=new Label("Codigo : ",Label.RIGHT); tf1= new TextField (10); tf2 = new TextField ("Nombre de la ciudad",30); tf3 = new TextField (15); tf3.setEchoChar ('*'); add (lbl1); add (tf1); add (lbl2); add (tf2); add (lbl3); add (tf3);
64

// se registran los eventos tf1.addActionListener(this); tf2.addActionListener(this); tf3.addActionListener(this); } public void actionPerformed(ActionEvent ae){ repaint(); } public void paint(Graphics g){ g.drawString ("Departamento :"+tf1.getText(),6,60); g.drawString ("Ciudad :"+tf2.getText(),6,80); g.drawString ("Texto seleccionado de Ciudad :"+tf2.getSelectedText(),6,100); g.drawString ("Codigo :"+tf3.getText(),6,120); } }

7.6 List. La clase List proporciona una lista de seleccin compacta, con desplazamiento, que permite realizar selecciones mltiples. A diferencia de Choice que solo muestra un nico objeto que se puede seleccionar en el men, se puede construir un objeto List que muestre cualquier numero de opciones en una ventana. Tambin se puede configurar de manera que puedan realizar selecciones mltiples. Constructors: List(), List(int num), List (int num, boolean multiple) Mtodos: void add(String nombre), void add(String nombre, int index): para aadir una lista. En listas que solo permiten seleccionar un nico elemento se determina si este esta seleccionado con los mtodos getSelectedItem() o con getSelectedIndex(), el primero devuelve un string con el nombre del elemento y el segundo devuelve la posicin del elemento. En las listas que permiten seleccionar mas de un elemento se utilizan los mtodos getSelectedItems() o getSelectedIndexes(). String [] getSelectedItems() int[] getSelectedIndexes() Para obtener el numero de elementos que hay en la lista hay que llamar a getItemCount(), que devuelve un array con las posiciones seleccionados actualmente. Se puede establecer el elemento que tiene que estar seleccionado con el mtodo select(), pasndole un entero que indique la posicin del elemento. Gestin de Listas: Es necesario implementar la interfaz ActionListener. Cada vez que se de dobleclick en un elemento List se genera un evento de la clase ActionEvent. Puede utilizarse getActionCommand(), para guardar el nombre del elemento recin seleccionado. Cada vez que se selecciona un evento con un solo click, se genera un objeto de la clase ItemEvent. Se puede utilizar su mtodo getStateChange() para determinar si una seleccin o deseleccion ha desencadenado ese evento. Si se utiliza getItemSelectable() devuelve una referencia al objeto que ha desencadenado ese evento.
65

Ejemplo:
//con doble click import java.awt.*; import java.awt.event.*; import java.applet.*; public class ListaApplet extends Applet implements ActionListener { List lista; final Color[] colores={Color.red, Color.green, Color.blue}; int indice; public void init() { lista=new List(3,false); //se aaden los elementos de la lista lista.add("Rojo"); lista.add("Verde"); lista.add("Azul"); lista.select(0); //se aaden la listas al contenedor add(lista); //registrando eventos lista.addActionListener(this); } public void actionPerformed(ActionEvent ae){ indice=lista.getSelectedIndex(); repaint(); } public void paint(Graphics g){ g.setColor(colores[indice]); g.fillRect(2, 2, 100, 50); } }

7.7 TextArea. Editor multilineas.


Constructores: TextArea(), TextArea(int numlin, int numchar), TextArea(String str), TextArea(String str, int numli, int numchar), TextArea(String str, int numlin, int numchar, int sbar). Donde numlin especifica la altura en lineas del area de texto, numchar especifica la anchura en caracteres. Se puede especificar un texto inicial con str. Tambien se puede especificar con sbar las barras de desplazamiento que va a tener el control, puede tomar los siguientes valores: SCROLLBARS_BOTH, SCROLLBARS_HORIZONTAL_ONLY, SCROLLBARS_NONE, SCROLLBARS_VERTICAL_ONLY.
66

TextArea es una subclase de TextComponent. Por tanto, soporta los metodos getText(), setText(), getSelectedText(), select(), isEditable() y setEditable(). TextArea aade los siguientes metodos: void append(String str), void insert(String str, int index), void replaceRange(String str, int inicio, int fin). El metodo append() aade el string especificado en str al final del texto actual. El metodo insert() inserta el string que se pasa en str en el punto especificado en index. Para reemplazar texto hay que llamar a replaceRange(), que reemplaza los caracteres desde inicio a fin con el texto que esta en str.
Ejemplo: import java.awt.*; import java.applet.*; import java.awt.event.*; // Presentamos dos areas de texto y un boton para imprimir el contenido public class AreaTexto extends Applet implements ActionListener{ String texto=""; TextArea t1,t2; public void init() { Button boton = new Button( "Aceptar" ); // Creamos las dos areas de texto, una con el constructor de // defecto del Componente y otra con un texto por defecto y // limitada su longitud a 40 columnas t1 = new TextArea(); t2 = new TextArea( "Aqui no se puede editar",5,40 ); // Hacemos que no sea editable t2.setEditable( false ); add( t1 ); add( t2 ); add( boton ); boton.addActionListener(this); } // Solo controlaremos el evento generado por el boton public void actionPerformed(ActionEvent ae ) { // Si pulsamos el boton, imprimimos en la consola el contenido // del campo de texto que se haya escrito String str=ae.getActionCommand(); if( str.equals("Aceptar") ) { texto = t1.getText(); } repaint(); } public void paint(Graphics g){ g.setColor(Color.red); g.drawString(texto,20,300); } }

67

7.8 Componentes avanzados 7.8.1 Paneles


Un panel es un componente contenedor y como tal puede contener otros componentes de AWT. Se utilizan para eliminar la restriccin que imponen los diseos de que solo cabe un componente por celda o por zona y poder programas interfaces grficas complejas. Un panel tiene su propio diseo y sus propios componentes como botones, campos de texto, etc. Los paneles a su vez pueden contener otros paneles que a su vez tengan otros componentes. No hay restriccin en el nmero de paneles ni en el nivel de anidacin. Parar usar un panel hay que crear un objeto de tipo Panel. Panel p = new Panel (); Como se dijo anteriormente los paneles pueden tener su propio diseo especificado con el mtodo setLayout. p.setLayout (diseo); Todo lo que se ha dicho sobre agregar componentes con el mtodo add es vlido para los paneles. p.add (botn); p.add (checkbox); El uso de paneles puede mejorar el diseo de una interface grfica evitando que los componentes se vean alineados como si estuvieran en una parrilla gigante. Una regla de diseo especifica que los componentes que estn relacionados entre s se coloquen juntos en un panel. El ejemplo a continuacion presenta un applet que utiliza 3 paneles en un diseo de parrilla vertical. El panel de en medio tiene a su vez dos paneles para que sus componentes no se amontonen en un solo lado.

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


68

public class testPanel extends Applet { Panel pnl_1 = new Panel (); Panel pnl_2 = new Panel (); Panel pnl_3 = new Panel (); Panel pnl_4 = new Panel (); Panel pnl_5 = new Panel (); Checkbox cb_1 = new Checkbox ("Opcion uno"); Checkbox cb_2 = new Checkbox ("Opcion dos"); Checkbox cb_a = new Checkbox ("Opcion A"); Checkbox cb_b = new Checkbox ("Opcion B"); Checkbox cb_c = new Checkbox ("Opcion C"); Label lbl_1 = new Label ("Soy etiqueta"); Choice ch_1 = new Choice (); Choice ch_2 = new Choice (); Button btn_1 = new Button ("Soy un boton"); Button btn_2 = new Button ("Soy otro boton"); public void init () { setBackground (Color.lightGray); setLayout (new GridLayout (3, 1)); add (pnl_1); add (pnl_2); add (pnl_5); // panel 1 pnl_1.setLayout (new FlowLayout (FlowLayout.LEFT)); pnl_1.add (cb_1); pnl_1.add (cb_2); // panel 2 pnl_2.setLayout (new BorderLayout ()); // panel 3 pnl_3.setLayout (new GridLayout (3, 1)); pnl_3.add (cb_a); pnl_3.add (cb_b); pnl_3.add (cb_c); pnl_2.add ("West", pnl_3); // panel 4 pnl_4.setLayout (new GridLayout (1, 3, 30, 10)); pnl_4.add (lbl_1); ch_1.add ("A"); ch_1.add ("B"); ch_1.add ("C"); ch_2.add ("1"); ch_2.add ("2"); pnl_4.add (ch_1); pnl_4.add (ch_2); pnl_2.add ("East", pnl_4); // panel 5 pnl_5.setLayout (new FlowLayout (FlowLayout.CENTER, 10, 30)); pnl_5.add (btn_1); pnl_5.add (btn_2);
69

} }

7.8.2 Canvas (Lienzos)


Un lienzo es un componente cuya principal utilidad es desplegar grficas o imgenes. Debido a que no puede contener otros componentes por lo general no se usa en forma directa sino a travs de programar una clase que extienda a la clase Canvas. public miCanvas extends Canvas { La clase Canvas define un mtodo paint vaco. La clase que extienda a Canvas sobrepone el mtodo paint para dibujar o desplegar una imagen de la misma forma en que lo hace el mtodo paint de un applet. Si se revisa el manual de Java encontraremos que tanto la clase Canvas como la clase Applet son subclases de la clase Component y de ella heredan el mtodo paint. La clase Component representa objetos que pueden ser desplegados en la pantalla y pueden interactuar con el usuario. Otras subclases de Component son precisamente las clases que definen los componentes que ya vimos como Panel, Button, TextField, etc. El ejemplo siguiente presenta un applet que del lado izquierdo coloca un lienzo donde dibuja una lmpara sobre una mesa y del lado derecho un panel donde hay dos choices que controlan el color con que se dibujan la mesa y la lmpara. La clase miCanvas es la que realmente hace el dibujo en su mtodo paint y adems tiene que definir dos mtodos para que el applet le avise si cambia el color. La figura muestra la salida del applet.

70

import java.applet.*; import java.awt.*; import java.awt.event.*; public class testCanvas extends Applet { miCanvas mc; Panel pnl_1; Label lbl_1 = new Label ("La lampara de Aladino"); Label lbl_2 = new Label ("Color de la mesa"); Choice color_mesa = new Choice (); Label lbl_3 = new Label ("Color de la lampara"); Choice color_lampara = new Choice (); public void init () { setLayout (new BorderLayout ()); lbl_1.setFont (new Font ("TimesRoman", Font.BOLD, 18)); add ("North", lbl_1); add ("Center", mc = new miCanvas ()); add ("East", pnl_1 = new Panel ()); pnl_1.setLayout (new GridLayout (4, 1)); pnl_1.add (lbl_2); color_mesa.add ("Negro"); color_mesa.add ("Rojo"); color_mesa.add ("Azul"); color_mesa.add ("Verde"); pnl_1.add (color_mesa); pnl_1.add (lbl_3); color_lampara.add ("Negro"); color_lampara.add ("Rojo"); color_lampara.add ("Azul"); color_lampara.add ("Verde"); pnl_1.add (color_lampara); // oidores color_mesa.addItemListener ( new java.awt.event.ItemListener () { public void itemStateChanged (ItemEvent e) { mc.cambiaColorMesa (color_mesa.getSelectedIndex ()); } }); color_lampara.addItemListener ( new java.awt.event.ItemListener () { public void itemStateChanged (ItemEvent e) { mc.cambiaColorLampara (color_lampara.getSelectedIndex ()); } }); } }
71

class miCanvas extends Canvas { int color_mesa = 0; int color_lampara = 0; public void paint (Graphics g) { switch (color_mesa) { case 0 : g.setColor (Color.black); break; case 1 : g.setColor (Color.red); break; case 2 : g.setColor (Color.blue); break; case 3 : g.setColor (Color.green); break; } g.fillRect (0, 250, 290, 290); // dibuja la mesa switch (color_lampara) { case 0 : g.setColor (Color.black); break; case 1 : g.setColor (Color.red); break; case 2 : g.setColor (Color.blue); break; case 3 : g.setColor (Color.green); break; } g.drawLine (125, 250, 125, 160); g.drawLine (175, 250, 175, 160); // dibuja la base g.drawArc (85, 157, 130, 50, -65, 312); // dibuja la cubierta g.drawArc (85, 87, 130, 50, 62, 58); g.drawLine (85, 177, 119, 89); g.drawLine (215, 177, 181, 89); g.fillArc (78, 120, 40, 40, 63, -174); // dibuja las motas g.fillOval (120, 96, 40, 40); g.fillArc (173, 100, 40, 40, 110, 180); } public void cambiaColorMesa (int c) { color_mesa = c; repaint (); } public void cambiaColorLampara (int c) { color_lampara = c; repaint (); } }

7.8.3 reas de texto


Un rea de texto es un componente que se utiliza para leer datos de entrada de manera semejante al campo de texto. La diferencia es que, mientras el campo de texto puede leer solo una lnea de texto, el rea de texto puede leer varias. Si el texto es ms grande que el tamao con el que se despliega el rea de texto automticamente se colocan barras de scroll horizontales o verticales segn sea el caso.

72

Para utiliza un rea de texto hay que crear un objeto de tipo TextArea pasando como argumentos al constructor en forma opcional un string inicial, un tamao preferido de desplegado en renglones y columnas y un indicador para que despliegue o no barras de scroll de inicio. TextArea ta1 = new TextArea (); // el tamao se deja al diseo TextArea ta2 = new TextArea (10, 60); // 10 renglones y 60 columnas TextArea ta3 = new TextArea ("hola mundo", 10, 60); // texto inicial TextArea ta4 = new TextArea ("hola", 10, 60, TextArea.SCROLLBARS_NONE); A pesar de que el texto est en varias lneas, el rea de texto lo trata como un string, es decir, como si fuera una sola cadena de caracteres. Cuando desde un programa se altera el texto de un rea de texto es responsabilidad del programador insertar saltos de lnea ('\n') dentro del string para que el texto se visualice por renglones. El texto capturado se puede recuperar llamando al mtodo getText. String s = ta1.getText (); Se puede asignar texto desde el programa con el mtodo setText. ta2.setText ("Esto es una lnea\nEsto es otra lnea"); El siguiente ejemplo presenta un applet con dos reas de texto. Despus de escribir un texto cualquiera en la primera rea de texto, al oprimir el botn de Cuenta, la segunda rea de texto despliega el nmero de lneas y de caracteres que se escribieron. La figura muestra la salida del programa.

73

import java.awt.*; import java.awt.event.*; import java.applet.*; public class ta extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); TextArea ta1 = new TextArea ("Escribe algo"); TextArea ta2 = new TextArea ("Soy una textarea", 5, 25); Button btn = new Button ("Cuenta"); public void init () { setLayout (gbl); // text area de arriba gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 100; gbc.weighty = 33; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (ta1, gbc); add (ta1); // boton gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 33; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (btn, gbc); add (btn); // text area de abajo gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 33; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (ta2, gbc); add (ta2); btn.addActionListener ( new java.awt.event.ActionListener () { public void actionPerformed (ActionEvent e) { String s = ta1.getText (); int k = 0; for (int i = 0; i < s.length (); i++) if (s.charAt (i) == '\n') k++; ta2.setText ("Escribiste\n" + k + " renglones\n" + s.length () + " caracteres");
74

} }); } }

7.8.4 Listas
Una lista es similar al choice que permite seleccionar de entre varias opciones de una lista con dos diferencias: su presentacin es en forma de lista y permite seleccionar ms de una opcin. Para usar una lista hay que crear un objeto de tipo List pasando como argumentos opcionales el nmero de opciones que estarn visibles y un indicador si la liste permite o no opciones mltiples. List lst1 = new List (); // permite escoger una opcin List lst2 = new List (5); // cinco opciones visibles a la vez List lst3 = new List (5, true); // permite opciones mltiples Las opciones se agregan con el mtodo add. lst2.add ("opcin uno"); lst2.add ("opcin dos"); En las listas que permiten la seleccin de nicamente un elemento el mtodo getSelectedIndex devuelve la posicin del elemento seleccionado. int opcin = lst1.getSelectedIndex (); En las listas que permiten mltiples selecciones el mtodo getSelectedIndexes devuelve un arreglo de enteros con las posiciones de los elementos seleccionados. int opciones[] = lst3.getSelectedIndexes (); El listado ejemplo presenta un applet con un choice, una lista que permite seleccionar un solo elemento y una lista que permite seleccin mltiple. Al oprimir el botn, en la parte inferior se despliegan las opciones que fueron seleccionadas en el choice y en las listas. La figura muestra la salida del programa.

75

import java.awt.*; import java.awt.event.*; import java.applet.*; public class lst extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Choice chs_1 = new Choice (); List lst_1 = new List (); // una sola seleccion List lst_2 = new List (4, true); // seleccion multiple Button btn_revisar = new Button ("Revisar"); Label lbl_1 = new Label ("En el choice la opcion es: "); Label lbl_2 = new Label ("En la lista 1 la opcion es: "); Label lbl_3 = new Label ("En la lista 2 las opciones son: "); public void init () { setLayout (gbl); // choice chs_1.add ("opcion 1"); chs_1.add ("opcion 2"); chs_1.add ("opcion 3"); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 33; gbc.weighty = 40; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (chs_1, gbc); add (chs_1); // lista 1 lst_1.add ("opcion 1"); lst_1.add ("opcion 2"); lst_1.add ("opcion 3"); lst_1.add ("opcion 4"); lst_1.add ("opcion 5"); gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 33; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (lst_1, gbc); add (lst_1); // lista 2 gbc.gridx = 2; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 33; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (lst_2, gbc); lst_2.add ("opcion 1"); lst_2.add ("opcion 2"); lst_2.add ("opcion 3"); lst_2.add ("opcion 4"); add (lst_2); // boton de revisar gbc.gridx = 0; gbc.gridy = 1;
76

gbc.gridwidth = 3; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 20; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (btn_revisar, gbc); add (btn_revisar); // etiqueta 1 gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 40; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (lbl_1, gbc); add (lbl_1); // etiqueta 2 gbc.gridx = 1; gbc.gridy = 2; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (lbl_2, gbc); add (lbl_2); // etiqueta 3 gbc.gridx = 2; gbc.gridy = 2; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (lbl_3, gbc); add (lbl_3); // oidor btn_revisar.addActionListener ( new java.awt.event.ActionListener () { public void actionPerformed (ActionEvent e) { lbl_1.setText ("En el choice la opcion es: " + chs_1.getSelectedIndex ()); lbl_2.setText ("En la lista 1 la opcion es: " + lst_1.getSelectedIndex ()); int idx[] = lst_2.getSelectedIndexes (); StringBuffer mssg = new StringBuffer ("En la lista 2 las opciones son: "); for (int i = 0; i < idx.length; i++) mssg.append (idx[i] + " "); lbl_3.setText (mssg.toString ()); } }); } }
77

7.8.5 Frames
Un frame es una ventana que, dependiendo del sistema operativo, cuenta con un ttulo, un men de barra, indicadores para minimizar, maximizar o cerrar, y otras caractersticas tpicas de las ventanas. Un frame es un componente contenedor y como tal puede tener su propio diseo y contener otros componentes de AWT. Para usar un frame hay que crear un objeto de tipo Frame pasando como parmetro el ttulo de la ventana. Frame frm = new Frame ("titulo de la ventana"); El frame puede tener su propio diseo y componentes. frm.setLayout (new GridLayout (4, 1)); frm.add (new Button ("soy un botn")); frm.add (new Label ("soy una etiqueta")); Se le puede asignar un tamao en pixeles usando el mtodo setSize o indicarle las coordenadas de su esquina superior izquierda con setLocation. frm.setSize (400, 300); // 400 pixeles de ancho y 300 pixeles de alto frm.setLocation (200, 100); // 200 en x y 100 en y Cuando se crea un nuevo frame, ste es invisible. El mtodo setVisible hace que el frame sea visible o de vuelta invisible mediante un argumento booleano que puede valer true (la ventana se hace visible) o false (el frame se hace invisible). frm.setVisible (true); // hace visible el frame El mtodo dispose elimina el frame y libera la memoria. Se invoca cuando el frame ya no va a ser utilizado. frm.dispose (); Por default el indicador de cerrar ventana, que en Windows tiene una marca de cruz y est en la parte superior derecha del frame, esta inactivo. Es necesario crear un oidor de la clase WindowAdapter y registrarlo usando el mtodo addWindowListener para indicarle al frame que se destruya al oprimir ese indicador. frm.addWindowListener ( new java.awt.event.WindowAdapter () { public void windowClosing (WindowEvent e) { frm.dispose (); } }); El siguiente ejemplo presenta un applet que crea un frame, le agrega una etiqueta, un campo de texto y un botn y luego lo hace visible. La figura muestra la salida de este programa. import java.awt.*; import java.awt.event.*; import java.applet.*; public class frm extends Applet {
78

Frame frm_1 = new Frame ("Mi ventanita"); Panel pnl_1 = new Panel (); GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Label lbl_nombre = new Label ("Nombre"); TextField tf_nombre = new TextField (20); Button btn_ok = new Button ("OK"); Label lbl_applet = new Label ("aqui es el applet"); public void init () { // layout del applet setBackground (Color.lightGray); setLayout (new FlowLayout (FlowLayout.LEFT)); add (lbl_applet); // layout de la ventana frm_1.setLayout (gbl); // etiqueta de nombre gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 30; gbc.weighty = 80; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (lbl_nombre, gbc); frm_1.add (lbl_nombre); // textfield de nombre gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 70; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (tf_nombre, gbc); frm_1.add (tf_nombre); // boton de ok gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 2; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 20; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (btn_ok, gbc); frm_1.add (btn_ok); frm_1.setLocation (100, 150); frm_1.setSize (400, 300); frm_1.setVisible (true); // oidor frm_1.addWindowListener ( new java.awt.event.WindowAdapter () {
79

public void windowClosing (WindowEvent e) { frm_1.dispose (); } }); } }

7.8.6 Dilogos
Un dilogo es semejante a un frame en el sentido que es una ventana que puede tener su propio diseo y contener a otros componentes. Las diferencias son que los dilogos necesitan tener un frame ligado a ellos, no tienen indicadores de maximizar ni de minimizar y pueden operar en forma modal. Un dilogo modal impide que sean accesadas otras ventanas hasta que el dilogo sea cerrado. Por estos motivos y por lo general, los dilogos se utilizan en forma modal y para avisar de errores en el programa o pedir datos al usuario. Para usar un dilogo hay que crear un objeto de tipo Dialog pasando como argumento obligatorio el frame al cual estn ligados y opcionalmente un argumento string con el ttulo del dilogo y una variable booleana para indicar si el dilogo es modal o no modal. Dialog dlg1 = new Dialog (frame); // el dilogo es no modal por default Dialog dlg2 = new Dialog (frame, true); // el dilogo es modal Dialog dlg3 = new Dialog (frame, "titulo", true); // tiene ttulo y es modal Los mtodos setLayout, add, setSize, setLocation, setVisible, dispose y addWindowListener
80

tienen la misma funcionalidad que en los frames. Para que un applet pueda usar un dilogo necesita hacer referencia a la ventana del navegador que lo contiene. Una forma de conseguirlo es obtener los componentes padres del applet, usando el mtodo getParent, hasta encontrar un objeto de tipo Frame y pasar como argumento ese objeto al constructor del dilogo. Object tmp = getParent (); while (! (tmp instanceof Frame)) tmp = ((Component) tmp).getParent (); dlg1 = new Dialog ((Frame) tmp, "ttulo", true); El ejemplo presenta un applet que abre un dilogo modal y por lo tanto se bloquea. Al cerrar el dilogo el programa puede continuar y abre un frame. La figura muestra la salida del programa antes de cerrar el dilogo.

import java.awt.*; import java.awt.event.*; import java.applet.*; public class dlgm extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Label lbl_error = new Label ("El frame no se abrira hasta cerrar este dialogo"); Label lbl_hola = new Label ("Hola"); Button btn_ok = new Button ("Cerrar"); Frame frm_1 = new Frame ("Ventanita"); Dialog dlg_1; public void init () { // Dialog Object tmp = getParent (); while (! (tmp instanceof Frame)) tmp = ((Component) tmp).getParent (); dlg_1 = new Dialog ((Frame) tmp, "Dialogo modal", true);
81

dlg_1.setLayout (gbl); // Etiqueta de error gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 100; gbc.weighty = 90; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (lbl_error, gbc); dlg_1.add (lbl_error); // Boton de ok gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 10; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (btn_ok, gbc); dlg_1.add (btn_ok); // Agrega los oidores btn_ok.addActionListener ( new java.awt.event.ActionListener () { public void actionPerformed (ActionEvent e) { dlg_1.dispose (); } }); dlg_1.pack (); dlg_1.setLocation (90, 120); dlg_1.setResizable (false); dlg_1.setVisible (true); // Frame frm_1.setForeground (Color.green); frm_1.setLayout (new BorderLayout ()); lbl_hola.setFont (new Font ("Times Roman", Font.PLAIN, 36)); frm_1.add ("Center", lbl_hola); frm_1.setLocation (100, 150); frm_1.setSize (400, 300); frm_1.setVisible (true); frm_1.addWindowListener ( new java.awt.event.WindowAdapter () { public void windowClosing (WindowEvent e) { frm_1.dispose (); } }); }}
82

8.0 Gestores de diseo

Introduccin
A lo largo de este captulo, vamos a continuar con la creacin de interfaces de usuario en Java, vamos a tratar dos caractersticas del interfaz de usuario en los programas Java, los gestores de diseo y los eventos. Lo que vamos a ver en este captulo es aplicable tanto a componentes AWT como a componentes Swing (que los veremos en el siguiente captulo), aunque muchas de las clases que implementan los gestores de diseo y el tratamiento de eventos se encuentran dentro del paquete JAVA.AWT y del subpaquete java.awt.events. Los eventos y gestores de diseo especficos de los componentes Swing los veremos en los prximos captulos en los que se abordar Swing, en ste veremos lo comn a los dos tipos de componentes.

8.1 Gestores de diseo


Los gestores de diseo (Layout Managers) tienen una gran importancia en el leguaje Java, para poder incorporar controles en una ventana (ya sea de una aplicacin o de un applet), es necesario que estos estn contenidos en un gestor de diseo, de otro modo no funcionarn correctamente. Comencemos por lo tanto, viendo qu es un gestor de diseo desde tres puntos de vista diferentes: Desde el punto de vista del lenguaje, como ya hemos comentado, un gestor de diseo es un "estilo de visualizacin", es decir, es el mecanismo que el lenguaje incorpora para conseguir crear aplicaciones que tengan la misma apariencia a travs de las diferentes plataformas en las que stas pueden ejecutarse. Desde el punto de vista de la jerarqua de clases de Java, un gestor de diseo es un interfaz, y deriva directamente de la clase Object, que es la que se encuentra en la raz de la jerarqua de clases. Sin embargo, es a travs de la clase Container como podemos establecer el o los gestores de diseo que la ventana va a utilizar para implementar los componentes que sta manejar. El gestor de diseo es parte del Container, de hecho, siempre que se crea un Container, se puede especificar con cual o cuales de los gestores de diseo disponibles en Java se quiere trabajar, o dicho de otro modo, qu gestor de diseo se asignar al contenedor. Desde el punto de vista operativo, un Gestor de diseo es una propiedad del contenedor, que puede almacenar cualquier tipo de objeto que derive de la clase Container (normalmente bien controles o bien otros gestores de diseo). El Container acta de intermediario entre la ventana y los componentes que l almacena pasndoles a estos los mensajes que a l le llegan desde la ventana. El modo de interactuar con un gestor de diseo es muy simple: basta con especificar con cual de los disponibles queremos trabajar y aadirle componentes. Veamos esto de una forma ms detenida. Puesto que existen diferentes gestores de diseo incluidos en el lenguaje Java y puesto que adems podemos construir nuestros propios gestores de diseo, se hace necesario indicar con cual de los disponibles deseamos trabajar. Lo que se hace pasndole al mtodo setLayout() de la clase Container. un objeto del tipo deseado de gestor de diseo con el que se desea trabajar. Veamos, por ejemplo, la siguiente lnea de cdigo:
this.setLayout(new FlowLayout());

El operador new devuelve un gestor de diseo del tipo FlowLayout, ste objeto es recogido por el mtodo setLayout() de la clase Container, quien a partir de ahora ya sabe con qu gestor deseamos trabajar.

83

La clase Container dispone del mtodo add(), que permite aadir componentes. Como ya hemos comentado, a un objeto de la clase Container le podemos aadir bien controles o bien otros gestores de diseo. Para ello algo tan simple como lo que aparece aqu:
this.add( new Button( "Uno" ) );

Los layouts por defecto para los contenedores de swing son los mismos que para sus homnimos de AWT. Si queremos cambiar el layout manager de un contenedor en un momento dado, tan slo tendremos que llamar al mtodo: contenedor.setLayout(LayoutManager layout); Si en cualquier momento decidimos que estamos hartos de un layout manager y queremos encargarnos nosotros mismos de la gestin de componentes tan slo tendremos que escribir: contenedor.setLayout(null); Los componentes como hemos dicho son objetos que forman parte de nuestro interfaz grfico. Cada componente tiene asignada una coordenada horizontal, una coordenada vertical, una longitud y una anchura determinadas. Estos valores sern los que se utilizarn para renderizar el componente en pantalla. La clase java.awt.Component nos ofrece una serie de mtodos para poder modificar los valores de los atributos anteriores: public void setSize(Dimension size); public void setBounds(Rectangle r); Hay pequeas variaciones de estos mtodos pero su funcin es la misma. Por su parte la clase javax.swing.JComponent tiene mtodos diferentes: public void setPreferredSize(Dimension size); public void setMinimumSize(Dimenstion size); public void setMaximumSize(Dimension size); Es muy frustrante, cuando conoces el funcionamiento de los layouts, ver que aunque utilizas estos mtodos, el intrprete Java no les hace ningn caso y los objetos muestran un tamao que nosotros no queramos. Veamos un ejemplo: .... JButton button = new JButton(); button.setMaximumSize(new Dimension(80,25)); JFrame frame = new JFrame(); frame.getContentPane().add(button); frame.setSize(400,300); frame.setVisible(true); ....
84

Aunque no lo he dicho, todos los layout managers implementan la interfaz LayoutManager directa o indirectamente (a travs de LayoutManager). Si creamos un pequeo programa que contenga las lneas de cdigo anteriores y lo ejecutamos, inicialmente podramos pensar que se ver un botn de 80 puntos de largo por 25 de ancho. Sin embargo, tras ejecutar el programa veremos como nuestro botn es considerablemente ms grande. Qu es lo que ha pasado? Por qu no nos ha hecho caso? La realidad es que cuando utilizamos layout managers no sirve de nada intentar establecer a la fuerza el tamao de los componentes ya que ser el layout el que siempre tenga la ltima palabra1. En el ejemplo anterior, si que se establece el tamao mximo de 80x25 para el botn, lo que pasa es que despus de aadirlo al frame es el layout manager de ste el que toma el control del interfaz y el que pasa a decidir el nuevo tamao del botn, por lo que nuestra llamada a setMaximumSize() no hace ningn efecto. El ltimo aspecto importante a tener en cuenta es el significado del atributo preferredSize. Este atributo indica el tamao que a un componente le gustara tener. Este tamao suele ser dependiente de la plataforma y el layout manager intentar respetarlo siempre que sea posible y siempre que lo permitan sus polticas de layout.

A continuacin vamos a comentar los distintos gestores de diseo que podemos encontrar dentro del paquete JAVA.AWT. En los distintos ejemplos se han utilizado botones para poner de manifiesto las diferentes distribuciones que los distintos gestores de diseo hacen del espacio ya que el gestor es invisible. Los cinco gestores de diseo son los que se comentan a continuacin.

8.1.1 FlowLayout
El gestor de diseo FlowLayout, adems de ser el gestor de diseo por defecto de los applets, es tambin el ms simple de todos. Este gestor, distribuye los controles de izquierda a derecha llenando el ancho del contenedor de arriba a abajo. Es decir, cuando acaba con una fila de controles, sigue con la siguiente, actuando con los controles como lo hace un editor de textos con las palabras. Es decir, los controles fluyen (Flow). Ejemplo:
import java.awt.*; import javax.swing.*; import java.awt.event.*; class EjemploFlowLayout extends JFrame{ public EjemploFlowLayout( ) { Container c = getContentPane ( ) ; JButton b1 = new JButton ( "A" ) ; JButton b2 = new JButton ( "B" ) ; JButton b3 = new JButton ( "Botn largo " ) ; JButton b4 = new JButton ( "D" ) ; JButton b5 = new JButton ( "E" ) ; c.setLayout (new FlowLayout ( ) ) ; c.add ( b1 ) ; c.add ( b2 ) ; c.add ( b3 ) ; c.add ( b4 ) ; c.add ( b5 ) ; }
85

public static void main (String [ ] args ) { EjemploFlowLayout f = new EjemploFlowLayout ( ) ; f.setSize (200 ,200) ; f.setVisible(true); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0);} }); } }

Como se puede observar, se utiliza primero el mtodo setLayout() pasndole por parmetro una instancia del gestor de diseo que se va a utilizar en nuestra ventana. En el bucle se van aadiendo botones a la ventana, en este caso se utiliza el mtodo add() de la clase Container, en la versin que ya conocemos, es decir pasndole por parmetro la instancia del componente AWT que queremos aadir.

8.1.2 BorderLayout
BorderLayout es el layout manager por defecto para frames por lo que al igual que FlowLayout su aprendizaje es indispensable. BorderLayout divide el espacio de un contenedor en cinco regiones diferentes. Estas regiones son: North, South, East, West y Center, y se corresponden con su situacin dentro del contenedor en el que se encuentran. Veamos ms claramente lo que quiere decir esto con un ejemplo sencillo: import javax.swing.*; import java.awt.*; public class TestBorderLayout extends JFrame { public static void main(String[] args) { TestBorderLayout frame = new TestBorderLayout(); Container panel = frame.getContentPane(); JButton norte = new JButton("Norte"); JButton sur= new JButton("Sur"); JButton este= new JButton("Este"); JButton oeste = new JButton("Oeste"); JButton centro = new JButton("Centro"); panel.add(norte, BorderLayout.NORTH); panel.add(sur, BorderLayout.SOUTH); panel.add(este, BorderLayout.EAST); panel.add(oeste, BorderLayout.WEST); panel.add(centro, BorderLayout.CENTER); frame.setSize(400,300); frame.setTitle("Prueba de BorderLayoutLayout"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }

86

Como podemos ver en el cdigo f uente anterior, al aadi r un elemento al BorderLayout tenemos que especificar la regin en la cual lo queremos aadir. Si no especificamos ninguna regin por defecto el componente se inserta en el centro del contenedor. Aunque ya las hemos visto en el cdigo las resumiremos de nuevo:

BorderLayout.NORTH BorderLayout.SOUTH BorderLayout.EAST BorderLayout.WEST BorderLayout.CENTER // Por defecto Existe una manera alternativa de especificar donde queremos colocar los componentes y es pasando como parmetros al mtodo add en lugar de los atributos anteriores las cadenas siguientes: North South East West Center Este mtodo no est recomendado porque es demasiado propenso a errores, ya que si nos equivocamos en una simple letra el compilador no nos avisar de nuestro error y por lo tanto podremos llevarnos sorpresas al ejecutar el programa. Si insertamos un componente en una regin donde haba otro componente previamente, el que se encontraba en el contenedor desaparecer y el nuevo ocupar su lugar. Por lo tanto tenemos que tener cuidado con donde insertamos los componentes. Para finalizar con este layout manager vamos a hablar de como trata el tamao de los componentes. Cuando aadimos un componente en las posiciones norte o sur, BorderLayout respeta su alto mientras que la longitud del mismo se ajusta hasta ocupar todo el ancho del contenedor. Con los componentes de las posiciones este y oeste pasa lo contrario, Border-Layout respeta su longitud y ajusta su altura hasta que ocupe la totalidad de la altura del contenedor o hasta que se encuentre con los componentes del norte o del sur. Por ltimo el objeto que se situe en la zona central ocupar el resto de espacio disponible. BorderLayout, por su estructura, es muy til para muchas aplicaciones. Por ejemplo, sera bastante normal colocar una barra de herramientas en el panel norte de nuestra ventana, una barra de estado en el panel sur y quizs un rbol de navegacin en el panel izquierdo o derecho, dejando el panel central para el resto del interfaz.

87

8.1.3 CardLayout
CardLayout es un layout manager ligeramente diferente a todos los dems ya que tan slo muestra en un instante dado un nico componente. Un contenedor que tenga asignado un CardLayout podr tener cualquier nmero de componentes en su interior pero slo uno se ver en un instante dado. En este layout manager los componentes ocuparn todo el tamao disponible en el contenedor. Los componentes a medida que se insertan en el contenedor van formando una secuencia. Para seleccionar el componente que queremos mostrar en cada momento disponemos de varios mtodos: public void first(Container contenedor); public void last(Container contenedor); public void next(Container contenedor); public void previous(Container contenedor); public void show(Container container, String nombre); El mtodo ms comn para aadir un componente es: public void add(Component componente, String nombre); Este mtodo inserta un componente dentro de un contenedor y le asigna un nombre, este nombre lo podremos utilizar con el mtodo show para mostrar el componente directamente. Por ltimo, al aadir componentes tendremos que fijarnos en que el orden en el que los aadamos al contenedor ser el orden en el que sern recorridos por el layout manager. Veamos un ejemplo simple: import javax.swing.*; import java.awt.*; import java.awt.event.*; public class TestCardLayout extends JFrame { public static void main(String[] args) { TestCardLayout frame = new TestCardLayout(); Container container = frame.getContentPane(); JButton siguiente = new JButton(siguiente); container.add(siguiente, BorderLayout.NORTH); JLabel label1 = new JLabel(Componente 1); JLabel label2 = new JLabel(Componente 2); JLabel label3 = new JLabel(Componente 3); JLabel label4 = new JLabel(Componente 4); JPanel panelComponentes = new JPanel(); CardLayout layout = new CardLayout(); panelComponentes.setLayout(layout); panelComponentes.add(label1,1); panelComponentes.add(label2,2); panelComponentes.add(label3,3); panelComponentes.add(label4,4); container.add(panelComponentes, BorderLayout.CENTER); siguiente.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { layout.next(panelComponentes); } }); frame.setSize(400,300); frame.setTitle("Prueba de BorderLayoutLayout"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
88

frame.setVisible(true); } } El ejemplo es muy sencillo y muestra un contenedor con varias etiquetas. Con un botn podemos ir avanzando de etiqueta. Al pulsar el botn se muestra la etiqueta siguiente llamando al mtodo next() de CardLayout. Este layout manager es muy sencillo y muy til especialmente cuando tenemos un panel que variar su contenido en funcin de alguna parte de nuestro interfaz (una combo box por ejemplo). En lugar de eliminar el panel e insertar otro nuevo, o en lugar de eliminar los componentes e insertar otros nuevos, podemos utilizar un CardLayout que nos ahorra gran cantidad de trabajo.

Este tercer gestor permite manejar varias "fichas intercambiables", de forma tal que slo una est visible a la vez y ocupando todo el rea del contenedor. Es similar a los controles de pestaa o "tabs" de Windows. El problema que este gestor de diseo tiene es que la implementacin que en Java se ha hecho de l, no es completa, y las fichas (o tarjetas si se prefiere) no tienen la correspondiente pestaa asociada, por lo que no existe una forma preestablecida de interactuar con los controles para cambiar de unas fichas a otras; siendo necesario crear este mecanismo programticamente con los inconvenientes de prdida de tiempo y esfuerzo que esto supone. En el captulo siguiente veremos que los componentes Swing ofrecen una clase llamada JTabbedPane que representa un componente en el que se pueden elegir las distintas fichas (paneles) a travs de los paneles que presenta.

8.1.4 GridLayout
GridLayout divide el espacio de un contenedor en forma de tabla, es decir, en un conjunto de filas y columnas. Cada fila y cada columna tiene el mismo tamao y el rea del contenedor se distribuye equitativamente entre todas las celdas. De todo esto se deduce que GridLayout no respetar el tamao preferido de los componentes que insertemos en cada una de las celdas. El nmero de filas y columnas se especifica en el constructor. Si pasamos cero como el nmero de filas o columnas el layout manager ir creando las filas y columnas en funcin del nmero de componentes y del valor de la otra dimensin, es decir, si creamos un GridLayout con cero filas y tres columnas e insertamos cuatro componentes el GridLayout ser lo suficientemente inteligente como para saber que tiene que crear dos filas. Veamos un ejemplo simple de funcionamiento: import javax.swing.*; import java.awt.*; public class TestGridLayout extends JFrame { public static void main(String[] args) { TestGridLayout frame = new TestGridLayout(); Container container = frame.getContentPane(); int X = 3; int Y = 3; container.setLayout(new GridLayout(X,Y)); for (int i = 0; i < X; i++) {
89

for (int j = 0; j < Y; j++) { container.add(new JButton(i + x + j)); } } frame.setSize(400,300); frame.setTitle("Prueba de BorderLayoutLayout"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }

En la figura anterior vemos el resultado de ejecutar el ejemplo. Como se puede ver todas las celdas tienen el mismo tamao y los botones ocupan la totalidad de la celda. GridLayout es un layout manager realmente muy simple y como tal su utilidad se encuentra bastante reducida. Suele ser til a la hora de crear partes del interfaz de usuario que necesiten representar una matriz de componentes o incluso para interfaces que tengan en si una forma matricial. Lo realmente interesante sera que algunas celdas pudiesen tener tamaos diferentes a las dems, o que una celda pudiese ocupar varias posiciones, o dejar celdas vacas, etc... Afortunadamente, esto es justamente lo que nos permite el prximo layout manager que veremos, GridBagLayout.

8.1.5 GridBagLayout
GridBagLayout es el layout manager ms poderoso y eficaz con mucha diferencia. Con GridBagLayout podemos imitar facilmente el comportamiento del resto de layout managers a parte de poder crear con el interfaces mucho ms complejas. Ventajas y desventajas GridBagLayout es el layout manager que ms pavor causa entre los programadores Java. Odiado por unos y alabado por otros, este layout manager proporciona una serie de ventajas sobre el resto: Permite la creacin de interfaces de usuario complejos. Con este layout manager tenemos control absoluto sobre las posiciones que ocuparn los objetos en el interfaz final.
90

Las interfaces construidas son ms ligeras. Cuando queremos crear un interfaz de usuario combinando el resto de layout managers vistos hasta el momento a menudo terminamos con un nmero grande de paneles anidados. Los paneles son objetos bastante pesados y tener una gran cantidad de los mismos puede influir perjudicialmente en el rendimiento de nuestro programa. Con GridBagLayout se pueden crear interfaces exactamente iguales pero con un nico panel con lo que nuestra interfaz ser mucho ms ligera. Pero como todo, tambin tiene sus inconvenientes: Requiere un tiempo de aprendizaje bastante grande. No slo es necesario comprender su funcionamiento sino que tambin es necesario haber hecho bastantes ejemplos para llegar a dominarlo. El cdigo necesario para crear un interfaz de usuario es considerablemente ms grande que con el resto de layout managers y adems suele ser un cdigo bastante complicado y dficil de comprender y por lo tanto de mantener. En la ltima parte de este apartado veremos como podemos crear una serie de clases auxiliares con las que solucionaremos estos dos inconvenientes y que harn que utilizar GridBag- Layout sea un juego de nios. GridBagLayout a fondo GridBagLayout basa su funcionamiento en una clase auxiliar que establece restricciones a los componentes, GridBagConstraints. Estas restricciones especifican exactamente como se mostrar cada elemento dentro del contenedor. La clase GridBagConstraints posee bastantes atributos que nos permiten configurar el layout de un contenedor a nuestro gusto. A continuacin vamos a ver los atributos importantes; no voy a mostrar la totalidad de atributos para no complicar las cosas a los menos experimentados. Vamos a ir viendo los atributos ms importantes de esta clase y de una forma grfica para que se entienda mejor: gridx y gridy Estos dos atributos especifican las coordenadas horizontal y vertical del componente que vamos a insertar en el grid. Realmente no siempre es necesario establecer su valor ya que en los casos ms simples nos llegara con gridwidth y gridheight, sin embargo la experiencia dice que poner este atributo es recomendable ya que permite saber en que elemento nos encontramos de una manera visual. La siguiente figura muestra grficamente lo que indican los atributos gridx y gridy:

gridwidth y gridheight Este otro par de elementos junto con gridx y gridy son la base de GridBagLayout. Comprendiendo a la perfeccin su significado no habr ningn interfaz que se nos resista. Bsicamente lo que indican
91

gridwidth y gridheight es el nmero de celdas que ocupar un componente dentro del GridBagLayout, su valor puede ser: Un nmero cardinal, en este caso indica exactamente el nmero de filas o columnas que ocupar el componente. GridBagConstraints.RELATIVE, indica que el componente ocupar el espacio disponible desde la fila o columna actual hasta la ltima fila o columna disponibles. GridBagConstraints.REMAINDER, indica que el componente es el ltimo de la fila actual o columna actual.

Analicemos la figura anterior para comprender el significado de estos dos atributos. En la primera fila tenemos tres componentes. Todos los componentes ocupan una celda en horizontal y en vertical luego su gridwidth y gridheight ha de ser igual a uno. En el segundo y tercer componente en lugar de ponerle uno como valor de gridwidth hemos de poner RELATIVE y REMAINDER. Tcnicamente slo sera necesario poner REMAINDER ya que es el nico que necesita conocer el GridBagLayout para saber que se ha acabado la fila.1 En la segunda fila tenemos dos componentes. Ambos tienen como gridheight RELATIVE ya que se encuentran en la penltima fila. El primer componente tiene de gridwidth REMAINDER, es decir, ocupar todo el espacio hasta el ltimo componente. En la figura se puede ver como se cumple esto que acabo de decir ya que el componente abarca dos celdas y la ltima se deja para el ltimo cuyo gridwidth es RELATIVE. 1.Siempre y cuando no se use gridx y gridy. c.gridwidth=1 c.gridheight=1 c.gridwidth=GridBagConstraints.RELATIVE c.gridheight=1 c.gridwidth=GridBagConstraints.REMAINDER c.gridheight=1 c.gridwidth=GridBagConstraints.RELATIVE c.gridheight=GridBagConstraints.RELATIVE
92

c.gridwidth=GridBagConstraints.REMAINDER c.gridheight=GridBagConstraints.REMAINDER c.gridwidth=GridBagConstraints.REMAINDER c.gridheight=GridBagConstraints.RELATIVE Por ltimo en la tercera fila tan slo tenemos un componente con gridwidth REMAINDER y gridheight REMAINDER. Como veis es bastante sencillo, gridwidth y gridheight especifican el nmero de celdas horizontal y vertical que abarcar un componente. Adems podemos utilizar los valores especiales REMAINDER y RELATIVE para indicar que un componente ha de ocupar todo el espacio restante o todo el espacio hasta el ltimo componente. anchor Este atributo es mucho ms sencillo; anchor especifica la posicin que ocupar un componente dentro de una celda. Los valores que puede tomar este atributo estn definidos como variables estticas dentro de la clase GridBagConstraints y son: NORTH, SOUTH, EAST, WEAST, NORTHWEST, SOUTHWEST, NORTHEAST, SOUTHEAST y CENTER. Como intuiris indican la orientacin de los componentes dentro de la celda que ocupan. Veamos una figura que nos aclare las cosas:

En la imgen anterior las lneas punteadas marcan el espacio que ocupa cada una de las celdas del contenedor. En este ejemplo he colocado cada uno de los componentes con un anchor diferente para que podis apreciar fcilmente el efecto que tiene este atributo. fill El atributo fill especifica el espacio que ocupar el componente dentro de la celda. Los valores que puede tomar tambin son variables estticas de la clase GridBagConstraints: NONE: El componente ocupar exactamente el tamao que tenga como preferido HORIZONTAL: El componente ocupar todo el espacio horizontal de la celda mientras que su altura ser la que tenga como preferida. VERTICAL: El componente ocupar todo el espacio vertical de la celda mientras que su longitud ser la que tenga como preferida. BOTH: El componente ocupar la totalidad de la celda Vemoslo en una imgen:

93

Como antes, las lneas marcan los bordes de las celdas. Como se puede apreciar en la figura segn el valor del atributo anchor los componentes abarcarn ms o menos espacio. weightx y weighty Estos dos atributos son la clave de los dolores de cabeza que GridBagLayout le da a tanta gente. Como habis podido observar hasta ahora no ha habido nada complicado. Todos los atributos que hemos visto son bastante sencillos de comprender y prcticamente ya estaramos en disposicin de crear un interfaz complejo con GridBagLayout. Sin embargo existe un pequeo problema que veremos a continuacin. A medida que vamos aadiendo componentes a un contenedor el layout manager va determinando en funcin del tamao de los componentes el espacio que ocupan las celdas. Hay que tener mucho cuidado porque al contrario de lo que pueda parecer si no indicamos nada las celdas no ocuparn la totalidad del contenedor. Fijaros en la figura anterior, en este caso las celdas si que ocupan todo el contenedor, pero eso es debido a que he utilizado los atributos weightx y weighty. Mirar lo que sucede si hago el mismo ejemplo anterior sin estos atributos:

He puesto las lneas que marcan el espacio ocupado por las celdas en rojo para que se vea mejor. Como veis las celdas tienen de largo la longitud mxima de los componentes y de alto la altura mxima de los componentes. Esto suele desconcertar a la gente ya que por mucho que se utilicen correctamente los otros atributos este comportamiento por defecto acaba por hacer que nuestros interfaces no salgan como planeabamos. Cmo hacemos entonces para que las celdas ocupen la totalidad del contenedor?, la respuesta como caba esperar son los atributos weightx y weighty.

94

Los atributos weightx y weighty especifican el porcentaje de espacio libre que ocupar una celda eterminada. En el ejemplo anterior una vez aadidos los componentes queda una determinada cantidad de espacio libre tanto horizontal como vertical. Veamoslo:

Este espacio libre (flechas) se dividir entre todas las celdas que especifiquen valores dentro de los atributos weightx y weighty. La forma de especificar el espacio que quiere ocupar cada componente es mediante un nmero entre 0.0 y 1.0. Este nmero representa al porcentaje de espacio libre que ocupar cada celda. Un ejemplo: Espacio libre 250 puntos en horizontal y 150 en vertical Componente 1: c.weightx=1.0, c.weighty=1.0 Componente 2: c.weightx=0.4, c.weighty=1.0 Veamos como se asigna el espacio horizontal. Como ambos componentes han pedido espacio libre, ste se divide entre ambos, por lo tanto a cada uno le tocan 125 puntos. Sin embargo como el componente 2 tan slo quiere el 40% del espacio libre se le asignan 50 puntos y el resto de los puntos pasan al otro componente que recibir sus 125 puntos ms los 75 puntos que sobraron del segundo componente, en total 200 puntos. El espacio vertical es ms sencillo. Como vimos antes se divide el espacio libre entre los componentes que lo han pedido. En este caso como los dos componentes han pedido la totalidad de su parte a ambos les corresponden 75 puntos. Obviamente cuando estemos diseando el interfaz no estaremos pensando en si este componente va a tener unos puntos y otro otros, sin embargo los atributos weightx y weighty son de una ayuda inestimable para hacer que determinadas partes de nuestra interfaz sean ms grandes que las otras. Veamos como queda el ejemplo anterior pero utilizando los atributos weightx y weighty:

95

Como se puede apreciar en la figura anterior cuanto mayor sea el porcentaje ms espacio libre ocupar nuestra celda. Lo ms importante es comprender que se los porcentajes se refieren al espacio libre y que el espacio libre se determina despus de insertar los componentes. Por lo tanto, que no os extrae que poniendo 0.1 como valor de weightx las celdas de la derecha ocupen tanto espacio. En este caso, las celdas de la derecha ocuparn el tamao preferido del botn ms grande ( HORIZONTAL ) ms su porcentaje del espacio libre horizontal. insets Con todo lo que hemos visto hasta ahora tenemos ms que de sobra para crear una interfaz compleja. Vamos a ver un ltimo atributo que nos servir de gran ayuda, insets. Si os fijis en la figura anterior, cuando se insertan componentes que ocupan la totalidad de la celda ya sea en horizontal, en vertical o en ambas direcciones, el componente se pega literalmente al borde de la celda. Muy comnmente desearemos que los componentes no estn tan pegados, es decir, que haya un mrgen entre el borde de la celda y los componentes. Esto lo conseguimos con el atributo insets. El atributo insets es un objeto de la clase java.awt.Insets cuyo constructor es: Insets(int top, int left, int bottom, int right) Como intuiris los parmetros del constructor especifican el espacio que se dejar de mrgen. Veamos un ejemplo:

96

Vamos a empezar con el ms sencillo, la ventana de entrada al sistema. Veamos como sera el cdigo para crear este interfaz: import javax.swing.*; import java.awt.*; public class A { public static void main(String[] args) { JFrame f = new JFrame(); Container container = f.getContentPane(); container.setLayout(new GridBagLayout()); ((JPanel)container).setBorder(BorderFactory.createTitledBorder( "Entrada al sistema")); GridBagConstraints c = new GridBagConstraints(); c.weightx=0.4; c.weighty=1.0; c.gridwidth=GridBagConstraints.RELATIVE; c.gridheight=GridBagConstraints.RELATIVE; c.fill=GridBagConstraints.BOTH; c.anchor = GridBagConstraints.WEST; c.insets = new Insets(2,5,2,0); container.add(new JLabel("Usuario"),c); c.gridwidth=GridBagConstraints.REMAINDER; c.gridheight=GridBagConstraints.RELATIVE; c.weightx=1.0; c.insets = new Insets(2,0,2,5); container.add(new JTextField(),c); c.gridwidth=GridBagConstraints.RELATIVE; c.gridheight=GridBagConstraints.REMAINDER; c.weightx=0.4; c.insets = new Insets(2,5,2,0); container.add(new JLabel("Contrasea"),c); c.gridwidth=GridBagConstraints.REMAINDER; c.gridheight=GridBagConstraints.REMAINDER; c.weightx=1.0; c.insets = new Insets(2,0,2,5); container.add(new JTextField(),c); f.setSize(220,110); f.setTitle("Login"); f.setVisible(true); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } Como se puede ver cada vez que aadimos un componente al contenedor hemos de pasar una variable de tipo GridBagConstraints al mtodo add. Tened mucho cuidado de no olvidaros pasar la variable de constraints porque en otro caso el interfaz no saldr como esperbais. Esta variable puede ser reutilizada, tal como se ve en el cdigo, para no tener que estar crendolas continuamente. Si compilais y ejecutis el ejemplo os deberais encontrar con la ventana siguiente:

97

Un ltimo punto que quera resaltar del cdigo fuente anterior es que como podis observar no he utilizado para nada los atributos gridx y gridy. Como ya os he comentado, estos atributos no son estrictamente necesarios al igual que tampoco lo son gridwidth o gridheight. A menudo con utilizar una de las dos alternativas ser suficiente pero habr ocasiones en las que tendremos que utilizar ambos atributos.

9.0 EVENTOS
El sistema de gestin de eventos de Java de la AWT es similar al de SWING, algunos diseadores pensaron que se necesitaba dejar a un lado las libreras AWT e introducir las Swing no sintieron lo mismo del sistema de gestin de eventos, consideraron que era lo suficientemente bueno. Realmente este sistema de gestin de eventos es bastante elegante y sencillo, sobre todo si se compara con el sistema de gestin de eventos de Java 1.0, mucho ms engorroso de usar y menos elegante.

Qu es un evento?
Todos los sistemas operativos estn constantemente atendiendo a los eventos generados por los usuarios. Estos eventos pueden ser pulsar una tecla, mover el ratn, hacer clic con el ratn, pulsar el ratn sobre un botn o men (Java distingue entre simplemente pulsar el ratn en un sitio cualquiera o hacerlo, por ejemplo, en un botn). El sistema operativo notifica a las aplicaciones que estn ocurriendo estos eventos, y ellas deciden si han de responder o no de algn modo a este evento.

El modelo de delegacin de eventos


El modelo de Java se basa en la delegacin de eventos: el evento se produce en un determinado componente, por ejemplo un scroll. Dnde se produce el evento se denomina fuente del evento. A continuacin el evento se transmite a un manejador de eventos (event listener) que este asignado al componente en el que se produjo el evento. El objeto que escucha los eventos es el que se encargar de responder a ellos adecuadamente. Esta separacin de cdigo entre generacin del evento y actuacin respecto a l facilita la labor del programador y da una mayor claridad a los cdigos.

98

Gestin de eventos en Java.


A la fuente del evento, en este caso un botn, le indicamos quin ser su manejador de eventos, manejador que ha de extender la clase Adapter correspondiente o implementar la interfaz Listener (interfaz ActionLitener en este caso). Cuando el usuario genere el evento deseado (en este caso pulse el botn), el objeto fuente empaqueta informacin a cerca del evento generando un objeto de tipo Event (ActionEvent en este caso) e invoca el mtodo correspondiente del manejador (actionPerformed(actionEvent)) pasndole como informacin el objeto de tipo Event generado. Es responsabilidad del manejador, y no de la fuente, responder al evento, por ello se dice que la fuente delega la gestin del evento en el manejador.

Lo que la fuente de eventos le pasa al objeto encargado de escuchar los eventos es, como no, otro objeto. Es un objeto tipo Event. En este objeto va toda la informacin necesaria para la correcta gestin del evento por parte del objeto que escucha los eventos. El objeto que escucha los eventos ha de implementar para ello una interface. El nombre de esta interface es siempre el nombre del evento ms Listener: para que un objeto escuche eventos de ratn ha de implementar la interface MouseListener, para que escuche eventos de teclado KeyListener..... Para hacer que un objeto escuche los eventos de otro objeto se emplea el mtodo add[nombre_evento]Listener, as si tuvisemos un Jframe llamado frame y quisisemos que el objeto llamado manejador escuchase los eventos de ratn de frame lo haramos del siguiente modo: frame.addMouseListener(manejador); manejador ha de pertenecer a una clase que implemente la interface MouseListener, que tiene un total de 7 mtodos que ha de implementar. A continuacin en la siguiente tabla mostramos los
99

eventos ms comunes, junto a la interface que debe implementar el objeto que escuche esos eventos y el mtodo para asociar un objeto para escuchar dichos eventos. En la columna de la derecha se presentarn diversos componentes que pueden generar dichos eventos.

100

Cabe preguntarse ahora por que mtodos tiene cada interface, ya que hemos de implementar todos ellos, incluso aunque no los usemos, sino la clase que se encargara de escuchar los eventos sera abstracta y no podramos crear ningn objeto de ella. Parece un poco estpido implementar mtodos que no hagan nada slo porque la interface de la que heredamos los tenga.
101

As por ejemplo si estamos interesados en escuchar clics de ratn hemos de crear una clase que implemente MouseListener, pero nosotros slo estaremos interesados en un mtodo de dicha interfase: mouseClicked. Los creadores de Java tambin pensaron en esto y por ello para cada interface que tiene ms de un mtodo crearon una clase llamada [nombre_evento]Adapter (MouseAdapter), que lo que hace es implementar todos los mtodos de la interface sin hacer nada en ellos. Nosotros lo nico que tendremos que hacer es que nuestra clase que escuche eventos extienda esta clase y sobrescriba los mtodos que nos interesen. A continuacin en la siguiente tabla damos un listado de las principales interfaces junto a sus respectivas clases Adapter y los mtodos que poseen:

102

Todos estos conceptos que estamos introduciendo acerca del tratamiento de eventos en Java, se ver mucho ms claro cuando se muestren algunos ejemplos ms adelante. As por ejemplo, si tenemos el siguiente GUI: un botn y una caja de texto dentro de una ventana, y queremos que al pulsar el botn aparezca un mensaje en la caja de texto, el oyente ser la ventana que contiene al botn y a la caja de texto, y la fuente ser el botn que lanzar el evento correspondiente. Este modelo de eventos ofrece las siguientes caractersticas: Ofrece un estructura robusta para soportar programas Java complejos. simple y fcil de aprender. Es Ofrece una clara separacin entre el cdigo de la aplicacin y del interfaz de usuario, en lo que al tratamiento de eventos se refiere. Facilita la creacin de un cdigo de tratamiento de eventos robusto y menos propenso a errores. Un oyente en lugar de implementar un interfaz, puede utilizar una clase que herede de una clase adaptadora de eventos del paquete java.awt.event. Las clases adaptadoras permiten sobrescribir solamente los mtodos del interfaz en los que se est interesado. As por ejemplo, si queremos atrapar un click del ratn, en lugar de implementar el interfaz MouseListener, heredamos de la clase adaptadora de los eventos de ratn, es decir de la clase MouseAdapter, solamente deberemos sobrescribir el mtodo mouseClicked(). Esto es posible debido a que las clases adaptadoras implementan el interfaz correspondiente y simplemente tienen implementaciones vacas de todos los mtodos del interfaz EventListener. De esta forma se consigue un cdigo ms claro y limpio. Estas clases se suelen utilizar cuando se quiere hacer uso de un interfaz muy complejo del que slo interesan un par de mtodos. Dentro de la jerarqua de clases de Java hay una serie de clases para representar todos los eventos y otra serie de interfaces que definen una serie de mtodos que deben implementar las clases que van a tratar los eventos, es decir, lo que hemos llamado oyentes. Otras clases que tambin hemos comentado y que se utilizan dentro del tratamiento de eventos en Java son las clases adaptadoras. Las clases adaptadoras las utilizaremos para simplificar nuestro cdigo, ya que implementan de forma vaca todos los mtodos de un interfaz de tratamiento de eventos determinado. Cada conjunto de eventos tiene asociado un interfaz, y como ya hemos dicho cada uno de estos interfaces declara una serie de mtodos para cada uno de los eventos lgicos asociados al tipo de evento de que se trate. Interfaz Mtodos Clase Adaptadora ActionListener actionPerformed(ActionEvent) --AdjustmentListener adjustmentValueChanged(AdjustmentEvent) --ComponentListener componentHidden(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent) componentShown(ComponentEvent) ComponentAdapter ContainerListener componentAdded(ContainerEvent) componentRemoved(ContainerEvent) ContainerAdapter FocusListener focusGained(FocusEvent) focusLost(FocusEvent) FocusAdapter ItemListener ItemStateChanged(ItemEvent) --103

KeyListener KeyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent) KeyAdapter MouseListener mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent) MouseAdapter MouseMotionListener mouseDragged(MouseEvent) mouseMoved(MouseEvent) MouseMotionAdapter TextListener textValueChanged(TextEvent) --WindowListener windowActivated(WindowEvent) windowClosed(WindowEvent) windowClosing(WindowEvent) windowDeactivated(WindowEvent) windowDeiconified(WindowEvent) windowIconified(WindowEvent) windowOpened(WindowEvent) WindowAdapter Como se puede apreciar en la tabla anterior, los interfaces que nicamente poseen un mtodo no tienen clase adaptadora correspondiente, ya que no tiene ningn sentido, siempre que utilizamos un interfaz con un nico mtodo vamos a implementarlo. A continuacin vamos a comentar los pasos genricos que se deben seguir a la hora de realizar el tratamiento de eventos en Java. Aunque los pasos son genricos, para poder hacer referencia a un interfaz concreto vamos a suponer que queremos realizar el tratamiento de eventos que se corresponde con la pulsacin de un botn. Lo primero es importar el paquete java.awt.event:
import java.awt.event.*;

A continuacin escribiremos la declaracin de la clase para que implemente el interfaz adecuado (listener interface). Por ejemplo si se est tratando de atrapar un evento ActionEvent (es decir, una pulsacin de un botn) generado por un botn, ser necesario implementar el interfaz ActionListener.
public class MiClase extends ClasePadre implements ActionListener{

Debemos determinar que componentes van a generar los eventos. Se registra cada uno de ellos con el tipo adecuado de oyente, si tenemos en cuenta el ejemplo anterior del botn, se debera escribir lo que objetoBoton.addActionListener(this); Una vez hecho esto debemos crear las implementaciones de todos los mtodos del interfaz que la clase debe implementar.
public void actionPerformed(ActionEvent evento){ //cuerpo del mtodo }

Una vez comentado el tratamiento de eventos en Java de forma ms o menos terica vamos a comentar una serie de ejemplos para aplicar la teora a la prctica. Estos ejemplos adems nos van a servir para repasar distintos puntos del lenguaje Java comentados hasta ahora.

104

Vamos a realizar una aplicacin que vamos a ir modificando y complicando para mostrar las diferentes facetas del tratamiento de eventos en Java. No vamos a tratar todos los interfaces, slo vamos a tratar tres de los ms representativos, el que se encarga de las pulsaciones de botones, ActionListener, el que se encarga del ratn, MouseListener, y el que se encarga de las ventanas, WindowListener, estos tres nos ofrecen ejemplos bastante prcticos. Tambin veremos los adaptadores MouseAdapter y WindowAdapter. Primero vamos a comenzar creando una aplicacin sencilla que va a consistir simplemente en una ventana que nos va a indicar si se encuentra minimizada o no, y cuando pulsemos sobre el aspa de cerrar la ventana esta se cierre y finalice la ejecucin de la aplicacin. El cdigo de esta aplicacin de ejemplo es:
import java.awt.*; //paquete necesario para el tratamiento de eventos import java.awt.event.*; public class Ventana extends Frame implements WindowListener{ //constructor de nuestra clase public Ventana(String titulo){ //constructor de la clase padre super(titulo); //tamao de la ventana setSize(150,150); //se muestra la ventana setVisible(trae); //se registra nuestra clase como oyente addWindowListener(this); } public void windowClosed(WindowEvent evento){ System.out.println("La ventana se ha cerrado"); //finaliza la aplicacin System.exit(0); } public void windowIconified(WindowEvent evento){ System.out.println("Estoy minimizada"); } public void windowDeiconified(WindowEvent evento){ System.out.println("No estoy minimizada"); } public void windowClosing(WindowEvent evento){ //se cierra la ventana dispose(); } public void windowDeactivated(WindowEvent evento){} public void windowActivated(WindowEvent evento){} public void windowOpened(WindowEvent evento){} public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos"); } }

Vamos a comentar algunos puntos importantes acerca del cdigo anterior. Vamos a comentar algunas consideraciones de carcter general, en primer lugar, al ser una aplicacin debe tener un mtodo main() de arranque, y como hemos dicho que es una ventana, hereda de la clase Frame. Como se puede observar hemos creado tambin un mtodo constructor para nuestra clase Ventana. En lo concerniente al tratamiento de eventos, como se puede ver importamos el paquete correspondiente e indicamos que nuestra clase implementa el interfaz WindowListener. En el constructor de nuestra clase indicamos quien va a ser el oyente de nuestra clase, es decir, quien va a
105

tratar los eventos, en este caso el oyente es nuestra misma clase. Para ello utilizamos la lnea de cdigo
addWindowListener(this);

Es decir, indicamos que el oyente de los eventos de la ventana va a ser nuestra propia clase, es decir, estamos registrando nuestra clase como oyente mediante la referencia this. Al ser nuestra clase fuente y oyente de los eventos, tambin debe implementar el interfaz que define los mtodos que se van a ejecutar atendiendo al evento que se produzca. En nuestro caso hay tres mtodos que no nos interesan, pero como implementamos el interfaz WindowListener debemos implementarlos aunque no tengan contenido. Los mtodos windowIconified() y windowDeiconified() se ejecutarn cuando se minimice la ventana y cuando se restaure, respectivamente, el mtodo windowClosing() se ejecuta en el momento de cerrar la ventana y el mtodo windowClosed() cuando ya se ha cerrado. El resto del cdigo es bastante sencillo y considero que no necesita una mayor explicacin. Como ya hemos comentado hay tres mtodos del interfaz WindowListener que no nos interesan y que por lo tanto nos gustara suprimir, en este momento entran en juego las clases adaptadoras. Como ya debe saber el alumno, las clases adaptadoras realizan una implementacin vaca de todos los mtodos del interfaz con el que se corresponden, y al heredar de ellas utilizaremos nicamente los mtodos del interfaz correspondiente que nos interese. En nuestro caso no podemos heredar de una clase adaptadora, ya que heredamos de la clase Frame y la herencia mltiple no se permite en Java. Por lo tanto deberemos crear y definir una nueva clase que herede de la clase adaptadora del interfaz WindowListener, es decir, que herede de la clase WindowAdapter. Por lo tanto si queremos hacer uso de la clase adaptadora WindowAdapter, el cdigo de nuestra aplicacin se debe modificar como indica :
import java.awt.*; import java.awt.event.*; //nuestra clase ya no implementa el interfaz WindowListener public class Ventana extends Frame{

public Ventana(String titulo){ super(titulo); setSize(150,150); setVisible(true); //se registra como oyente la clase que hereda la clase adaptadora addWindowListener(new AdaptadorVentana(this)); } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos"); } } //clase que hereda de la clase adaptadora class AdaptadorVentana extends WindowAdapter{ //atributo que se utiliza como referencia a la clase //que es la fuente del evento que queremos tratar private Ventana fuente; //constructor de nuestra clase adaptadora //recibe como parmetro la clase fuente del evento public AdaptadorVentana(Ventana fuente){ this.fuente=fuente; } //contiene la implementacin de los mtodos que nos interesan //del interfaz WindowListener
106

public void windowClosed(WindowEvent evento){ System.out.println("La ventana se ha cerrado"); System.exit(0); } public void windowIconified(WindowEvent evento){ System.out.println("Estoy minimizada"); } public void windowDeiconified(WindowEvent evento){ System.out.println("No estoy minimizada"); } public void windowClosing(WindowEvent evento){ fuente.dispose(); } }

Los mayores cambios se aprecian en que existe una nueva clase que va a ser la encargada de tratar los eventos, en este caso si que existe una separacin clara entre clase fuente y clase oyente. El oyente sera la clase AdaptadorVentana y la clase fuente sera la clase principal Ventana. Nuestra clase Ventana, al ser slo fuente de eventos no va a implementar el interfaz WindowListener y por lo tanto nicamente va a tener en su cuerpo el mtodo constructor y el mtodo main(). Otra cosa que cambia tambin en el cdigo de nuestra aplicacin es la forma en la que se registra el oyente. Se sigue utilizando el mtodo addWindowListener() pero en este caso se pasa por parmetro una instancia de la clase adaptadora que hemos creado nosotros.
addWindowListener(new AdaptadorVentana(this));

Ahora vamos a detenernos en la clase AdaptadorVentana. Esta clase hereda de la clase WindowAdapter y posee un atributo denominado fuente que es de la clase Ventana. Este atributo es necesario para tener una referencia a la clase fuente del evento. Esta referencia es necesaria ,en este caso, dentro del mtodo windowClosing(), a la hora de cerrar la ventana.
public void windowClosing(WindowEvent evento){ fuente.dispose(); }

Para conseguir la referencia a la fuente del evento se pasa una instancia de la clase Ventana actual al constructor de la clase adaptadora. De esta forma la clase adaptadora puede manipular y acceder al objeto de clase Ventana.
public AdaptadorVentana(Ventana fuente){ this.fuente=fuente; }

Como se puede comprobar esta nueva clase contiene todos los mtodos que nos interesan del interfaz WindowListener. Si queremos tratar distintos eventos mediante clases adaptadoras, deberemos crear una clase que herede de la clase adaptadora de cada tipo de evento, siempre que exista una clase adaptadora para el evento en cuestin. Tambin existe la posibilidad de utilizar la clases adaptadoras como clases internas (inner classes). Una clase interna es una clase definida dentro de otra. El beneficio que podemos obtener de estas clases adaptadoras, es que no tenemos porque llevar la referencia de la clase fuente. La clase interna va a tener acceso a todos los mtodos y atributos de la clase en la que est declarada, aunque estos sean privados. Realmente una clase interna se sale fuera de los principios de la POO, pero puede ser bastante prctica.
107

El nuevo cdigo tendra el aspecto siguiente:


import java.awt.*; import java.awt.event.*; public class Ventana extends Frame{ public Ventana(String titulo){ super(titulo); setSize(150,150); setvisible(true); //ya no se indica la referencia de la clase fuente addWindowListener(new AdaptadorVentana()); } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos"); } //clase adaptadora interna class AdaptadorVentana extends WindowAdapter{ //Ya no es necesario el atributo fuente public void windowClosed(WindowEvent evento){ System.out.println("La ventana se ha cerrado"); System.exit(0); } public void windowIconified(WindowEvent evento){ System.out.println("Estoy minimizada"); } public void windowDeiconified(WindowEvent evento){ System.out.println("No estoy minimizada"); } public void windowClosing(WindowEvent evento){ //llamamos directamente al mtodo dispose() dispose(); } }//se cierra la clase interna }

Ahora, vamos a ofrecer una posibilidad distinta a la hora de tratar los eventos en Java, se puede utilizar una clase interna annima:
import java.awt.*; import java.awt.event.*; public class Ventana extends Frame{ public Ventana(String titulo){ super(titulo); setSize(150,150) setVisible(true); //Utilizamos una clase interna annima addWindowListener(new WindowAdapter(){ //mtodos de la clase annima public void windowClosed(WindowEvent evento){ System.out.println("La ventana se ha cerrado"); System.exit(0); } public void windowIconified(WindowEvent evento){ System.out.println("Estoy minimizada"); } public void windowDeiconified(WindowEvent evento){ System.out.println("No estoy minimizada"); } public void windowClosing(WindowEvent evento){ dispose(); } });//se cierra la clase interna annima } public static void main (String[] args){
108

Ventana miVentana=new Ventana("Eventos"); } }

A la vista del cdigo se puede ver que se trata de instanciar una clase sin indicar un objeto que contenga una referencia a la misma, ya que en realidad esta clase slo se va a utilizar para el tratamiento de eventos y no se va a querer hacer una referencia a ella. El mecanismo es muy sencillo, se utiliza el nombre de la clase adaptadora correspondiente, WindowAdapter en nuestro caso, y se implementan los mtodos que se consideren necesarios, por lo dems funciona exactamente igual a una clase interna. Una vez comentados las distintas opciones que tenemos a la hora de tratar los eventos, ahora vamos a aadir a nuestra aplicacin un botn para que al pulsarlo escriba un mensaje en la pantalla. En este caso deberemos implementar el interfaz ActionListener, ya que este interfaz no posee una clase adaptadora, al tener un nico mtodo llamado actionPerformed(). El nuevo aspecto de nuestra aplicacin de ejemplo se puede observar en la Figura.

import java.awt.*; import java.awt.event.*; //implementamos el interfaz para tratar la pulsacin del botn public class Ventana extends Frame implements ActionListener{ private Button boton; public Ventana(String titulo){ super(titulo); setSize(150,150); setLayout(new FlowLayout()); boton=new Button("Plsame"); add(boton); setVisible(true); addWindowListener(new AdaptadorVentana()); //oyente del botn, en este caso la clase Ventana boton.addActionListener(this); } public void actionPerformed(ActionEvent evento){ System.out.println("Buenas tardes"); } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos"); } class AdaptadorVentana extends WindowAdapter{ public void windowClosed(WindowEvent evento){ System.out.println("La ventana se ha cerrado"); System.exit(0); } public void windowIconified(WindowEvent evento){ System.out.println("Estoy minimizada"); } public void windowDeiconified(WindowEvent evento){ System.out.println("No estoy minimizada"); } public void windowClosing(WindowEvent evento){
109

dispose(); } } }

Como se puede observar la fuente del evento en este nuevo caso va a ser por un lado, el botn y su oyente la clase Ventana, y por otro lado la fuente de eventos va a ser la clase Ventana y el oyente la clase interna AdaptadorVentana. Las lneas utilizadas para aadir el botn se encuentran en el constructor, la nica lnea digna de mencin es la que registra el oyente del botn.
boton.addActionListener(this);

La clase Ventana al implementar el interfaz ActionListener debe facilitar el mtodo actionPerformed(), que se ejecutar al pulsar el botn. Al pulsar el botn veremos en pantalla el saludo "Buenas tardes".
public void actionPerformed(ActionEvent evento){ System.out.println("Buenas tardes"); }

Para finalizar el presente captulo modificaremos de nuevo nuestra aplicacin, aadiendo un rea de texto, en la que se escribir si el puntero del ratn se encuentra en el rea de texto, o por el contrario ha salido. Aqu vamos a tratar un nuevo tipo de evento, en este caso los eventos del ratn, y lo vamos a realizar a travs de la clase adaptadora MouseAdapter. La clase MouseAdapter es la clase adaptadora del interfaz MouseListener. Utilizamos la clase adaptadora, porque slo nos van a interesar un par de mtodos del interfaz MouseListener.
import java.awt.*; import java.awt.event.*; public class Ventana extends Frame implements ActionListener{ private Button boton; private TextArea area; public Ventana(String titulo){ super(titulo); setLayout(new FlowLayout()); boton=new Button("Plsame"); area=new TextArea(); add(boton); add(area); show(); pack(); addWindowListener(new AdaptadorVentana()); boton.addActionListener(this); //El oyente del ratn es una nueva clase interna area.addMouseListener(new AdaptadorRaton()); } public void actionPerformed(ActionEvent evento){ System.out.println("Buenas tardes"); } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos"); } //clase adaptadora para los eventos del ratn class AdaptadorRaton extends MouseAdapter{ public void mouseEntered(MouseEvent evento){ area.setText("El ratn ha entrado"); } public void mouseExited(MouseEvent evento){ area.setText("El ratn ha salido"); } } class AdaptadorVentana extends WindowAdapter{
110

public void windowClosed(WindowEvent evento){ System.out.println("La ventana se ha cerrado"); System.exit(0); } public void windowIconified(WindowEvent evento){ System.out.println("Estoy minimizada"); } public void windowDeiconified(WindowEvent evento){ System.out.println("No estoy minimizada"); } public void windowClosing(WindowEvent evento){ dispose(); } } }

En esta ltima versin de la aplicacin de prueba tenemos una nueva fuente de eventos, que va a ser el rea de texto, y un nuevo oyente que es la clase adaptadora AdaptadorRaton. La clase Ventana ofrece un nuevo atributo que va a representar el rea de texto. Para registrar el oyente del rea de texto se utiliza el mtodo addMouseListener(), a este mtodo se le pasa una instancia de la clase adaptadora que va a tratar los eventos del ratn, en este caso se trata de la clase AdaptadorRaton. Esta clase implementa los mtodos mouseEntered() y mouseExited() que se lanzarn cuando en ratn entre en el rea de texto o salga del mismo, respectivamente. Como se puede apreciar cada oyente se encarga de los eventos para los cuales se ha registrado, sin tener en cuenta el resto de los que se produzca y sin interferir entre s.

Un evento es una accin que se genera al cambiar el estado de un componente de AWT. Los eventos es la forma que tiene un programa para enterarse de que ocurri algo y reaccionar conforme al suceso. Un evento puede ser una entrada de usuario (movimientos o clicks del ratn, pulsaciones de teclas), cambios en el medio ambiente del sistema (abrir, cerrar o mover una ventana) o cualquier otra actividad que pudiera afectar la operacin del programa. Java soporta varios tipos de eventos. Cada vez que ocurre alguna accin de las que se listan a continuacin se genera un evento que puede ser atrapado por un programa: Eventos de ratn. El ratn esta sobre un componente. El ratn sali de un componente. El botn del ratn est oprimido o fue liberado. El ratn se movi. El ratn est siendo arrastrado. Eventos de teclado. La tecla est oprimida o fue liberada. Eventos de botn. El botn fue oprimido. Eventos de choice. Alguna de las opciones del choice fue seleccionada. Eventos de checkbox. El checkbox fue seleccionado. Eventos de campo de texto. Se est escribiendo sobre el campo de texto. Se dio un enter sobre el campo de texto. Eventos de foco. El componente obtuvo o perdi el foco. El foco se obtiene cuando se da un click al ratn sobre un componente. Para que un programa atienda un evento se necesita asignar al componente un oidor o escuchador (listener en la terminologa de Java). Dentro del oidor se escribe el cdigo que se desea ejecutar cuando ocurra el evento.
111

Hay dos estrategias para definir oidores: la primera es usar una clase que tenga un solo mtodo por cada tipo de evento que atienda todos los eventos que se generen de ese tipo. La segunda estrategia consiste en declarar oidores annimos, uno por cada componente que pueda generar el evento en particular. La ventaja de la primera estrategia es que el manejo de eventos se centraliza en un solo mtodo. La desventaja es que ese mtodo debe ser capaz de distinguir cul componente fue el que gener el evento. Respecto a la segunda estrategia, la ventaja es que no es necesario distinguir nada, cada componente tiene su propio oidor y el cdigo se vuelve ms legible y elegante. La desventaja es que la cantidad de cdigo generado se vuelve mayor. En mi opinin la ventaja de los oidores annimos sobrepasa la desventaja y por eso en este curso se definen oidores annimos para el manejo de eventos. En las secciones siguientes vamos a revisar los eventos ms utilizados que son los de botn, de choice y de checkbox.

10.0 EXCEPCIONES
10.1- QU SON LAS EXCEPCIONES?
Definicin: Una excepcin es un evento excepcional que ocurre durante la ejecucin del programa y que interrumpe el flujo normal de las sentencias. Si una operacin no puede completarse debido a un error, el programa deber: volver a un estado estable y permitir otras operaciones. intentar guardar el trabajo y finalizar. Esto es difcil debido a que generalmente, el cdigo que detecta el error no es el que puede realizar dichas tareas. El que detecta el error debe informar al que pueda manejarlo. La solucin ms habitual a esto son los cdigos de error, es decir, que un mtodo devuelva un cdigo de error como valor de retorno. Esto presenta una serie de inconvenientes: No siempre queda sitio para cdigos de error (p. ej.: getchar). No se garantiza que se vayan a consultar. Si se contemplan todos los errores, el cdigo crece considerablemente. La lgica del programa queda oscurecida. No sirven en los constructores. En definitiva, hacen que el tratamiento de errores sea complejo y que, por ello, muchas veces no se tenga en cuenta y no se traten todos los errores. Esto impide la construccin de programas robustos. Java ofrece una solucin, otra forma de tratar con los errores: las excepciones.

Ejemplo:
class Excepcion { public static void main(String argumentos[]) { int i=5, j=0; int k=i/j; // Divisin por cero } }

Produce la siguiente salida al ser ejecutado: java.lang.ArithmeticException: / by zero at Excepcion.main(Excepcion.java:4)


112

10.2.- DETECCIN DE ERRORES


Una condicin excepcional es aquella que impide la continuacin de una operacin. Es decir, no se sabe cmo manejarla, pero no se puede continuar hasta que no se resuelva. En Java, cuando ocurre algo as, se lanza una excepcin (throw) para que alguien que sepa manejarla la trate en un contexto superior. Por ejemplo: if( error de CRC ) // Si se produce un error de CRC, lanzamos una excepcin de E/S throw new IOException(); // Para lanzar una excepcin hay que crear un objeto, // ya que una excepcin es un objeto. Con la palabra throw se lanza una excepcin. Cuando ocurre esto, finaliza el mtodo y se lanza un objeto que facilite informacin sobre el error ocurrido. Normalmente se utilizar una clase diferente para cada tipo de error. Java obliga a que un mtodo informe de las excepciones (explcitas) que puede lanzar. Un mtodo no slo tienen que decir qu devuelve si todo va bien, tambin debe indicar qu puede fallar. Un mtodo especifica las excepciones que se pueden producir en l mediante la palabra throws en la declaracin del mtodo. Por ejemplo: void f() throws EOFException, FileNotFoundException { if( ... ) throw new EOFException(); if( ... ) throw new FileNotFoundException(); }

10.3.- MANEJO DE EXCEPCIONES


Una vez que se detecta un error hace falta indicar quin se encarga de tratarlo. Para manejar excepciones hay que utilizar las palabras clave try y catch. Se ponen entre un try y un catch los mtodos que pueden producir alguna excepcin. El try delimita el grupo de operaciones que pueden producir excepciones. El bloque catch es el lugar al que se transfiere el control si alguna de las operaciones produce una excepcin. Es decir: try{ // Operaciones que pueden fallar, es decir, que // pueden producir alguna excepcin. } catch( <tipoDeExcepcin> ref ){ // Tratamiento de esa excepcin } Por ejemplo: try{ a.abreFichero(); a.leeCabecera(); a.actualizaDatos(); }
113

catch( IOException ref ){ System.out.println( Error de E/S ); } Si alguna de las operaciones del bloque produce una excepcin, se interrumpe el bloque try y se ejecuta el catch. Al finalizar ste, se contina normalmente. Si no se produce ninguna excepcin el bloque catch se ignora. Un bloque try puede tener varios catch asociados, uno para cada tipo de excepcin que se pueda producir. Por ejemplo, try{ a.abreFichero(); a.leeCabecera(); a.actualizaDatos(); } catch( FileNotFoundException ref ){ System.out.println( Error de apertura ); } catch( IOException ref ){ System.out.println( Error de E/S ); } Si se produce una excepcin que no se corresponde con ningn catch indicado, la excepcin se propaga hacia atrs en la secuencia de invocaciones hasta encontrar un catch adecuado. Por ejemplo: void f1( int accion ) throws EOFException { try{ if( accion == 1 ) throw new FileNotFoundException(); else if( accion == 2 ) throw new EOFException(); } catch( FileNotFoundException e ){ System.out.println( Error corregido ); } System.out.println( Finalizacin normal de f1 ); } void f2( int accion ) { try{ f1( accion ): } catch( EOFException e ){ System.out.println( Error corregido ); } System.out.println( Finalizacin normal de f2 ); } Qu pasa si se llama a f2 pasndole los valores 1 2 para el parmetro accin? En qu mtodo se producen las excepciones y en cul se tratan?
114

Cuando se invoca a un mtodo que lanza excepciones, es obligatorio: Que se maneje el error (en un catch). Que se indique mediante throws su propagacin. Es decir, o se trata el error o se avisa que se va a continuar sin haberlo corregido para que otro mtodo lo corrija. Por ejemplo: void f1() throws IOException { ... } void f2() { try{ // Alternativa 1: tratar el error en cuanto se produce f1() } catch( IOException e ){ // Tratamiento del error } } // Alternativa 2: se contina propagando el error para que lo gestione // otro mtodo. void f2() throws IOException { f1(); } Es mejor intentar tratar los errores en cuanto sea posible (alternativa 1), en vez de dejarlos para que los gestione alguien por encima (alternativa 2). De todos modos, no siempre es posible tratarlos en el mismo momento en que se producen y por tanto, a menudo hay que recurrir a la segunda alternativa.

10.4.- FINALLY
Puede haber ocasiones en que se desea realizar alguna operacin tanto si se producen excepciones como si no. Dichas operaciones se pueden situar dentro de un bloque finally, de la siguiente forma: try{ // Operaciones con posibles excepciones } catch( <tipoDeExcepcion> ref ){ // Tratamiento de la excepcin } finally{ // Operaciones comunes } Ejemplo: class Recurso { void reserva(){ ... } void libera(){ ... }
115

} class Ejemplo { void prueba() { Recurso recurso = new Recurso(); try{ recurso.reserva(); // Operaciones con posibles excepciones recurso.libera(); } catch( ExceptionA a ){ // Tratamiento del error recurso.libera(); } catch( ExceptionB b ){ // Tratamiento del error recurso.libera(); } catch( ExceptionC c ){ // Tratamiento del error recurso.libera(); } } } En el ejemplo anterior queda suficientemente claro que independientemente de que se produzca una excepcin o no, e independientemente de que sta sea capturada o no, la liberacin del recurso siempre debe llevarse a cabo (instruccin recurso.libera()). Para ello existe una solucin mucho ms elegante. Solucin con finally: class Ejemplo { void prueba() { Recurso recurso = new Recurso(); try{ recurso.reserva(); // Operaciones con posibles excepciones } catch( ExceptionA a ){ // Tratamiento del error } catch( ExceptionB b ){ // Tratamiento del error } catch( ExceptionC c ){ // Tratamiento del error } finally{
116

recurso.libera(); } } } Si no se produce ninguna excepcin, se ejecutarn el bloque try y el finally. En cambio, si se produce alguna excepcin: Si es atrapada por un catch del mismo try, se ejecuta ste y luego el finally. La ejecucin contina despus del finally con normalidad. Si no es atrapada, se ejecuta el finally y la excepcin se propaga al contexto anterior.

10.5.- LANZAMIENTO DE EXCEPCIONES


Hay situaciones en las que interesa relanzar una excepcin ya atrapada, es decir, interesa dar un tratamiento determinado a una excepcin pero tambin interesa seguir propagndola hacia arriba para que en un contexto superior se siga teniendo constancia de ella. Esto se hace as: catch( Exception e ){ // Salvar datos o cualquier otro tratamiento, pero propagar throw e; } Ejemplo:
class LanzaExcepcion { public static void main(String argumentos[]) throws ArithmeticException { int i=1, j=2; if (i/j< 1) throw new ArithmeticException(); else System.out.println(i/j); } }

Genera el siguiente mensaje: java.lang.ArithmeticException at LanzaExcepcion.main(LanzaExcepcion.java:5)


La excepcin pasara a buscarse entre los catch de un contexto superior (los del mismo nivel se ignoran).

10.6.- JERARQUA DE EXCEPCIONES


Toda excepcin debe ser una instancia de una clase derivada de la clase Throwable. De ella hereda una serie de mtodos que dan informacin sobre cada excepcin. Estos mtodos son: getMessage toString printStackTrace (imprime en la salida estndar un volcado de la pila de llamadas). fillInStackTrace
117

La jerarqua de excepciones en Java es la siguiente: Throwable Error (AWTError, LinkageError, ThreadDeath, VirtualMachineError) Exception (ClassNotFoundException, CloneNotSupportedException, DataFormatException, GeneralSecurityException, IllegalAccessException, IOException, NoSuchFieldException, NoSuchMethodException, PrinterException, etc.) RuntimeException (ArithmeticException, ArrayStoreException, CannotRedoException, CannotUndoException, ClassCastException, EmptyStackException, IllegalArgumentException, IndexOutOfBoundsException, NullPointerException, etc.) Que representada en forma grfica da lugar a:

Figura Jerarqua de clases de las excepciones Java Todas las excepciones descienden de la clase Throwable, la cual se divide en dos subclases: Error y Exception. Las clases derivadas de Error describen errores internos de la JVM e indican errores serios que normalmente la aplicacin no debera intentar gestionar. Tampoco deberan ser lanzadas por las clases del usuario. Estas excepciones rara vez ocurren, y cuando as sea, lo nico que se puede hacer es intentar cerrar el programa sin perder datos. Ejemplos de este tipo de excepciones son OutOfMemoryError, StackOverflowError, etc. Los programas en Java trabajarn con las excepciones de la rama Exception. Esta clase, a su vez, tiene una subclase llamada RuntimeException que, a su vez, tiene otras clases derivadas. Estas excepciones se clasifican en: Explcitas: las que derivan directamente de Exception. Implcitas: las que derivan de RunTimeException. Se utilizan las RunTimeException para indicar un error de programacin. Si se produce una
118

excepcin de este tipo hay que arreglar el cdigo. Por ejemplo: un cast incorrecto, acceso a un array fuera de rango, uso de un puntero null, etc. El resto de las Exception indican que ha ocurrido algn error debido a alguna causa ajena al programa (est correcto). Por ejemplo: un error de E/S, error de conexin, etc. Los mtodos deben declarar slo las excepciones explcitas. Las implcitas no deben declararse (el compilador no lo exige, aunque pueden producirse igualmente). Por tanto, cuando un mtodo declara una excepcin, est avisando de que puede producirse dicho error (por causas externas al mtodo) adems de cualquier error implcito (consecuencia de un error en el cdigo que debera ser subsanado).

10.7.- CREACIN DE EXCEPCIONES


Si se necesita notificar algn error no contemplado en Java se puede crear una nueva clase de Excepcin. La nica condicin es que la clase derive de la clase Throwable o de alguna derivada. En la prctica, normalmente derivar: de RuntimeException si se desea notificar un error de programacin. de Exception en cualquier otro caso. En un mtodo que tiene como parmetro un entero entre 1 y 12 se recibe un 14 Qu tipo de excepcin se creara para notificarlo? Si un mtodo para listar Clientes por impresora se encuentra con que deja de responder, qu tipo de excepcin debera crear? Ejemplo de creacin de una excepcin: class PrinterException extends Exception{} Cuando se produzca una excepcin de este tipo, se lanzar, como se puede ver en el siguiente ejemplo: class Ejemplo { void imprimirClientes( Cliente[] clientes ) throws PrinterException { for( int i=0; i<clientes.length; i++ ) { // Imprimir cliente[i] if( error impresin ) throw new PrinterException(); } } } Vemos ahora otro ejemplo en el que creamos y lanzamos una excepcin de tipo RuntimeException: class MesInvalidoException extends RuntimeException {} class Ejemplo {
119

void setMes( int mes ) { if( mes < 1 || mes > 12 ) throw new MesInvalidoException(); } } En este ejemplo no se avisa que se lanza una excepcin porque es de tipo RuntimeException (implcita).

10.8.- OBTENCIN DE INFORMACIN DE UNA EXCEPCIN


Una excepcin, como cualquier otra clase, puede tener operaciones que permitan obtener ms informacin sobre el error. class PrinterException extends Exception { private int numPagina; PrinterException( int pag ) { numPagina = pag; } int getNumPagina() { return numPagina; } } class Ejemplo { void imprimirClientes( Cliente[] clientes ) throws PrinterException { for( int pagina = 0; pagina<clientes.length; pagina++ ) { // Imprimir cliente[ pagina ] if( error impresin ) throw new PrinterException( pagina ); } } void Informe() { try{ imprimirClientes( clientes ); } catch( PrinterException e ) { System.out.println( Slo se han imprimido + e.getNumPagina() ); } } }
120

Otro ejemplo con una excepcin descendiente de RuntimeException: class MesInvalidoException extends RuntimeException { private int mes; MesInvalidoException( int m ) { mes = m; } getMesInvalido() { return mes; } public String toString() // Mtodo heredado de la clase Throwable, que devuelve // la representacin String del error. { return Mes invlido: + mes; } } class Fecha { void setMes( int m ) { if( m < 1 || m > 12 ) throw new MesInvalidoException( m ); } } class Ejemplo { public static void main( String[] args ) { Fecha fecha = new Fecha(); fecha.setMes( 14 ); } } Java obliga a atrapar las excepciones explcitas, qu pasa si no se atrapa una implcita, como en este caso? El compilador no da ningn error por tratarse de una excepcin implcita y el programa compila correctamente. La excepcin MesInvalidoException se produce al invocar el mtodo setMes() con el valor 14 y al no haber sido capturada por el cdigo que realiza la invocacin (el main), el programa termina de manera anormal. Por defecto, se visualizar el mensaje Mes invlido: 14 y el programador deber darse cuenta de que existe un error en el cdigo que llama al mtodo setMes(). La clase Throwable tiene una serie de mtodos que tienen ya implementados todas las excepciones de Java (por herencia), y que pueden ser implementados por las nuevas excepciones que se creen. Algunos de estos mtodos son: public String getMessage() Devuelve el mensaje detallado de error de este objeto (o null si el objeto no tiene mensaje)..
121

public String getLocalizedMessage() Lo mismo, pero pensado para devolver el mensaje localizado de una forma ms especfica. Hay que redefinirlo, si no simplemente se comporta como getMessage() public String toString() Devuelve la representacin String del error (la clase del error, bsicamente). public void printStackTrace() Visualiza en el flujo de error estndar la traza de llamadas (backtrace) que se almacena automticamente en todo error. public void printStackTrace(PrintStream s) Lo mismo, pero indicando el flujo al que se manda la informacin. public void printStackTrace(PrintWriter s) Idem. public native Throwable fillInStackTrace() "Rellena" la pila de llamadas; es decir, devuelve el error cambiando su traza de llamadas como si se hubiera producido ahora. Es til cuando un error que se ha producido en un lugar queremos que se indique en otro.

10.9.- RESUMEN EXCEPCIONES EXPLCITAS-IMPLCITAS


Excepciones explcitas: Derivan de Exception (no de RuntimeException). Indican un error externo a la aplicacin. Si se lanzan es obligatorio declararlas. Si se invoca un mtodo que las lanza, es obligatorio atraparlas o declararlas. Excepciones implcitas: Derivan de RuntimeException. Indican un error de programacin. No se declaran: se corrigen. Se pueden atrapar. En caso contrario finaliza la aplicacin.

10.10.- EXCEPCIONES Y HERENCIA


Al redefinir un mtodo se est dando otra implementacin para un mismo mensaje. El nuevo mtodo slo podr lanzar excepciones declaradas en el mtodo original. class A { void f() throws EOFException { } } class B extends A { // Incorrecto void f() throws EOFException, FileNotFoundException {} } Es decir, un hijo puede lanzar menos excepciones que las que declara el padre, no puede lanzar una excepcin que no lanzaba el padre, no puede lanzar nuevas excepciones. Por otro lado, un mtodo puede declarar excepciones que no lance realmente. As un mtodo base puede permitir que las redefiniciones puedan producir excepciones. Los constructores, al no heredarse, s pueden lanzar nuevas excepciones.

122

10.11.- NORMAS EN EL USO DE EXCEPCIONES


Norma 1: Si una excepcin se puede manejar no debe propagarse. Es ms cmodo usar mtodos que no produzcan errores. Norma 2: No utilizar las excepciones para evitar una consulta. No abusar de ellas. try { stack.pop(); } catch {EmptyStackExecution e) { ... } while (!stack.empty()) stack.pop(); Norma 3: Separar el tratamiento de errores de la lgica. for (int i = 0; i < len; i++) { try { obj1.mesg1(); } catch(ExcA a) { // Tratamiento } try { obj2.mesg2(); } catch(ExcB b) { // Tratamiento } } try { for (int i = 0; i < len; i++) { obj1.mesg1(); obj2.mesg2(); } } catch(ExcA a) { // Tratamiento } catch(ExcB b) { // Tratamiento } Norma 4: No ignorar una excepcin try { // operaciones; } catch {EmptyStackExecution e) { } // Para que calle el compilador

123

10.12.- EJEMPLOS
Ejemplo 1: Lectura de un nmero por teclado class LeerTeclado { static BufferedReader inp = new BufferedReader( new InputStreamReader(System.in)); public static String leerLinea() { String s = ""; try { s = inp.readLine(); } catch (java.io.IOException e) { } return s; // Otra forma de hacerlo // // char c; // try { // while( (c = (char)System.in.read()) != '\n') // s += c; // } // catch (java.io.IOException e) { } } public static String leerLinea( String mens ) { // con prompt System.out.print( mens ); return leerLinea(); } /* leerDoble() Devuelve NaN en caso de error */ public static double leerDoble() { String s = leerLinea(); double d; try { d = Double.valueOf( s ).doubleValue(); } catch (java.lang.NumberFormatException e) { d = Double.NaN; } return d; } public static double leerDoble( String mens ) { // con prompt System.out.print( mens ); return leerDoble(); } public static void main (String[] a) { double d1 = leerDoble( "Introduce un nmero real: " ); double d2 = leerDoble( "Introduce otro: " ); System.out.println( "La suma de ambos es: " + (d1+d2) ); } }

124

Ejemplo 2: Incorporacin de excepciones a ListaEnlazada. public class FueraDeListaException extends IndexOutOfBoundsException { FueraDeListaException( ) { } FueraDeListaException( String s ) { super( s ); // Llama al constructor del padre } } public class ListaEnlazada { ... public void insertar( Object o, int posicion ) throws FueraDeListaException { NodoLista aux = l; for (int i = 1; aux != null && i < posicion-1; i++ ) { aux = aux.siguiente; } if (posicion == 1) l = new NodoLista( o, l ); else { if (aux == null) throw new FueraDeListaException("intentando insertar elemento ms all del fin de la lista" ); else aux.siguiente = new NodoLista( o, aux.siguiente ); } } public void borrarPrimero() throws FueraDeListaException { if (l == null) throw new FueraDeListaException("intento de borrar primero en lista vaca" ); else l = l.siguiente; } public static void main( String[] a ) { ListaEnlazada l = new ListaEnlazada(); try { l.borrarPrimero(); } catch (FueraDeListaException e) { System.out.println( "Error: " + e.getMessage() + ". Continuamos..." ); } l.insertarPrimero( new Integer( 1 ) ); l.insertarPrimero( new Integer( 2 ) ); l.insertar( new Integer( 4 ), 1 ); l.insertar( new Integer( 3 ), 2 ); // no obligatorio recoger el error // pq es RuntimeException l.insertar( new Double( 2.5 ), 3 ); try { l.insertar( new Integer( 18 ), 10 );
125

} catch (FueraDeListaException e) { System.out.println( "Error: " + e.getMessage() + ". Continuamos..." ); } finally { l.insertarPrimero( new Integer(18) ); } System.out.println( l ); l.destruir(); l.borrarPrimero(); // de este error no se recupera el programa System.out.println( "Aqu no se llega... error en ejecucin" ); } } Salida: La salida resultado de ejecutar este main() sera la siguiente: Error: intento de borrar primero en lista vaca. Continuamos... Error: intentando insertar elemento ms all del fin de la lista. Continuamos... ( 4 3 2.5 2 1 ) FueraDeListaException: intento de borrar primero en lista vaca at ListaEnlazada.borrarPrimero(ListaEnlazada.java:42) at ListaEnlazada.main(ListaEnlazada.java:66)

126

11.0 THREADS.
11.1 Qu son los threads?
Todos los programadores conocen lo que es un proceso, la mayora dira que es un programa en ejecucin: tiene un principio, una secuencia de instrucciones y tiene un final. Un thread39 es un flujo simple de ejecucin dentro de un programa. Hasta el momento, todos los programas creados contenan un nico thread, pero un programa (o proceso) puede iniciar la ejecucin de varios de ellos concurrentemente. Puede parecer que es lo mismo que la ejecucin de varios procesos concurrentemente a modo del fork() en UNIX, pero existe una diferencia. Mientras en los procesos concurrentes cada uno de ellos dispone de su propia memoria y contexto, en los threads lanzados desde un mismo programa, la memoria se comparte, utilizando el mismo contexto y recursos asignados al programa (tambin disponen de variables y atributos locales al thread). Un thread no puede existir independientemente de un programa, sino que se ejecuta dentro de un programa o proceso.

11.1.1 Un ejemplo, mejor que mil palabras.


class UnThread { public static void main(String args[] ) { int i; NoThread t = new NoThread(); t.start(); for (i=1; i<=20; i++) System.out.print("SI "); } } class NoThread extends Thread { public void run() { int i; for (i=1;i<=20; i++) System.out.print("NO "); } }

La salida de este programa es una serie alternativa de Ses y Noes: SI NO NO NO SI SI SI NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO SI SI SI SI SI SI SI SI SI SI SI SI SI SI SI SI En el ejemplo se declara una clase principal (UnThread) que inicia su ejecucin como un proceso con un nico thread mediante su mtodo main(), como ocurra en todos los programas vistos hasta ahora. En este proceso, se declara y se crea un thread (NoThread t = new NoThread(); ). Despus se inicia su ejecucin mediante la llamada al mtodo de la clase
127

Thread start() (t.start() ), con lo cul comienza a ejecutarse el mtodo run() redefinido en la clase NoThread (el mtodo start() llama al mtodo run() ). Tenemos

dos threads ejecutndose. Una vez se inicia la ejecucin del thread, el tiempo de la CPU se reparte entre todos los procesos y threads del sistema, con lo cul, se intercalan instrucciones del mtodo main() con instrucciones del mtodo run() entre otras correspondientes a otros procesos (del sistema operativo y otros procesos de usuario que pudieran estar ejecutndose).

11.1.2 Otro ejemplo

class DosThreads { public static void main(String args[] ) { NoThread n = new NoThread(); SiThread s = new SiThread(); n.start(); s.start(); } } class NoThread extends Thread { public void run() { int i; for (i=1;i<=20; i++) System.out.print("NO "); } }
128

class SiThread extends Thread { public void run() { int i; for (i=1;i<=20; i++) System.out.print("SI "); } }

La salida del programa, al igual que en el ejemplo anterior, es: NO NO NO NO NO NO SI SI SI SI SI SI SI SI SI SI SI SI SI SI SI SI SI NO NO SI SI NO NO SI NO NO NO NO NO NO NO NO NO NO En este caso se instancian dos threads y se llama a su ejecucin mediante los mtodos start(). Estos dos threads se reparten el tiempo de la CPU y se ejecutan concurrentemente. Una vez que finalizan su ejecucin, el programa termina.

11.1.3 Y otro ejemplo ms.


class UnThreadDosInstancias { public static void main(String args[] ) { SiNoThread s = new SiNoThread("SI"); SiNoThread n = new SiNoThread("NO"); s.start(); n.start(); } } class SiNoThread extends Thread { private String SiNo; static int Contador=0; public SiNoThread(String s) { super(); SiNo=s; } public void run() { int i; for (i=1;i<=20; i++) System.out.print(++Contador+":"+SiNo+" "); } }

Produce la siguiente salida: 1:SI 2:SI 3:SI 5:NO 6:NO 4:SI 8:SI 9:SI 10:SI 11:SI 12:SI 13:SI 14:SI 15:SI 7:NO 17:NO 16:SI 19:SI 20:SI 21:SI 22:SI 23:SI 24:SI 25:SI 18:NO 26:NO 27:NO 28:NO 29:NO 30:NO 31:NO 32:NO 33:NO 34:NO 35:NO 36:NO 37:NO 38:NO 39:NO 40:NO En este caso se declaran dos instancias de una misma clase ( SiNoThread) y se ejecutan concurrentemente. Cada una de ellas con sus propios atributos de objeto (String SiNo), pero comparten los atributos de clase40 (int Contador). Puede comprobarse que el contador no ha funcionado todo lo correctamente que pudiera esperarse: 1,2,3,5,6,4,8, Esto es debido a que se ha accedido concurrentemente a una misma zona de memoria sin que se produzca exclusin mutua.
129

11.2 Estado de un thread.


El ciclo de vida de un thread puede pasar por varios estados ilustrados en la siguiente figura:

Cuando se instancia un thread, se inicializa sin asignarle recursos. Est en el estado nuevo Thread. Un thread en este estado nicamente acepta las llamadas a los mtodos start() o stop(). La llamada al mtodo start() asigna los recursos necesarios al objeto, lo sita en el estado ejecutable y llama al mtodo run() del objeto. Esto no significa que el thread est jecutndose (existen multitud de sistemas que poseen una sola CPU que debe ser compartida por todos los threads y procesos) sino que est en disposicin de ejecutarse en cuanto la CPU le conceda su tiempo. Un thread en estado ejecutable puede pasar al estado no ejecutable por alguna de las siguientes razones: que sean invocados alguno de sus mtodos sleep() o suspend(), que el thread haga uso de su mtodo wait() o que el thread est bloqueado esperando una operacin de entrada/salida o que se le asigne algn recurso. Un thread puede pasar al estado muerto por dos motivos: que finalice normalmente su mtodo run() o que se llame a su mtodo stop() desde cualquiera de sus posibles estados (nuevo thread, ejecutable o no ejecutable). Un thread pasa del estado no ejecutable a ejecutable por alguna de las siguientes razones: dormido: que pase el tiempo de espera indicado por su mtodo sleep(), momento en el cual, el thread pasar al estado ejecutable y, si se le asigna la CPU, proseguir su ejecucin.

130

suspendido: que, despus de haber sido suspendido mediante el mtodo suspend(), sea continuado mediante la llamada a su mtodo resume(). Nota: Estos dos metodos, ya casi no se usan en java2. esperando: que despus de una llamada a wait() se contine su ejecucin con notify() o notifyAll(). bloqueado: una vez finalizada una espera sobre una operacin de E/S o sobre algn recurso.

11.3 Creacin de threads.


Pueden crearse threads de dos formas distintas: declarando una subclase de la clase Thread o declarando una clase que implemente la interface Runnable y redefiniendo el mtodo run() y start() de la interface. Se utilizar la primera forma, ms evidente y sencilla, cuando la clase declarada no tenga que ser subclase de ninguna otra superclase. Se utilizar la segunda forma cuando la clase declarada tenga que ser subclase de una superclase que no es subclase de Thread o implemente el interface Runnable.

La forma en que se crean los threads como subclase de Thread ya se ha visto en puntos anteriores. Creacin de threads utilizando la interface Runnable: class PrimerThread implements Runnable { Thread t; public void start() { t = new Thread(this); t.start(); } public void run() { int i; for (i=1;i<=50;i++) System.out.println(i); } } class SegundoThread extends PrimerThread { } class ThreadRunnable {
131

public static void main(String args[]) { SegundoThread s = new SegundoThread(); SegundoThread ss = new SegundoThread(); s.start(); ss.start(); } }
En este caso se utiliza el constructor de la clase Thread:

public Thread(Runnable destino); Metodologa para la creacin del thread: 1) La clase creada debe implementar el interface Runnable: class
Runnable {

PrimerThread implements

2) La clase ha de crear un atributo (o variable local en el mtodo start() ) de la clase Thread: Thread t; 3) Hay que redefinir el mtodo start(): Instanciar el atributo de la clase Thread llamando a su constructor pasndole como parmetro la propia instancia de clase ( this):
t = new Thread(this);

Iniciar el thread:
t.start();

4) Redefinir el mtodo run() tal y como se hace en la otra alternativa de creacin de threads (mediante subclases de Thread). Una vez declarada la clase que implementa la interface Runnable, ya puede ser instanciada e iniciada como cualquier thread. Es ms, cualquier subclase descendiente de esta clase poseer tambin las caractersticas propias de los threads, como ocurre en el ejemplo: SegundoThread es subclase de PrimerThread, y por lo tanto, ambas son Thread. Ambas formas de crear threads admiten un parmetro de tipo String que identifica al thread por su nombre: public Thread(String nombre); public Thread(Runnable destino, String nombre); Para poder despus averiguarlo mediante el mtodo: public final String getName(); Si no se ha asignado nombre a los threads, la clase Thread se los asigna por defecto como Thread-1, Thread-2,

11.4 Operaciones sobre threads.


11.4.1 currentThread().
public static Thread currentThread(); Devuelve el thread que se est ejecutando acutalmente.

11.4.2 isAlive().
public final boolean isAlive(); Devuelve false si el thread est en el estado nuevo thread o muerto y true en caso contrario.
132


class MiThread extends Thread { public void run() { try { sleep(2000); } catch (InterruptedException e) { } } } class Vivo { public static void main(String args[]) { MiThread t = new MiThread(); System.out.println("isAlive() antes de iniciar: "+ t.isAlive()); t.start(); System.out.println("isAlive() en ejecucin: "+ t.isAlive()); try { // Hace "dormir" al thread actual Thread.currentThread().sleep(3000); // de esta forma, le da tiempo a terminar al // thread t } catch (InterruptedException e) { } System.out.println("isAlive() despus de ejecucin: "+ t.isAlive()); } }

La salida correspondiente al programa es: Estado antes de iniciar: false Estado en ejecucin: true Estado despus de ejecucin: false

11.4.3 sleep().
1) public static void sleep(long milisegundos) throws InterruptedException; 2) public static void sleep(long milisegundos, int nanosegundos) throws InterruptedException; 1) Hace que el thread actual pase del estado ejecutable a dormido y permanezca en dicho estado durante los milisegundos especificados como parmetro. Una vez que se ha cumplido el tiempo, el thread despierta y pasa automticamente al estado de ejecutable. 2) Acepta un parmetro ms: un nmero entero de nanosegundos (0 999999 ) que se sumaran a los milisegundos de espera. En ambos casos se lanzar la excepcin InterruptedException si se llama al mtodo interrupt() del thread.

11.4.4 suspend().
public final void suspend(); Llamando al mtodo suspend() de un thread, el estado del mismo pasa de ejecutable a suspendido inmediatamente y slo puede ser reactivado (pasado al estado ejecutable) llamando a su mtodo resume(). La llamada al mtodo resume() de un thread que est dormido no tiene ningn efecto.

133

11.4.5 wait().
El mtodo wait() es heredado de la superclase Object, de la cul heredan todos sus mtodos todas las clases creadas en Java. 1) public final void wait() throws InterruptedException; 2) public final void wait(long milisegundos) throws InterruptedException; 3) public final void wait(long milisegundos, int nanosegundos) throws InterruptedException 1) Causa el paso al estado esperando del thread indefinidamente hasta que el thread sea notificado mediante notify() o notifyAll(). 2) El thread estar esperando hasta que sea notificado o expire el timeout (milisegundos). 3) El thread estar esperando hasta que sea notificado o expire el timeout (milisegundos + nanosegundos).

11.4.6 yield().
public static void yield(); Transfiere el control al siguiente Thread en el scheduler 41 (con la misma prioridad que el actual) que se encuentre en estado ejecutable.
class UnThreadDosInstanciasAmables { public static void main(String args[] ) { SiNoThreadAmable s = new SiNoThreadAmable("SI"); SiNoThreadAmable n = new SiNoThreadAmable("NO"); s.start(); n.start(); } } class SiNoThreadAmable extends Thread { private String SiNo; static int Contador=0; public SiNoThreadAmable(String s) { super(); SiNo=s; } public void run() { int i; for (i=1;i<=20; i++) { System.out.print(++Contador+":"+SiNo+" "); yield(); } } }

En este caso hay dos threads que se ejecutan concurrentemente. Para cada salida por pantalla, (System.out.println(++Contador+:SiNo+ ); ) el thread cede la CPU al otro thread mediante la llamada a yield(). Esto no significa que el otro thread contine su ejecucin hasta llegar a su propio yield(), ya que puede perder la CPU antes y por consiguiente no tienen porqu salir los Ses y Noes alternativamente, pero s que est ms repartida la CPU: 1:NO 2:SI 3:SI 4:NO 5:SI 6:NO 7:NO 8:SI 9:NO 10:SI 11:NO 12:NO 14:NO 13:SI 15:NO 16:SI 17:SI 18:NO 19:SI 20:NO 21:SI 22:NO 23:SI 24:SI 25:NO 26:SI 27:SI 28:NO 29:NO 30:NO 31:SI 32:NO 33:SI 34:SI 35:SI 36:NO 37:SI 38:NO 39:SI 40:NO

134

11.4.7 join().
public final void join() throws InterruptedException; Hace que el thread que se est ejecutando actualmente pase al estado esperando indefinidamente hasta que muera el thread sobre el que se realiza el join().

class MiThread extends Thread { public void run() { int i; for (i=1;i<=100; i++) System.out.print(i+" "); } } class Join1 { public static void main (String args[]) throws InterruptedException { MiThread t = new MiThread(); t.start(); t.join(); System.out.println("El thread ha terminado"); } }

El thread de la clase Join1 espera indefinidamente hasta que finalice el thread t. La salida por pantalla es: 1 2 3 4 5 6 7 8 9 22 23 24 25 26 27 40 41 42 43 44 45 58 59 60 61 62 63 76 77 78 79 80 81 94 95 96 97 98 99 10 11 12 13 14 15 16 17 18 28 29 30 31 32 33 34 35 36 46 47 48 49 50 51 52 53 54 64 65 66 67 68 69 70 71 72 82 83 84 85 86 87 88 89 90 100 El thread ha terminado 19 37 55 73 91 20 38 56 74 92 21 39 57 75 93

Si no se hubiera realizado el t.join(), el thread de la clase Join1 no habra esperado la finalizacin de t y la salida por pantalla habra sido otra: 1 2 3 El thread ha terminado 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 Existen dos mtodos join() ms: public final synchronized void join(long miliseg) throws InterruptedException; public final synchronized void join(long miliseg, int nanoseg) throws InterruptedException; En estos dos casos, el thread actual no espera indefinidamente sino que reinicia su ejecucin en el instante en que se finalice el thread sobre el que se hace el join() o pase el tiempo especificado por los parmetros miliseg (milisegundos) y opcionalmente nanoseg (nanosegundos).
135

11.5 Threads y sincronismo.


Hasta el momento se ha estado tratando con threads asncronos, cada uno de ellos se ejecutaba independientemente de los dems, que se ejecutaban concurrentemente. Existen, sin embargo, situaciones en las que es imprescindible sincronizar en cierta forma la ejecucin de distintos threads que buscan un objetivo comn, cooperando entre ellos para su correcto funcionamiento.

11.5.1 El paradigma productor/consumidor.


El paradigma productor/consumidor consiste en la ejecucin de un thread que produce un flujo de datos que deben ser consumidos por otro thread. Este flujo de datos puede ser almacenado en una zona de memoria compartida por ambos threads. En este caso, la sincronizacin es vital, ya que de lo contrario, si el productor genera los datos ms deprisa de lo que el consumidor es capaz de tratarlos, puede producirse un desbordamiento de la memoria asignada al efecto; por otra parte, si el consumidor es ms rpido que el productor, puede encontrarse con que no hay datos que consumir o que se consuman ms de una vez (si no elimina los datos consumidos, sino que los elimina el productor).

En este caso, debe existir un mecanismo que frene al productor o al consumidor en los casos necesarios.

11.5.2 Seccin crtica.


Se llama seccin crtica a los segmentos de cdigo dentro de un programa que acceden a zonas de memoria comunes desde distintos threads que se ejecutan concurrentemente

136

En Java, las secciones crticas se marcan con la palabra reservada synchronized. Aunque est permitido marcar bloques de cdigo ms pequeos que un mtodo como synchronized, para seguir una buena metodologa de programacin, es preferible hacerlo a nivel de mtodo.
class Contador { private long valor=0; public void incrementa() { long aux; aux=valor; aux++; valor=aux; } public long getValor() { return valor; } } class Contable extends Thread { Contador contador; public Contable (Contador c) { contador=c; } public void run () { int i; long aux; for (i=1;i<=100000; i++) { contador.incrementa(); } System.out.println("Contado hasta ahora: "+ contador.getValor()); } } class ThreadSinSync { public static void main(String arg[]) { Contable c1 , c2 ; Contador c = new Contador(); c1 = new Contable(c); c2 = new Contable(c); c1.start(); c2.start(); } }
137

Para realizar el incremento en el mtodo incrementa(), a efectos didcticos, se ha empleado una variable auxiliar intermedia (aux), aunque el efecto de haber codificado valor++ habra sido similar. En este caso, la seccin crtica es la lnea: contador.incrementa(); ya que desde esta instruccin se accede a un objeto (contador) que es compartido por ambos threads (c1 y c2). La salida por pantalla es: Contado hasta ahora: 124739 Contado hasta ahora: 158049 cuando debera haber sido: en la primera lnea un nmero comprendido entre 100000 y 200000; y en la segunda lnea el valor 200000. Lo que ha ocurrido en realidad para que se produzca este resultado equivocado es lo siguiente

Evidentemente, esto mismo ha ocurrido un gran nmero de veces durante la ejecucin de los threads. Simplemente cambiando la lnea de cdigo:
public void incrementa() {

por
public synchronized void incrementa() {

se habra conseguido bloquear el mtodo incrementa, de forma que no pudieran ejecutarlo simultneamente dos threads sobre el mismo objeto (contador).

138

La salida del programa habra sido (correcta): Contado hasta ahora: 186837 Contado hasta ahora: 200000

11.5.3 Monitores.
En Java, cada objeto que posee un mtodo synchronized posee un nico monitor para ese objeto. Cuando un thread est ejecutando un mtodo synchronized de un objeto, se convierte en el propietario del monitor, evitando que cualquier otro thread ejecute ningn otro mtodo synchronized sobre ese mismo objeto. Esto no impide que el propietario del monitor ejecute otro mtodo synchronized de ese mismo objeto, adquiriendo de nuevo el monitor (llamada a un mtodo synchronized desde otro mtodo synchronized).
class Caja { private int valor; public synchronized void meter(int nv) { valor = nv; System.out.println("metido el valor: "+valor); } public synchronized void sacar() { System.out.println("sacado el valor: "+valor); valor=0; } } class Productor extends Thread { Caja c; public Productor(Caja nc) { c = nc; } public void run() { int i; for (i=1; i<=10; i++) c.meter(i); } } class Consumidor extends Thread { Caja c; public Consumidor(Caja nc) { c = nc; } public void run() {
139

int i; for (i=1; i<=10; i++) c.sacar(); } } class ProdCons { public static void main(String argum[]) { Caja cj = new Caja(); Productor p = new Productor(cj); Consumidor c = new Consumidor(cj); p.start(); c.start(); } }

En este ejemplo existen dos threads que se comportan siguiendo el paradigma productor/consumidor. Existe un objeto de la clase Caja que es capaz de almacenar un nico nmero entero. Sobre esta caja se pueden realizar dos tipos de operaciones: meter(int): Almacena el entero que se le pasa como parmetro y muestra el valor en pantalla. sacar(): Muestra en pantalla el valor almacenado y pone ese valor a cero. Tanto el thread productor como el consumidor comparten el mismo objeto de la clase Caja. Para asegurar la exclusin mutua en la zona crtica (la que accede a valor), se declaran los mtodos meter y sacar como synchronized. Pero esto no es suficiente para que el programa funcione correctamente, ya que el productor puede almacenar varios valores antes de que el consumidor extraiga el valor o, tambin, que el consumidor intente sacar varios valores consecutivamente, por lo que la salida por pantalla podra ser la siguiente: metido el valor: 1 sacado el valor: 1 sacado el valor: 0 sacado el valor: 0 metido el valor: 2 metido el valor: 3 sacado el valor: 3 sacado el valor: 0 sacado el valor: 0 metido el valor: 4 metido el valor: 5 sacado el valor: 5 sacado el valor: 0 metido el valor: 6 metido el valor: 7 metido el valor: 8 sacado el valor: 8 sacado el valor: 0 metido el valor: 9 metido el valor: 10 De alguna forma habra que asegurar que no se meta ningn valor si la caja est llena y que no se saque ningn valor si la caja est vaca.
140

class Caja { private int valor; private boolean disponible=false; public synchronized void meter(int nv) { if (!disponible) { valor = nv; disponible=true; System.out.println("metido el valor: "+valor); } } public synchronized void sacar() { if (disponible) { System.out.println("sacado el valor: "+valor); valor=0; disponible=false; } } } class Productor extends Thread { Caja c; public Productor(Caja nc) { c = nc; } public void run() { int i; for (i=1; i<=10; i++) c.meter(i); } } class Consumidor extends Thread { Caja c; public Consumidor(Caja nc) { c = nc; } public void run() { int i; for (i=1; i<=10; i++) c.sacar(); } } class ProdCons2 { public static void main(String argum[]) { Caja cj = new Caja(); Productor p = new Productor(cj); Consumidor c = new Consumidor(cj); p.start(); c.start(); } }

Salida por pantalla: metido el valor: 1 sacado el valor: 1 metido el valor: 2 Despus de sacar el valor 1, el consumidor ha seguido su ejecucin en el bucle y como no se mete ningn valor por el consumidor, termina su ejecucin, el productor introduce el siguiente valor (el 2), y sigue su ejecucin hasta terminar el bucle; como el consumidor ya ha terminado su ejecucin no se pueden introducir ms valores y el programa termina.

141

Lo que hace falta es un mecanismo que produzca la espera del productor si la caja tiene algn valor (disponible) y otro que frene al consumidor si la caja est vaca (no disponible):
class Caja { private int valor; private boolean disponible=false; public synchronized void meter(int nv) { if (disponible) { try { wait(); } catch (InterruptedException e) { } } valor = nv; disponible=true; System.out.println("metido el valor: "+valor); notify(); } public synchronized void sacar() { if (!disponible) { try { wait(); } catch (InterruptedException e) { } } System.out.println("sacado el valor: "+valor); valor=0; disponible=false; notify(); } } class Productor extends Thread { Caja c; public Productor(Caja nc) { c = nc; } public void run() { int i; for (i=1; i<=10; i++) c.meter(i); } } class Consumidor extends Thread { Caja c; public Consumidor(Caja nc) { c = nc; } public void run() { int i; for (i=1; i<=10; i++) c.sacar(); } } class ProdCons3 { public static void main(String argum[]) { Caja cj = new Caja(); Productor p = new Productor(cj); Consumidor c = new Consumidor(cj); p.start(); c.start(); } }

Produce el resultado deseado: metido el valor: 1


142

sacado el valor: 1 metido el valor: 2 sacado el valor: 2 metido el valor: 3 sacado el valor: 3 metido el valor: 4 sacado el valor: 4 metido el valor: 5 sacado el valor: 5 metido el valor: 6 sacado el valor: 6 metido el valor: 7 sacado el valor: 7 metido el valor: 8 sacado el valor: 8 metido el valor: 9 sacado el valor: 9 metido el valor: 10 sacado el valor: 10 Si el primer thread en adquirir el monitor del objeto Caja es el Productor al ejecutar el mtodo meter(i): la variable disponible contendr el valor false y no se ejecutar wait(). Realiza la operacin correspondiente y ejecuta el mtodo notify() que no tiene ningn efecto. Si el primer thread en adquirir el monitor del objeto Caja es el Consumidor al ejecutar el mtodo sacar(): La variable disponible contendr el valor false y por lo tanto, se ejecuta el mtodo wait() el mtodo wait hace que el thread que actualmente posee el monitor lo abandone el monitor es tomado por el otro thread al llamar a meter(i) como disponible sigue siendo false, no se ejecuta el wait(), se completa el mtodo, se cambia el valor de disponible a true y se llama al mtodo notify() el mtodo notify() hace que el thread que est en estado de espera pase a ejecutable y tome el monitor del objeto.

143

11.6 Prioridad de un thread.


Hasta ahora se ha supuesto que todos los threads se repartan el tiempo de la CPU por igual, pero esto no es generalmente as. Se repartirn la CPU por igual slo si la prioridad de los distintos thread es la misma. En todos los ejemplos que se han visto anteriormente los threads tenan la misma prioridad porque cuando se crea un thread, ste hereda la prioridad del thread que lo cre, sin embargo esta prioridad puede ser modificada en cualquier momento llamando al mtodo setPriority(). Para conocer la prioridad de un thread se utiliza el mtodo getPriority(). public final static int MAX_PRIORITY = 10; prioridad mxima de un thread public final static int MIN_PRIORITY = 1; prioridad mnima de un thread public final static int NORM_PRIORITY = 5; prioridad normal (por defecto) de un thread public final void setPriority(int newPriority); public final int getPriority(); El algoritmo de planificacin (scheduling) es el siguiente: la CPU se asigna al thread en estado ejecutable de mayor prioridad y si hay varios, se reparten el tiempo mediante round-robin. Adems este algoritmo es explusivo, es decir, si se est ejecutando un thread y en ese momento pasa a estado ejecutable un thread de mayor prioridad, se le asigna directamente la CPU. Este algoritmo tiene un peligro: que un thread nunca se ejecute porque siempre existan threads de mayor prioridad. Java evita esto asignando la CPU tambin a threads de menor prioridad aunque cada mucho menos tiempo que a los threads de mayor prioridad. De todas formas, no debe confiarse la no inanicin a la bondad del Java.
class ThreadConPrioridad extends Thread { String nombre; public ThreadConPrioridad(String n , int pri) { super(); Threads.

225
nombre=n; setPriority(pri); } public void run() { int i; for(i=1;i<100;i++) { System.out.print(nombre); yield(); } } } class Prioridad1 { public static void main(String args[]) { ThreadConPrioridad t1 = new ThreadConPrioridad("1",5); ThreadConPrioridad t2 = new ThreadConPrioridad("2",5); t1.start(); t2.start(); } }

144

Salida por pantalla: 221211122122212121212121112221212121212121212121212121 212121212121212121212122121212121212121121212121221212 121212121212121212112121212121212121212121221212211212 121212121211212121212121212121221111 En este caso, los threads t1 y t2 tienen la misma prioridad (5), por lo que al llamar al mtodo yield() se cede la ejecucin al otro thread. Si las prioridades hubieran sido otras. Por ejemplo:
ThreadConPrioridad t1 = new ThreadConPrioridad("1",5); ThreadConPrioridad t2 = new ThreadConPrioridad("2",4);

La salida por pantalla habra sido diferente: 111111111111111111111111111111111111111111111111111111 111111111111111111121212121212121212111111111111111111 222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222 Como puede observarse, la mayora del tiempo de la CPU es acaparada por el thread de mayor prioridad.

11.7 Threads Daemon.


Un daemon, tambin llamado demonio o servicio es un thread que proporciona servicios al resto de threads que se ejecutan en el mismo proceso que el daemon. Normalmente ejecuta un bucle infinito que realiza algn tipo de operacin y tiene una prioridad baja (se ejecuta en background). Cualquier thread puede ser un daemon. Unicamente se diferencian de los threads normales en que, a pesar de ejecutar un bucle infinito, termina su ejecucin cuando slo quedan threads que son de tipo daemon en ejecucin. public final void setDaemon(boolean on); La llamada al mtodo setDaemon(true) hace que el thread se considere Daemon y setDaemon(false) hace que deje de serlo. Para saber si un thread es un Daemon se utiliza el mtodo: public final boolean isDaemon();

145

11.8 Grupos de threads.


Todo thread creado en Java pertenece a un Grupo de threads. Los grupos de threads sirven para manejar como una unidad a un conjunto de threads; por ejemplo: iniciar o detener varios threads mediante una sola operacin. Hay que sealar que los grupos de threads no aaden ningn tipo de funcionalidad, nicamente sirven para que sea ms cmodo el manejo muchos threads en un programa. Para ello se utiliza la clase ThreadGroup que pertenece al paquete java.lang. Cuando se inicia la ejecucin de un programa, Java crea un grupo de threads llamado main. A no ser que se especifique lo contrario, cuando se crea un thread, se aade al grupo al que pertenece el thread que lo cre. Esa es la razn por la cual todos los threads creados hasta el momento pertenecen al grupo main. Un grupo de threads puede contener threads, pero tambin grupos, y todos ellos pertenecen a un grupo raz: main.

11.8.1 Creacin de grupos de threads.


Mediante llamadas al constructor: 1) public ThreadGroup(String nombre); 2) public ThreadGroup(ThreadGroup padre, String nombre); 1) Crea un grupo cuyo nombre se pasa como parmetro. Este grupo pertenecer (subgrupo) al grupo actual (al que pertenece el thread que realiza la llamada al contructor).
146

Ejemplo: ThreadGroup tg = new ThreadGroup(MiGrupo); 2) Crea un grupo cuyo nombre se pasa como parmetro. Este grupo pertenecer al grupo que se pasa como parmetro (padre). Ejemplo: ThreadGroup tg2 = new ThreadGroup( tg ,
MiGrupo2);

Es posible obtener el grupo al que pertenece un thread mediante la llamada al mtodo getThreadGroup() de la clase Thread:

class Grupo1 { public static void main(String args[]) { ThreadGroup tg = Thread.currentThread().getThreadGroup(); System.out.println(tg.getName()); } }

El mtodo getName() devuelve un String que contiene el nombre del grupo. La salida por pantalla, evidentemente es: main

11.8.2 Operaciones sobre threads agrupados. 11.8.2.1 Operaciones sobre threads. 11.8.2.1.1 Asignacin de un thread a un grupo.
Como se ha visto, los threads se insertan por defecto en el mismo grupo que el thread actual (el thread que lo crea) a no ser que se indique explcitamente a qu grupo debe pertenecer. Esto se realiza en la llamada al constructor del thread: public Thread(ThreadGroup grupo, String nombreDelThread); public Thread(ThreadGroup grupo, Runnable destino, String nombreDelThread); 11.8.2.1.2 Operaciones sobre un conjunto de threads. public final void suspend(); Pasan al estado de suspendido todos los threads descendientes de este grupo (todos los threads pertenecientes a este grupo) y se llama al mtodo suspend() de todos los grupos pertenecientes a este grupo ( valga la redundancia). public final void resume(); Pasan al estado de ejecutable todos los threadsdescendientes de este grupo que estaban en estado suspendido. public final void stop(); Pasan al estado muerto todos los threads descendientes de este grupo. 11.8.2.2 Operaciones sobre grupos. public final void setDaemon(boolean daemon); Convierte al grupo en daemon si el parmetro es true43. Un grupo daemon es destruido automticamente cuando no contiene ningn thread o grupo. public final boolean isDaemon(); Devuelve true si el grupo es un daemon y false en caso contrario. public final void setMaxPriority(int pri); Establece la mxima prioridad que puede tener un thread descendiente de este grupo, aunque los threads descendientes que ya pertenecan al grupo antes de llamar al mtodo setMaxPriority() pueden conservar su prioridad mayor que el parmetro (pri).
147

public final void destroy() throws IllegalThreadStateException; Destruye el grupo (pero no los threads descendientes). Este grupo debe estar vaco: no debe contener threads activos, de lo contrario se genera una excepcin de la clase IllegalThreadStateException. Si el grupo no est vaco, hay que llamar antes al mtodo stop() del grupo44 para que todos los threads descendientes pasen al estado muerto. public final boolean parentOf(ThreadGroup g); Devuelve true si este grupo es padre (o es el mismo) que el grupo que se pasa como parmetro (g) y false en caso contrario. 11.8.2.3 Obtencin del contenido de un grupo. 1) public int enumerate(Thread lista[]); 2) public int enumerate(Thread lista[], boolean recurr); 3) public int enumerate(ThreadGroup lista[]); 4) public int enumerate(ThreadGroup lista[], boolean recurr); El mtodo enumerate puede obtener un vector con los threads pertenecientes al grupo (los casos 1 y 2) o un vector con los grupos pertenecientes a un grupo (casos 3 y 4) se almacena en el parmetro lista[]. Por defecto, se obtienen listas de threads (y grupos) recursivas, aadiendo los threads o grupos del grupo sobre el que se realiza la llamada y de los grupos descendientes del mismo. Para obtener nicamente los threads pertenecientes al grupo en cuestin es necesario pasar un nuevo parmetro de tipo bolean (recurr) con el valor false.

class MiThread extends Thread { public MiThread(ThreadGroup tg, String nom) { super(tg,nom); } public void run() { System.out.println("Ejecutado "+getName()); } } class Grupo2 { public static void main(String args[]) throws InterruptedException { ThreadGroup tg = new ThreadGroup("grupo uno"); ThreadGroup tg2 = new ThreadGroup(tg,"grupo dos"); MiThread t1 = new MiThread(tg,"Thread 1.1"); MiThread t12 = new MiThread(tg,"Thread 1.2"); MiThread t21 = new MiThread(tg2,"Thread 2.1"); MiThread t22 = new MiThread(tg2,"Thread 2.2"); Thread t[] = new Thread[4]; int i; tg.enumerate(t); for (i=0;i<t.length;i++) System.out.println(t[i]); } }

148

En este caso, la salida por pantalla es la siguiente: Thread[Thread 1.1,5,grupo uno] Thread[Thread 1.2,5,grupo uno] Thread[Thread 2.1,5,grupo dos] Thread[Thread 2.2,5,grupo dos] Si en lugar de enumerate(t) se hubiera utilizado la frmula enumerate(t, false), la salida por pantalla habra sido: Thread[Thread 1.1,5,grupo uno] Thread[Thread 1.2,5,grupo uno] null null

149

12.0 Swing

Interfaces de usuario en Java: componentes Swing / contenedores


Introduccin
Muchos de los conceptos vistos en captulos anteriores, como pueden ser los eventos o los gestores de diseo siguen siendo vlidos para los componentes Swing, as por ejemplo, los gestores de diseo que utilizan los componentes Swing son los mismos que los vistos en los componentes AWT, de hecho se debe importar el paquete JAVA.AWT para utilizarlos, pero Swing aporta un nuevo gestor de diseo (que veremos en el siguiente captulo, tambin dedicado por completo al extenso mundo de los componentes Swing) que se encuentra en el paquete javax.swing. Lo mismo sucede con los eventos, los componentes Swin utilizan las clases de los eventos incluidas en el paquete java.awt.event, y adems aaden nuevos eventos que se pueden encontrar en el paquete javax.swing.event. Como hemos dicho vamos a tener tres captulos dedicados a Swing. Swing es bastante extenso y aun utilizando tres captulos no lo vamos a ver completamente, sera necesario un curso de la misma extensin de ste (o ms) para tratar Swing de forma completa. En este primer captulo vamos a introducir una serie de conceptos de Swing y tambin se va a tratar un tipo de componentes Swing, los contenedores Swing. En el segundo captulo trataremos el otro gran grupo de componentes Swing, los componentes atmicos. Y en el ltimo de esta serie de captulos trataremos otras caractersticas de Swing como son el nuevo gestor de diseo que incorpora (implementado por la clase BoxLayout) y la caracterstica Pluggable Look & Feel, es decir, aspecto y comportamiento configurables.

JFC y Swing
JFC (Java Foundation Classes) es el nombre que recibe el conjunto de un gran nmero de caractersticas cuya funcin primordial es la de permitir la construccin de complejos interfaces de usuario. El primer anuncio de JFC se realiz en la conferencia de desarrolladores JavaOne y se defini mediante las caractersticas que iba a ofrecer: Componentes Swing: comprenden todos los componentes utilizados para interfaces de usuario desde botones, barras de men, dilogos y ventanas hasta cajas de texto, barras de progreso, paneles con pestaas y listas. A lo largo de este captulo describiremos algunos de los componentes Swing, no todos ya que Swing ofrece un gran nmero de clases agrupadas en 15 paquetes distintos.
150

Soporte para Pluggable Look and Feel: es decir, soporte para una apariencia y comportamiento configurables. Permite a cualquier programa que utilice componentes Swing definir un tipo de apariencia y comportamiento (Look and Feel). As por ejemplo una misma aplicacin puede presentar una apariencia y comportamiento al estilo de Java o bien tipo Windows. En el apartado correspondiente volveremos a retomar esta caracterstica y la trataremos en detalle. API de accesibilidad: permite a tecnologas de rehabilitacin tales como lectores de pantalla y displays Braille obtener informacin acerca del interfaz de usuario. El API de accesibilidad se encuentra formando parte de las caractersticas avanzadas del lenguaje Java y por lo tanto no lo vamos a tratar en este curso. API Java 2D: al igual que ocurra con la caracterstica o funcionalidad anterior, se trata de un conjunto de clases especializadas, en este caso, en el tratamiento de grficos en dos dimensiones, imgenes y texto. El API Java 2D se escapa tambin del alcance y pretensiones de este curso. Soporte para arrastrar y soltar (Drag and Drop): permite la posibilidad de arrastrar y soltar componentes entra aplicaciones Java y aplicaciones en otros lenguajes. En muchos casos se utilizan indistintamente los trminos JFC y Swing sin hacer ningn tipo de distincin, Swing fue el nombre en clave que recibi el proyecto de Sun encargado de desarrollar los nuevos componentes para la construccin de interfaces de usuario. La denominacin swing aparece tambin en los paquetes correspondientes. Los componentes Swing se encuentran disponibles de dos formas distintas, como parte de la plataforma Java 2, tanto en la herramienta JDK 1.2 o como en el JDK 1.3, y como una extensin del JDK 1.1 (versin de Java 1.1) denominada JFC 1.1. En nuestro caso vamos a utilizar la versin que se encuentran integrada con la plataforma Java 2.

Componentes Swing frente a componentes AWT


En este apartado vamos a comentar las diferencias entre los dos grupos de componentes que ofrece Java para la creacin de interfaces de usuario, y el porqu de la existencia de dos grupos distintos de componentes. Los componentes AWT aparecieron en la versin 1.0 del JDK y eran los nicos componentes que se encontraban disponibles para desarrollar interfaces de usuario. Los componentes AWT se utilizan principalmente en las versiones 1.0 y 1.1 del lenguaje (que coincidan con las versiones de la herramienta JDK), aunque la plataforma Java 2 soporta perfectamente los componentes AWT como hemos podido comprobar en captulos anteriores. Desde Sun nos recomiendan que utilicemos si es posible los componentes Swing en lugar de los componentes AWT.

151

Los componentes Swing los podemos identificar porque los nombres de sus clases suelen ir precedidos por la letra J. As por ejemplo, la clase Button del AWT, tiene su clase correspondiente en Swing denominada JButton. Los componentes AWT se encuentran en el paquete JAVA.AWT y los Swing en el paquete javax.swing. La mayor diferencia entre los componentes AWT y los componentes Swing, es que los componentes Swing se encuentran implementados sin utilizar ningn tipo de cdigo nativo. Los componentes Swing se denominan componentes ligeros (lightweight) y los componentes AWT componentes pesados (heavyweight). Otra diferencia importante es la gran potencia y funcionalidad que ofrecen los componentes Swing. Incluso los componentes Swing ms sencillos ofrecen una serie de posibilidades que los componentes AWT no recogen, algunas de estas ventajas de los componentes Swing frente a los AWT son las que se enumeran a continuacin: botones Swing pueden mostrar imgenes y/o texto. Los posible situar bordes en torno a los componentes Swing. Es Los componentes Swing no tienen porque ser rectangulares, por ejemplo, los botones pueden ser redondos. tecnologas de rehabilitacin pueden obtener informacin de los componentes Swing Las de forma sencilla. Swing nos permite especificar el aspecto y comportamiento (look and feel) del interfaz de usuario de nuestra aplicacin (ms adelante veremos como), sin embargo los componentes AWT tienen el aspecto y comportamiento de la plataforma nativa sobre la que se ejecutan. Vamos a comentar brevemente algunas de las razones por las que nos puede interesar utilizar componentes Swing en lugar de componentes AWT. rico conjunto de componentes que ofrece Swing: botones con imgenes, barras de El herramientas, imgenes, elementos de men, selectores de color, etc. arquitectura Pluggable Look & Feel, es decir, aspecto y comportamiento configurable La y seleccionable para los elementos del interfaz de usuario. Posiblemente en un futuro se ampliarn los componentes Swing disponibles.

Parece hasta ahora que con los componentes Swing todo son ventajas, e incluso que ni siquiera debemos plantearnos que tipo de componentes debemos utilizar para construir interfaces de usuario, siempre elegiremos componentes Swing. Pero este razonamiento no es correcto, ya que en la prctica no resulta tan sencillo. Se debe sealar que, desgraciadamente, todava no existen navegadores Web que soporten Swing, ms claro todava, los navegadores Web actuales, incluso en sus ltimas versiones, no implementan la mquina virtual correspondiente a la plataforma Java 2. Por lo tanto si queremos construir applets que puedan ejecutarse en cualquier navegador deberemos construir su interfaz grfica mediante componentes AWT. Aunque se pueden generar parches que utilizaremos para que los navegadores soporten componentes Swing.
152

Contenedores de alto nivel


Bajo esta nomenclatura se agrupan una serie de clases que se corresponden con componentes Swing que realizan la funcin de contenedores de otros componentes y que constituyen la raz en la jerarqua de contenedores. Los componentes de alto nivel se basan en los siguientes principios: Swing ofrece tres clases que representan a componentes de alto nivel: JFrame (ventana), JDialog (dilogo) y JApplet. Tambin dentro de los componentes Swing encontramos una cuarta clase denominada JWindow que representa una ventana sin controles ni ttulo y que se encuentra siempre por encima de cualquier ventana, es el equivalente a la clase Window de los componentes AWT. A lo largo de este captulo iremos viendo como en los componentes Swing encontramos equivalentes de los componentes AWT. Para aparecer en pantalla todo componente de interfaz de usuario debe formar parte de una jerarqua de contenedores. Cada jerarqua de contenedores posee un contenedor de alto nivel como raz. Cada contenedor de alto nivel posee un panel de contenido (content pane) que contiene los componentes visibles del contenedor de alto nivel del interfaz de usuario correspondiente. Opcionalmente se puede aadir una barra de men a un contenedor de alto nivel. La barra de men se sita en el contenedor de alto nivel, pero fuera del panel de contenido. Veamos un sencillo ejemplo que muestra un contenedor de alto nivel. Se trata de un objeto de la clase JFrame que contiene una barra de men (JMenuBar) de color azul y una etiqueta (JLabel) de color amarillo. En la Figura se puede ver el aspecto del contenedor.

Y el cdigo fuente completo de este ejemplo es :


import java.awt.*; import javax.swing.*; public class ContenedorAltoNivel { public static void main(String s[]) { //se crea la ventana raz de la jerarqua de contenedores JFrame ventana = new JFrame("Contenedor de alto nivel"); //se crea la etiqueta JLabel etiquetaAmarilla = new JLabel(""); etiquetaAmarilla.setOpaque(true); etiquetaAmarilla.setBackground(Color.yellow); etiquetaAmarilla.setPreferredSize(new Dimension(200, 180)); //se crea la barra de men JMenuBar barraMenuCyan = new JMenuBar(); barraMenuCyan.setOpaque(true); barraMenuCyan.setBackground(Color.cyan);
153

barraMenuCyan.setPreferredSize(new Dimension(200, 20)); //se aaden la etiqueta y la barra de men a la ventana ventana.setJMenuBar(barraMenuCyan); ventana.getContentPane().add(etiquetaAmarilla, BorderLayout.CENTER); //se muestra la ventana ventana.pack(); ventana.setVisible(true); ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } }

A la vista de este cdigo podemos realizar los siguientes comentarios. Se han importado los paquetes JAVA.AWT y javax.swing. El paquete JAVA.AWT es necesario para utilizar las clases Dimension, Color y BorderLayout, como se puede observar aunque utilicemos componentes Swing vamos a necesitar de los componentes AWT. Tambin podemos comprobar la forma en la que se aade un componente a un panel de contenido de un contenedor de alto nivel como puede ser un objeto de la clase JFrame, y tambin como aadir una barra de men, en este mismo apartado veremos en ms detalle el cdigo que realiza estas funciones. Veamos un ejemplo ms para ilustrar las jerarquas de contenedores. Para ello nos debemos fijar en la Figura siguiente:

En este ejemplo se han utilizado los siguientes componentes Swing: Una ventana principal (JFrame). panel (JPanel). Un botn (JButton). Un Una etiqueta (JLabel). Al panel se le ha aadido un borde mediante la clase BorderFactory para mostrar el agrupamiento de forma ms clara entre el botn y la etiqueta. El cdigo fuente de este ejemplo es el que aparece en el Cdigo fuente :
import javax.swing.*; import java.awt.*; public class Ventana{ public static void main(String[] args) { //se establece el Look & Feel try { UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { } //se crean los componentes JFrame ventana = new JFrame("Ventana"); JLabel etiqueta = new JLabel(" Soy una etiqueta"); JButton boton = new JButton("Soy un botn");
154

JPanel panel = new JPanel(); //se asigna un borde y un gestor de diseo al panel panel.setBorder(BorderFactory.createLineBorder(Color.red,5)); panel.setLayout(new GridLayout(0, 1)); //se aaden al panel el botn y la etiqueta panel.add(boton); panel.add(etiqueta); //se aade el panel al panel de contenido de la ventana ventana.getContentPane().add(panel, BorderLayout.CENTER); //se muestra la ventana ventana.pack(); ventana.setVisible(true); ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } }

Este cdigo adelanta algo que veremos en detalle en el apartado correspondiente, cmo asignar un aspecto y comportamiento (Look & Feel) determinado a nuestro interfaz de usuario, esto aparece en las primeras lneas del mtodo main() y en este caso se establece el aspecto y comportamiento de Java. Vamos a pasar a describir las funciones que presentan cada uno de los componentes Swing utilizados en el ejemplo. En este ejemplo la ventana representada mediante la clase JFrame es el contenedor de alto nivel, igualmente podra tratarse de otro componente de alto nivel como podra ser un objeto de la clase JApplet o JDialog. El panel es un contenedor intermedio, su propsito es el de simplificar la situacin del botn y la etiqueta, para que sea visible se le ha asignado un borde de color rojo. Otros contenedores intermedios pueden ser paneles con pestaas (JTabbedPane) y paneles de scroll (JScrollPane), que juegan un papel ms visible e interactivo dentro del interfaz de usuario. Y por ltimo y el botn y la etiqueta se denominan componentes atmicos, son componentes que no contienen otros componentes Swing, como ocurra con los anteriores, sino que tienen entidad suficiente para presentar por s mismos informacin al usuario. A menudo los componentes atmicos tienen como funcin obtener informacin del usuario. Swing ofrece un gran nmero de componentes atmicos (en este captulo veremos algunos de ellos) como pueden ser listas desplegables (JComboBox), cajas de texto (JTextField) o tablas (JTable).

155

Como se puede observar incluso el programa Swing ms sencillo presenta mltiples niveles en su jerarqua de contenedores, adems, como ya hemos dicho la raz de esta jerarqua siempre es un contenedor de alto nivel. Cada contenedor de alto nivel indirectamente contiene un contenedor intermedio llamado panel de contenido (content pane), y normalmente el panel de contenido contiene, directa o indirectamente, todos los componentes visibles de la ventana del interfaz de usuario. La excepcin a esta regla son las barras de men, que se sitan en un lugar especial fuera del panel de contenido, como vimos en el primer ejemplo de este apartado. Para aadir un componente a un contenedor se utiliza una de las distintas formas del mtodo add(), como ya vimos en el tema dedicado a los componentes AWT. Como regla general, una aplicacin Java con un interfaz de usuario basado en componentes Swing, tiene al menos una jerarqua de contenedores con un objeto de la clase JFrame como raz de la misma. Por ejemplo, si una aplicacin tiene una ventana principal y dos dilogos, la aplicacin presentar tres jerarquas de contenedores, y por lo tanto tres contenedores de alto nivel. Una de las jerarquas de contenedores tiene a un objeto de la clase JFrame como raz, y cada una de las otras dos jerarquas un objeto de la clase JDialog como raz. Un applet basado en Swing tiene al menos una jerarqua de contenedores y su raz es un objeto de la clase JApplet. Por ejemplo un applet que muestra un dilogo tiene dos jerarquas de contenedores, los componentes en la ventana del navegador tienen como raz un objeto JApplet, y los que se encuentran en el dilogo tienen como raz de su jerarqua de componentes un objeto de la clase JDialog. Hasta ahora hemos visto un par de ejemplos que por un lado nos han mostrado como funcionan las jerarquas de contenedores, y por otro, nos ha mostrado como utilizar algunos de los componentes Swing, aunque ms adelante veremos algunos de ellos con ms detalle. Para obtener una referencia al panel de contenido de un componente de alto nivel debemos utilizar el mtodo getContentPane(), que devuelve un objeto de la clase Container. Por defecto el panel de contenido es un contenedor intermedio que hereda de la clase JComponent y que tiene como gestor de diseo un BorderLayout. Una vez que tenemos una referencia al panel de contenido podremos aadir los componentes que consideremos necesarios utilizando una sentencia similar a la que muestra el siguiente fuente .
ventana.getContentPane().add(componente,BorderLayout.CENTER);

Todos los contenedores de alto nivel pueden tener, en teora, una barra de men, sin embargo en la prctica las barras de men se utilizan nicamente en ventanas y en raras ocasiones en applets. Para aadir una barra de men a un contenedor de alto nivel,
156

crearemos un objeto de la clase JMenuBar, aadiremos los elementos de men que se consideren necesarios y por ltimo se lanzar sobre el contenedor de alto nivel el mtodo setMenuBar(), pasndole por parmetro el objeto JMenuBar correspondiente.
ventana.setMenuBar(objMenuBar);

Todo contenedor de alto nivel adems de poseer un panel de contenido, poseen otro panel llamado panel raz (root pane). Normalmente este panel intermedio no se suele utilizar, su funcin es la de gestionar el panel de contenido y la barra de men junto con otros dos contenedores. Estos dos contenedores son el panel de capas (layered pane) y el panel de cristal (glass pane). El panel de capas contiene directamente la barra de men y el panel de contenido, y adems permite ordenar los componentes que se vayan aadir con detenimiento (Z-order), es decir, permite organizar los componentes del interfaz de usuario en capas. Esto puede ser til para mostrar mens de aparicin sbita (popup menus) por encima de otros componentes. El panel de cristal suele permanecer oculto y se encuentra por encima de todos los elementos del panel raz. Este panel es til para interceptar eventos o pintar sobre un rea que ya contiene otros componentes, se utiliza sobre todo para interceptar eventos de entrada que suceden sobre el contenedor de alto nivel. En la Figura 66 se puede ver un esquema que muestra la disposicin de los distintos paneles que podemos encontrar en un componente de alto nivel.

A continuacin vamos a comentar los distintos componentes de alto nivel: JFrame, JDialog y JApplet.

JFrame
Ya hemos visto este componente Swing realizando las labores de contenedor de alto nivel, un objeto de la clase JFrame representa a una ventana con bordes, ttulo y botones que permiten cerrar y maximizar la ventana. Las aplicaciones Java que poseen interfaz de usuario al menos utilizan un JFrame, y tambin lo hacen a veces los applets. Veamos la utilizacin de la clase JFrame mediante un sencillo ejemplo. Se trata de una ventana que contiene una etiqueta y que al pulsar el cierre de la misma se finalizar la ejecucin de la aplicacin.
157

import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Ventana{ public static void main(String s[]) { JFrame ventana = new JFrame("Ventana Sencilla"); ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); JLabel etiqueta = new JLabel("Soy una etiqueta"); etiqueta.setPreferredSize(new Dimension(175, 100)); ventana.getContentPane().add(etiqueta, BorderLayout.CENTER); ventana.pack(); ventana.setVisible(true); } }

En la primera lnea del mtodo main() se utiliza el constructor de la clase JFrame que nos permite indicar el ttulo de la ventana mediante una cadena que pasamos como parmetro, otra versin de este constructor es sin argumentos. A continuacin se aade un oyente para los eventos de la ventana, en este caso se implementa nicamente el mtodo windowClosing() para finalizar la ejecucin de la aplicacin. En las siguientes lneas se crea y aade una etiqueta (JLabel) al panel de contenido de la ventana. Por ltimo se lanzan los mtodos pack() y setVisible() sobre la instancia de la clase JFrame. El mtodo pack() de un tamao a la ventana de forma que todos sus contenidos tengan el tamao especificado o superior, una alternativa al mtodo pack() es le mtodo setSize() en el que se puede especificar de forma explcita las dimensiones de la ventana. Al mtodo setVisible() se le pasa el parmetro true para que muestre la ventana en la pantalla. Por defecto cuando se pulsa el botn de cierre de la ventana, la ventana se oculta, sin la necesidad de utilizar ningn tratamiento de eventos, para cambiar este comportamiento se puede utilizar el mtodo setDefaultCloseOperation() o bien implementar un tratamiento de eventos similar al del ejemplo. El parmetro que se le pasa al mtodo setDefaultCloseOperation() debe ser una de las siguientes constantes: DO_NOTHING_ON_CLOSE: en este caso cuando se pulsa el botn de cierre de la ventana no ocurre nada, la ventana sigue visible. HIDE_ON_CLOSE: es el valor por defecto, cuando se pulsa el botn de cierre la ventana se oculta, pero sigue existiendo, teniendo la posibilidad de mostrarse la ventana de nuevo si as lo indicamos en el programa.

158

DISPOSE_ON_CLOSE: al cerrar la ventana se elimina de la memoria y de la pantalla, es decir, en este caso se liberan todos los recursos que estaban siendo utilizados por la ventana. La clase JFrame hereda de la clase java.awt.Frame y por lo tanto hereda todos los mtodos de la clase Frame, como pueden ser setSize(), pack(), setTitle(), setVisible() y getTitle(). Adems de los mtodos de la clase java.awt.Frame, la clase JFrame tiene los siguientes mtodos propios: void setDefaultCloseOperation(int): asigna el tipo de operacin que va a realizar la ventana cuando el usuario pulse el botn de cierre de la ventana. Ya hemos visto las posibles acciones. getDefaultCloseOperation(): devuelve la operacin de cierre por defecto asignada a la int ventana. void setContentPane(Container): asigna a la ventana un panel de contenido, que va a ser el que contenga todos los componentes visibles del interfaz de usuario de la ventana. Este mtodo permite utilizar nuestro propio panel de contenido, lo normal es utilizar un objeto de la clase JPanel para crear el panel de contenido, en el Cdigo se puede observar como se crea un objeto JPanel que luego va a ser el panel de contenido de una ventana.
JPanel panelContenido=new JPanel(); panelContenido.setLayout(new BorderLayout()); panelContenido.add(componente,BorderLayout.CENTER); panelContenido.add(otroComponente,BorderLayout.SOUTH); ventana.setContentPane(panelContenido);

Container getContentPane(): devuelve el panel de contenido de la ventana, este mtodo se suele utilizar para una vez que tenemos una referencia al panel de contenido de la ventana aadir componentes a la misma. void setMenuBar(MenuBar): asigna a la ventana una barra de men determinada representada por un objeto de la clase MenuBar. JMenuBar getMenuBar(): devuelve la barra de men que tiene asignada la ventana. void setGlassPane(Component): permite asignar un panel de cristal a la ventana. Component getGlassPane(): devuelve el panel de cristal de la ventana.

JDialog, JOptionPane
Vamos a pasar ahora a comentar el segundo tipo de contenedores de alto nivel, los dilogos, que son un tipo de ventana ms limitada que las ventanas representadas por la clase JFrame. Hay varias clases que ofrecen dilogos: JOptionPane: permiten crear sencillos dilogos estndar. ProgressMonitor: muestra un dilogo que muestra el progreso de una operacin. JColorChooser: ofrece un dilogo estndar para la seleccin de colores. JFileChooser: ofrece un dilogo estndar para la seleccin de un fichero. JDialog: permite crear directamente dilogos completamente personalizados. Como se indica al principio de esta seccin nosotros nicamente nos vamos a encargar de las clase JOptionPane y JDialog.

159

Todo dilogo es dependiente de una ventana determinada, si la ventana se destruye tambin lo harn sus dilogos asociados. Cuando la ventana se transforma en icono sus dilogos desaparecen de la pantalla, cuando se maximiza la ventana los dilogos vuelven a aparecer. Los dilogos pueden ser modales, cuando un dilogo modal se encuentra visible se bloquea la entrada del usuario en todas las dems ventanas del programa. Los dilogos que ofrece la clase JOptionPane son modales, para crear un dilogo no modal se debe hacer uso de la clase JDialog que permitir indicar si el dilogo que se crea va a ser modal o no. La clase JDialog hereda de a clase AWT java.awt.Dialog, aade a la clase Dialog un panel raz (root pane) y soporte para la operacin por defecto de cierre. Como se puede observar son las mismas caractersticas que ofreca la clase JFrame sobre la clase Frame del AWT. Cuando se utiliza la clase JOptionPane en realidad tambin estamos haciendo uso de la clase JDialog de forma indirecta, ya que la clase JOptionPane es un contenedor que puede crear de forma automtica una instancia de la clase JDialog y aadirse al panel de contenido de ese dilogo. A continuacin vamos a comentar las distintas caractersticas de la clase JOptionPane. Mediante la clase JOptionPane podemos crear distintos tipos de dilogos, JOptionPane ofrece soporte para mostrar dilogos estndar, indicar los iconos, especificar el ttulo y texto del dilogo y los textos de los botones que aparecen. En cuanto a los iconos que se muestran en el dilogo facilitado por JOptionPane, podemos utilizar iconos personalizados, no utilizar ningn tipo de icono, o utilizar uno de los cuatro iconos estndar que ofrece la clase JOptionPane, estos iconos son los de pregunta, informacin, advertencia y error. El aspecto de estos iconos variar segn el Look & Feel (aspecto y comportamiento) que se aplique. Para mostrar dilogos modales sencillos se utilizar directamente uno de los mtodos showXXXDialog() de la clase JOptionPane. Este conjunto de mtodos son mtodos estticos y por lo tanto se lanzarn sobre una instancia concreta de la clase, sino que se lanzaran de forma directa sobre la clase. Si lo que necesitamos es controlar el comportamiento del dilogo cuando se cierre o si el dilogo no debe ser modal entonces instanciaremos un objeto de la clase JOptionPane y lo aadiremos a un objeto de la clase JDialog. Veamos a continuacin los principales mtodos showXXXDialog() de la clase JOptionPane. El primero de estos mtodos es el mtodo showMessageDialog(), que muestra un dilogo modal con un botn de aceptar. Se puede especificar el mensaje, el icono y el ttulo que muestra el dilogo. Este mtodo se encuentra sobrecargado y ofrece tres versiones distintas: void showMessageDialog(Component componentePadre, Object mensaje): el primer parmetro indica el componente padre del que depende, puede ser una ventana, un componente dentro de una ventana o nulo y el segundo el mensaje que se muestra, suele ser
160

una cadena de texto que podemos dividir en varias lneas utilizando el carcter de nueva lnea (\n). void showMessageDialog(Component componentePadre, Object mensaje, String ttulo, int tipoMensaje): en este caso se especifica tambin el ttulo, que ser una cadena, y el tipo de mensaje a mostrar. Este ltimo parmetro determinar el icono que se va a mostrar en el dilogo, para ello se utilizan una serie de constantes definidas en la clase JOptionPane, estas constantes son: PLAIN_MESSAGE (sin icono), ERROR_MESSAGE (icono de error), INFORMATION_MESSAGE (icono de informacin), WARNING_MESSAGE (icono de advertencia) y QUESTION_MESSAGE (icono de pregunta). void showMessageDialog(Component componentePadre, Object mensaje, String ttulo, int tipoMensaje, Icon icono): en esta ltima versin del mtodo podemos indicar un icono personalizado para que se muestre en el dilogo. Veamos algunos ejemplos con el mtodo showMessageDialog(). Podemos aadir las distintas sentencias de creacin de dilogos que vamos a ver ahora al ejemplo de la seccin anterior en la que tratbamos la clase JFrame, la instancia de la clase JFrame, llamada ventana va a ser el componente padre de los dilogos. Si aadimos la lnea que muestra el Cdigo, a nuestra clase Ventana de la seccin anterior, obtendremos el resultado que aparece en la Figura.
JOptionPane.showMessageDialog(ventana,"Esto es un mensaje", "Ttulo",JOptionPane.WARNING_MESSAGE);

Otras variaciones sobre la sentencia anterior se muestran a continuacin con sus correspondientes resultados.
JOptionPane.showMessageDialog(ventana,"Esto es un mensaje");

En este caso se muestra el ttulo y tipo de mensaje por defecto.

ImageIcon icono = new ImageIcon("icono.gif"); JOptionPane.showMessageDialog(ventana,"Esto es un mensaje","Ttulo", JOptionPane.INFORMATION_MESSAGE,icono);

En este otro caso indicamos un icono personalizado para que se muestre en el dilogo.
161

JOptionPane.showMessageDialog(ventana,"Esto es un mensaje","Ttulo", JOptionPane.ERROR_MESSAGE);

Como se puede comprobar en todos los ejemplos, si pulsamos el botn etiquetado como OK, el dilogo se cerrar. Otro mtodo de la clase JOptionPane que muestra un dilogo es el mtodo showConfirmaDialog(). En este caso este mtodo muestra un dilogo de confirmacin, para que el usuario seleccione entre los botones correspondientes. Este mtodo, al igual que el anterior, se encuentra sobrecargado y por lo tanto ofrece tres versiones distintas, que pasamos a comentar a continuacin. showConfirmDialog(Component componentePadre, Object mensaje): este mtodo int muestra un dilogo modal con los botones, que representan las opciones disponibles, si, no y cancelar, y adems con el ttulo por defecto. showConfirmDialog(Component componentePadre, Object mensaje, String ttulo, int int tipoOpcin): en este caso podemos especificar el ttulo del dilogo de confirmacin y el tipo de opciones que se van a mostrar, para esto ltimo utilizaremos las siguientes constantes ofrecidas por la clase JOptionPane: DEFAULT_OPTION, YES_NO_OPTION, YES_NO_CANCEL_OPTION y OK_CANCEL_OPTION. showConfirmDialog(Component componentePadre, Object mensaje, String ttulo, int int tipoOpcin, int tipoMensaje): en esta versin del mtodo podemos indicar adems el tipo de mensaje que se muestra en el dilogo, de la misma forma que lo hacamos en el mtodo showMessageDialog(). showConfirmDialog(Component componentePadre, Object mensaje, String ttulo, int int tipoOpcin, int tipoMensaje, Icon icono): en esta ltima versin especificamos un icono personalizado que se va a mostrar en el dilogo de conformacin correspondiente. Como se puede observar en todas las versiones del mtodo showConfirmDialog() se devuelve un entero (int), este entero va a recoger la seleccin que ha realizado el usuario, es decir, indicar el botn que ha sido pulsado. El entero que devuelve este mtodo se corresponde con uno de los valores de las siguientes constantes de la clase JOptionPane:
162

YES_OPTION, NO_OPTION, CANCEL_ OPTION, OK_OPTION y CLOSED_OPTION (el usuario cierra el dilogo sin pulsar ninguna de las opciones disponibles). Al igual que ocurra con el mtodo anterior, vamos a ver ejemplos de utilizacin del mtodo showConfirmDialog.
JOptionPane.showConfirmDialog(ventana,"Desea formatear el disco?");

En este caso se muestra el dilogo de confirmacin por defecto.

JOptionPane.showConfirmDialog(ventana,"Desea formatear el disco?","Confirme operacin", JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE);

El siguiente mtodo que vamos a tratar de la clase JOptionPane va a ser el mtodo showOptionDialog(). Este mtodo es mucho ms potente que los vistos anteriormente, ya que permite una mayor personalizacin del dilogo. La funcin de este mtodo es la de mostrar un dilogo modal con los botones, iconos, mensaje y ttulos especificados para que el usuario seleccione entre las distintas opciones que se le ofrecen, con este mtodo podremos indicar los botones que queremos aparezcan en el dilogo. El mtodo showOptionDialog() se diferencia del mtodo showConfirmDialog() principalmente en que permite especificar las etiquetas de los botones que aparecen y adems permite especificar la opcin seleccionada por defecto. La sintaxis de este mtodo es la siguiente: int showOptionDialog(Component componentePadre, Object mensaje,String ttulo, int tipoOpcin,int tipoMensaje, Icon icono, Object[] opciones, Object valorInicial) Vamos a comentar los distintos parmetros de este nuevo mtodo. Los tres primeros parmetros son los mismos que los vistos en el mtodo showMessageDialog() y adems tienen el mismo significado.
163

En el tipo de opcin se especifica el conjunto de opciones que se van a presentar al usuario, y por lo tanto se correspondern con las constantes vistas en el mtodo showConfirmDialog(). Los dos siguientes parmetros, tipo de mensaje y el icono personalizado, tiene el mismo cometido que el mtodo showMessageDialog(). El siguiente parmetro es un array de objetos que se va a corresponder con un array de cadenas que se van a mostrar en cada uno de los botones del dilogo, es decir, podemos utilizar nuestras propias etiquetas pera mostrar en los botones. El ltimo parmetro indica cual es la opcin que se encuentra seleccionada por defecto. Como se puede observar en la sintaxis del mtodo showOptionDialog() se devuelve un entero (int), este entero tiene la misma funcin que el que devolva el mtodo showConfirDialog(), es decir, va a recoger la seleccin que ha realizado el usuario, es decir, indicar el botn que ha sido pulsado. Se debe indicar que aunque utilicemos etiquetas personalizadas para nuestros botones, se siguen devolviendo los mismos valores de las constantes, as por ejemplo un dilogo del tipo YES_NO_OPTION siempre devolver los valores: YES_OPTION, NO_OPTION o CLOSED_OPTION. Ahora se va a mostrar ejemplos de uso del mtodo showOptionDialog(). Estos ejemplos son nicamente unas cuentas sentencias que podemos incluir, como ocurra con los mtodos anteriores, en nuestra ya conocida clase Ventana.
Object[] opciones={"Vale", "Ni hablar"}; ImageIcon icono = new ImageIcon("icono.gif"); JOptionPane.showOptionDialog(ventana,"Desea formatear el disco?","Confirme operacin", JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE, icono, opciones, opciones[1]);

Se muestra un dilogo con las opciones si/no pero con unas etiquetas de botones personalizadas, as como con un icono personalizado, adems es el segundo botn el que se encuentra seleccionado por defecto.

El ltimo mtodo que vamos a ver de la clase JOptionPane es el mtodo showInputDialog(), este mtodo va a permitir obtener informacin de entrada del usuario a travs del dilogo, ya sea a travs de una caja de texto o a travs de un una lista de opciones. El dilogo que se muestra va a tener dos botones, uno de aceptar y otro de cancelar, si el usuario pulsa el botn de aceptar indicar que ha introducido una informacin que podemos recuperar. Este mtodo se encuentra sobrecargado, y ofrece las siguientes versiones: Object showInputDialog(Component componentePadre, Object mensaje): muestra un dilogo con el mensaje correspondiente que requiere una entrada del usuario a travs de una caja de texto.
164

Object showInputDialog(Component componentePadre, Object mensaje, String ttulo, int tipoMensaje): en esta caso nos permite especificar un ttulo y un tipo de mensaje (ya conocemos las constantes correspondientes). Object showInputDialog(Component componentePadre, Object mensaje, String ttulo, int tipoMensaje, Icon icono, Object opciones, Object opcinSeleccionada): en este mtodo se permite indicar un icono personalizado y un conjunto de opciones en forma de lista desplegable para que el usuario seleccione una de ellas adems se permite seleccionar una opcin por defecto. String showInputDialog(Object mensaje): en este caso no se indica nada ms que el mensaje que se va a mostrar al usuario, sin utilizar ningn componente padre, por lo que el dilogo se situar en el centro de la pantalla, ya que en los otros casos se centra siempre con respecto al componente padre, aunque de todas forma sigue bloqueando a la ventana que lo ha generado. Como se puede comprobar siempre se devuelve un objeto de la clase String, que se va a corresponder con una cadena que va a representar la informacin indicada por el usuario, ya sea a travs de una caja de texto una de una lista de opciones disponibles. En los siguientes ejemplos se muestra la utilizacin del mtodo showInputDialog(). En este caso se va a recoger el dato facilitado por el usuario y mostrarlo en la salida estndar de la aplicacin.
String respuesta=JOptionPane.showInputDialog(ventana,"Cul es tu nombre?"); System.out.println("El nombre del usuario es: "+respuesta);

Object[] valores={"Rojo","Verde","Azul","Negro","Amarillo"}; ImageIcon icono = new ImageIcon("icono.gif"); Object respuesta=JOptionPane.showInputDialog(ventana,"Cul es tu color favorito?","Seleccin de color",JOptionPane.QUESTION_MESSAGE, icono,valores,valores[2]); System.out.println("El color favorito del usuario es el: "+respuesta);

En este caso se muestra una lista con las opciones disponibles, para que el usuario seleccione la que desee. Tambin se ha utilizado un icono personalizado.

165

Hasta ahora hemos visto los mtodos que nos ofrece la clase JOptionPane para mostrar distintos tipos de dilogo. Estos dilogos tienen en comn una serie de caractersticas: al pulsar alguno de los botones que contienen se cierran, tambin se cierran cuando el usuario pulsa el cierre del dilogo y por ltimo son todos modales. En algunos casos estos dilogos nos servirn, ya que su comportamiento se puede adecuar a nuestras necesidades, pero en otros casos esto puede no ser as. En algunos casos nos puede interesar validar la informacin ofrecida por el usuario en un dilogo, o tambin nos puede interesar utilizar un dilogo que no sea modal. En estos casos en los que deseamos personalizar al mximo los dilogos utilizaremos la clase JDialog conjuntamente con la clase JOptionPane. Para ello necesitamos crear una instancia de la clase JOptionPane para aadirla al objeto JDialog correspondiente. La clase JOptionPane presenta mltiples constructores que permiten especificar el mensaje que se va a mostrar, el tipo de mensaje, el tipo de opciones, el icono personalizado, las opciones disponibles y la opcin seleccionada por defecto. Una vez creado el objeto JOptionPane, deberemos crear el objeto JDialog que lo va a contener, algunos de los constructores ofrecidos por la clase JDialog son: JDialog(): crea un dilogo no modal, sin ttulo definido y sin ninguna ventana propietaria. JDialog(Frame ventanaPropietaria): se indica la ventana a la que se encuentra asociado el dilogo. JDialog(Frame ventanaPropietaria, boolean modal): se indica si el dilogo va a ser modal o no. JDialog(Frame ventanaPropietaria, boolean modal, String ttulo): se especifica adems el ttulo que va a tener el dilogo. Si ya tenemos instanciados el objeto JOptionPane y el objeto JDialog, nicamente nos queda asignar al dilogo (JDialog) su contenido (JOptionPane), para ello utilizamos el mtodo setContentPane() de la clase JDialog, y que es comn, como ya hemos comentado, a todos los contenedores de alto nivel. A continuacin se muestra este proceso con un ejemplo que consiste en una sencilla aplicacin Java que muestra una ventana y un dilogo asociada a la misma.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Dialogo{ public static void main(String s[]) { JFrame ventana = new JFrame("Ventana Sencilla"); ventana.setSize(175,100); ventana.setVisible(true); JOptionPane contenido=new JOptionPane("Esto es un mensaje", JOptionPane.INFORMATION_MESSAGE,JOptionPane.YES_NO_CANCEL_OPTION); JDialog dialogo=new JDialog(ventana,"Esto es un dilogo",true); dialogo.setContentPane(contenido); dialogo.setLocationRelativeTo(ventana); dialogo.pack(); dialogo.setVisible(true); } }
166

El resultado es :

Como se puede comprobar la pulsacin de los botones del dilogo no tiene el efecto que tenan anteriormente, si queremos que realicen alguna operacin cuando se pulsen, deberemos hacerlo a travs de una gestin de eventos a travs del cdigo de nuestra aplicacin. Aunque si pulsamos el cierre del dilogo ste se sigue cerrando, ya que la operacin tiene asignada por defecto para el cierre es HIDE_ON_CLOSE, es decir, ocultarse en el cierre, como ocurra con la clase JFrame. En el cdigo se puede observar que se utiliza el mtodo setLocationRealtiveTo(), este mtodo centra el dilogo de forma relativa al componente que le pasamos por parmetro. Otros mtodos de la clase JDialog son los siguientes: Container getContentPane(): devuelve el panel de contenido del dilogo. getDefaultCloseOperation(): devuelve la operacin de cierre por defecto que tiene int asignada el dilogo. void setDefaultCloseOperation(int operacion): asigna una operacin de cierre por defecto al dilogo.

JApplet
Este es el tercero de los contenedores de alto nivel. Esta clase hereda de la clase java.applet.Applet, es por lo tanto la versin Swing de la clase Applet. La clase JApplet aporta dos caractersticas esenciales a los applets, ofrece soporte para tecnologas de rehabilitacin y ofrece un panel raz, con todo lo que ello supone, es decir, aadir componentes al panel de contenido, posibilidad de tener una barra de men, etc.

Contenedores intermedios
Los contenedores intermedios son contenedores Swing, que aunque no son contenedores de alto nivel, su labor principal es la de contener otros componentes. Estos contenedores se siguen basando en la jerarqua de contenedores de Swing que ya hemos visto anteriormente. Swing ofrece una serie de contenedores de propsito general: JPanel: es le ms flexible y utilizado de todos ellos. Se utiliza normalmente para agrupar componentes, se le puede asignar gestores de diseo y bordes. Los paneles de contenido de los contenedores de alto nivel suelen ser de la clase JPanel. JScrollPane: ofrece una vista de scroll de un componente, suele contener componentes grandes o que pueden crecer. Se utilizan debido a las limitaciones del tamao de la pantalla.
167

JSplitPane: este panel agrupa dos componentes, uno al lado del otro. El usuario puede ajustar la lnea divisora que separa ambos componentes arrastrndola. El aspecto es similar al que ofrece, por ejemplo, el explorador de Windows, la vista del rbol de directorios se encuentra separada de la vista de contenidos de un directorio. JTabbedPane: contiene muchos componentes pero slo puede mostrar un conjunto de ellos a la vez, el usuario puede ir cambiando entre los distintos conjuntos de manera sencilla. Un ejemplo podran ser las distintas pestaas de una hoja de propiedades. JToolBar: grupa una serie de componentes (normalmente botones) en una fila o en una columna, pueden permitir al usuario arrastrar la barra a distintos lugares. Un ejemplo pueden ser las barras de herramientas de un procesador de textos como MS Word. Adems tambin existen los siguientes contenedores intermedios que se encuentran ms especializados: JInternalFrame: permite mostrar una ventana dentro de otra. Parece una ventana y ofrece toda su funcionalidad pero debe parecer siempre dentro de otra ventana (contenedor de alto nivel) que la contiene. JLayeredPane: este panel ofrece una tercera dimensin, la profundidad, para poder posicionar componentes de esta forma. Esta tercera dimensin se denomina tambin Zorder. Al aadir un componente a un panel de este tipo se especifica su profundidad mediante un entero, cuanto mayor sea el entero especificado mayor ser la profundidad en la que se sita el componente. JRootPane: esta clase representa el panel raz de un contenedor de alto nivel, como ya vimos en el apartado anterior el panel raz esta formado por las siguientes partes: panel de capas, panel de contenido, panel de cristal y barra de men. A continuacin vamos a comentar algunos de estos paneles intermedios, no vamos a tratar todos ya nos excederamos en la extensin del presente curso, no se debe olvidar que Swing tienen un gran nmero de componentes.

JPanel
Esta clase permite construir paneles de propsito general para contener componentes Swing. Por defecto un panel no muestra nada en pantalla a excepcin de su fondo, tambin por defecto los paneles son opacos, aunque se pueden hacer transparentes mediante el mtodo setOpaque() pasndole el valor false por parmetro. La clase JPanel es la versin Swing de la clase Panel de AWT. La clase JPanel permite asignar un gestor de diseo al panel para indicar la forma en la que se van aadiendo los distintos componentes al mismo, por defecto el gestor de diseo de un objeto JPanel es un FlowLayout. Swing utiliza los mismos gestores de diseo que vimos para los componentes AWT, de hecho se debe importar el paquete JAVA.AWT para poder utilizarlos, pero se aade un nuevo gestor mediante la clase BoxLayout, que veremos en el apartado correspondiente. Para aadir componentes lo haremos de la misma forma que veamos en los paneles AWT, es decir, con las distintas versiones de los mtodos add(). En el Cdigo siguiente se muestra la forma de utilizar esta clase.
import java.awt.*;
168

import java.awt.event.*; import javax.swing.*; public class Panel{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana Sencilla"); JPanel panel=new JPanel(); panel.setLayout(new BorderLayout()); JLabel etiqueta1 = new JLabel("Soy una etiqueta",JLabel.CENTER); etiqueta1.setPreferredSize(new Dimension(175, 100)); JLabel etiqueta2 = new JLabel("Soy otra etiqueta",JLabel.CENTER); etiqueta2.setPreferredSize(new Dimension(175, 100)); panel.add(etiqueta1, BorderLayout.NORTH); panel.add(etiqueta2, BorderLayout.SOUTH); ventana.getContentPane().add(panel, BorderLayout.CENTER); ventana.pack(); ventana.setVisible(true); } }

Se trata simplemente de aadir dos etiquetas a un panel (JPanel) y aadir este panel al panel de contenido de una ventana (JFrame), el resultado se puede apreciar en la Figura.

. La clase JPanel hereda de la clase JComponent, y debido a ello permite utilizar una nueva funcionalidad ofrecida por Swing, los bordes. Cada objeto de la clase JComponent puede tener uno o ms bordes. Los bordes no son realmente componentes, sino que se utilizan para delimitar visualmente una serie de componentes de otros. Para asignar un borde a un componente, en este caso un objeto de la clase JPanel, se utiliza el mtodo setBorder(). Para crear los distintos bordes que ofrece Swing se suele utilizar la clase BorderFactory. Esta clase ofrece un gran nmero de mtodos que permiten crear distintos tipos de bordes: de lneas, elevados, hundidos, marcados, con ttulos, una combinacin de dos bordes, etc. Veamos un ejemplo que asigna distintos bordes a varios paneles dentro de una ventana.
import import import public public java.awt.*; java.awt.event.*; javax.swing.*; class Panel{ static void main(String args[]) {
169

JFrame ventana = new JFrame("Ventana Sencilla"); ventana.getContentPane().setLayout(new GridLayout(7,1)); ((JPanel)ventana.getContentPane()).setBorder( BorderFactory.createEmptyBorder(10,10,10,10)); JPanel panel1=new JPanel(); JLabel etiqueta1 = new JLabel("Borde tipo lnea",JLabel.CENTER); panel1.setBorder(BorderFactory.createLineBorder(Color.black)); panel1.add(etiqueta1); ventana.getContentPane().add(panel1); JPanel panel2=new JPanel(); JLabel etiqueta2 = new JLabel("Borde elevado",JLabel.CENTER); panel2.setBorder(BorderFactory.createRaisedBevelBorder()); panel2.add(etiqueta2); ventana.getContentPane().add(panel2); JPanel panel3=new JPanel(); JLabel etiqueta3 = new JLabel("Borde hundido",JLabel.CENTER); panel3.setBorder(BorderFactory.createLoweredBevelBorder()); panel3.add(etiqueta3); ventana.getContentPane().add(panel3); JPanel panel4=new JPanel(); JLabel etiqueta4 = new JLabel("Borde decorado",JLabel.CENTER); panel4.setBorder(BorderFactory.createMatteBorder(5,5,5,5,Color.red)); panel4.add(etiqueta4); ventana.getContentPane().add(panel4); JPanel panel5=new JPanel(); JLabel etiqueta5 = new JLabel("Borde con ttulo",JLabel.CENTER); panel5.setBorder(BorderFactory.createTitledBorder( BorderFactory.createLineBorder(Color.blue),"Ttulo")); panel5.add(etiqueta5); ventana.getContentPane().add(panel5); JPanel panel6=new JPanel(); JLabel etiqueta6 = new JLabel("Borde grabado",JLabel.CENTER); panel6.setBorder(BorderFactory.createEtchedBorder()); panel6.add(etiqueta6); ventana.getContentPane().add(panel6); JPanel panel7=new JPanel(); JLabel etiqueta7 = new JLabel("Borde compuesto",JLabel.CENTER); panel7.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(Color.yellow), BorderFactory.createRaisedBevelBorder())); panel7.add(etiqueta7); panel7.setPreferredSize(new Dimension(175,50)); ventana.getContentPane().add(panel7); ventana.pack(); ventana.setVisible(true); } }

170

Comentarios del cdigo: El primero de ellos es referente al tipo de borde utilizado en el panel de contenido, como se puede observar al recuperar el panel de contenido para asignarle un borde vaco se ha tenido que hacer un "cast" con la clase JPanel, este borde vaco se ha utilizado para crear un margen entre el panel de contenido y el contenedor, en este caso una instancia de la clase JFrame. Al panel de contenido de la ventana se le ha asignado un gestor de diseo GridLayout con siete filas y una nica columna. Algunos de los mtodos de la clase BorderFactory se encuentran sobrecargados, para permitir una mayor personalizacin de los distintos tipos de bordes que queremos asignar. A cada panel se le ha asignado un borde distinto y se le ha aadido una etiqueta (JLabel) con la descripcin del tipo de borde correspondiente.

JTabbedPane
Gracias a este contenedor intermedio podemos tener distintos componentes, normalmente otros paneles, compartiendo un mismo espacio. El usuario puede visualizar los componentes que desea ver seleccionando la pestaa correspondiente. Para crear un contenedor de este tipo primero debemos instanciar el objeto correspondiente de la clase JTabbedPane, luego se van creando los distintos componentes y se van aadiendo al contenedor mediante el mtodo addTab().

171

Antes de seguir con esta nueva clase vamos a verla en accin mediante un sencillo ejemplo. El ejemplo consiste simplemente en un objeto JTabbedPane al que se le van aadiendo distintos paneles, cada uno con sus componentes.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class PanelTab{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana Sencilla"); ImageIcon icono = new ImageIcon("icono.gif"); JTabbedPane panelTab = new JTabbedPane(); //tratamiento de eventos para el cierre de la ventana ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); //se van creando paneles con componentes y se aaden al objeto JTabbedPane JPanel panel1=new JPanel(); panel1.add(new JButton("Botn",icono)); //se crea una nueva pestaa con el nuevo componente panelTab.addTab("Uno",icono,panel1,"Soy la primera pestaa"); panelTab.setSelectedIndex(0); JPanel panel2=new JPanel(); panel2.add(new JLabel("Soy una etiqueta",JLabel.CENTER)); panelTab.addTab("Dos",icono,panel2,"Soy la segunda pestaa"); JPanel panel3=new JPanel(); panel3.add(new JLabel("Soy otra etiqueta",JLabel.CENTER)); panel3.setBorder(BorderFactory.createLineBorder(Color.red)); panelTab.addTab("Tres",icono,panel3,"Soy la tercera pestaa"); JPanel panel4=new JPanel(); panel4.add(new JToggleButton("Otro botn",icono)); panelTab.addTab("Cuatro",icono,panel4,"Soy la cuarta pestaa"); JPanel panel5=new JPanel(); panel5.add(new JSlider(JSlider.HORIZONTAL,0,30,10)); panelTab.addTab("Cinco",icono,panel5,"Soy la ltima pestaa"); //se le da un tamao al contenedor de pestaas panelTab.setPreferredSize(new Dimension(400,200)); //se aade a la ventana ventana.getContentPane().add(panelTab); ventana.pack(); ventana.setVisible(true); } }

172

Si se prueba el ejemplo anterior se puede comprobar que no se ha escrito ningn cdigo para realizar el tratamiento de eventos, la clase JTabbedPane realiza este tratamiento (mostrar los componentes de la pestaa seleccionada) de forma automtica. En el ejemplo se ha utilizado un constructor de la clase JTabbedPane que no utiliza ningn parmetro, pero existe otro constructor que permite, mediante una constante que recibe como parmetro, especificar la localizacin de las pestaas. Estas constantes se encuentran definidas en la clase JTabbedPane y son las siguientes TOP, BOTTOM, LEFT y RIGHT. As si modificamos el ejemplo anterior cambiando el constructor utilizado para el panel JTabbedPane mediante la siguiente lnea de cdigo:e
JTabbedPane panelTab = new JTabbedPane(JTabbedPane.BOTTOM);

Obtenemos el resultado siguiente:

Para aadir una nueva pestaa ya sabemos que debemos utilizar el mtodo addTab(), este mtodo presenta las siguientes versiones: addTab(String texto, Component componente): el primer argumento indica el texto que va a parecer en la pestaa y el componente que va a contener. addTab(String texto, Icon icono, Component componente): en este caso adems se indica el icono que se va a mostrar en la pestaa. addTab(String texto, Icon icono, Component componente, String ayuda): en la ltima versin del mtodo addTab() se permite especificar el texto que aparecer a modo de ayuda (tooltip) cuando situemos el puntero del ratn sobre la pestaa.
173

En el ejemplo se ha utilizado el mtodo setSelectedIndex() para indicar la pestaa que por defecto se encuentra seleccionada, que ser la que mostrar sus componentes. Las pestaas comienzan a numerarse en cero. Para manipular las pestaas la clase JTabbedPane ofrece los siguientes mtodos: insertTab(String texto, Icon icono, Component componente, String ayuda, int ndice): inserta una nueva pestaa en la posicin indicada por el parmetro ndice. removeTabAt(int ndice): elimina la pestaa cuya posicin coincida con la indicada. remove(Component componente): elimina la pestaa que contenga el componente especificado como argumento. removeAll(): elimina todas las pestaas. indexOfTab(String texto): devuelve el ndice de la pestaa que posea el texto indicado int por parmetro. getSelectedIndex(): devuelve el ndice de la pestaa seleccionada actualmente. int Tambin es posible modificar la apariencia de las pestaas del panel JTabbedPane. Podemos indicar el icono que va a mostrar la pestaa segn se encuentre habilitada o deshabilitada, tambin el color de fondo y el del texto de la pestaa. As si una vez creado el panel de nuestro ejemplo aadimos las lneas de cdigo que se muestran en el Cdigo fuente siguientey obtenemos el resultado que aparece en la Figura.
//color de fondo cuando no est seleccionada la pestaa panelTab.setBackgroundAt(0,Color.red); //color del texto panelTab.setForegroundAt(0,Color.green); ImageIcon otroIcono = new ImageIcon("icono2.gif"); //icono que se muestra cuando la pestaa est desactivada panelTab.setDisabledIconAt(0,otroIcono); //se modifica el ttulo panelTab.setTitleAt(2,"Otro texto"); JPanel panelNuevo=new JPanel(); panelNuevo.add(new JTextArea(5,30)); //se cambia el componente que posee la pestaa panelTab.setComponentAt(3,panelNuevo);

Curioso resultado ya que no se muestra el icono que se utiliza para indicar que la pestaa est deshabilitada. Si utilizamos cualquiera de los mtodos anteriores ,que tiene como parmetro el ndice de la pestaa, y la pestaa no existe se producir una excepcin.

174

JToolBar
Esta clase representa una barra de herramientas, normalmente este tipo de contenedor va a contener botones con iconos dentro de una fila o columna. Estos botones cumplen las mismas funciones que las opciones de men. Por defecto el usuario puede arrastrar la barra de herramientas y situarla en los diferentes bordes del contenedor o bien como una ventana independiente. Para que este funcionamiento de arrastre de la barra sea correcto, el contenedor en el que se sita la barra de herramientas debe tener un gestor de diseo BorderLayout. Normalmente la barra de herramientas se aade en el norte del gestor de diseo y el componente al que afecta en el centro, no debe existir ningn componente ms en el centro del contenedor. En el Cdigo siguiente se puede observar como se utiliza un objeto de la clase JToolBar. En nuestro caso vamos a tener una ventana (JFrame) que va a tener un panel de contenido al que se va a aadir la barra de herramientas (JToolBar) y un rea de texto (JTextArea). Como se puede ver la barra de herramientas puede contener otros tipos de componentes, no slo botones.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class BarraHerramientas{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con barra de herramientas"); //tratamiento de eventos para el cierre de la ventana ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); //iconos ImageIcon icono1=new ImageIcon("icono1.gif"); ImageIcon icono2=new ImageIcon("icono2.gif"); ImageIcon icono3=new ImageIcon("icono3.gif"); //botones JButton boton1=new JButton(icono1); JButton boton2=new JButton(icono2); JButton boton3=new JButton(icono3); //lista con elementos JComboBox combo=new JComboBox(); combo.addItem("uno"); combo.addItem("dos"); combo.addItem("tres"); //caja de texto JTextField caja=new JTextField("caja de texto"); //barra de herramientas JToolBar barra=new JToolBar(); //se aaden los botones barra.add(boton1); barra.add(boton2); barra.add(boton3); //se aade el separador a la barra de herramientas barra.addSeparator(); barra.add(combo); barra.add(caja); //se aade la barra al panel de contenido
175

ventana.getContentPane().add(barra,BorderLayout.NORTH); //se crea un rea de texto JTextArea areaTexto = new JTextArea(5,30); //se aade a un panel de scroll JScrollPane scrollPane = new JScrollPane(areaTexto); //se aade el panel de scroll al panel de contenido ventana.getContentPane().add(scrollPane,BorderLayout.CENTER); //se asigna un tamao preferido a la ventana ((JPanel)ventana.getContentPane()).setPreferredSize(new Dimension(400, 100)); ventana.pack(); ventana.setVisible(true); } }

Como se puede comprobar en el cdigo anterior, para aadir un separador a la barra de herramientas se utiliza el mtodo addSeparator(). Si queremos que la barra de herramientas permanezca fija se puede lanzar sobre el objeto de la clase JToolBar el mtodo setFloatable() pasando como argumento el valor false. Para posicionar los distintos elementos que contiene, la clase JToolBar utiliza el gestor de diseo BoxLayout, que veremos ms adelante en el apartado dedicado al mismo.

JLayeredPane
Este contenedor ofrece una tercera dimensin que permite posicionar los componentes que contiene especificando una profundidad. La profundidad de un determinado componente se especifica mediante un entero, cuanto mayor sea este entero mayor ser la profundidad del componente correspondiente. Si los componentes que contiene el panel de capas se superponen los componentes que se encuentran a una mayor profundidad se muestran encima de los de una menor profundidad. Vimos que los contenedores de alto nivel de Swing contienen un panel de raz que a su vez contiene un panel de capas (layered pane), normalmente no se suele utilizar el JLayeredPane del JRootPane, sino que se crea un objeto JLayeredPane distinto para utilizarlo dentro de otro panel. Esto mismo ocurre en nuestro ejemplo, que se sita un objeto de la clase JLayeredPane dentro del panel de contenido de una ventana (JFrame). Para aadir un componente a un JLayeredPane se utiliza el mtodo add(), en este mtodo se debe indicar la profundidad del componente, es decir, la capa en la que se encuentra. En el siguiente ejemplo se van aadiendo a una instancia de la clase JLayeredPane etiquetas con color de fondo a distintas profundidades. Veamos el Cdigo:.
import import import public java.awt.*; java.awt.event.*; javax.swing.*; class PanelCapas{
176

static private String[] textosCapa = { "Amarillo(0)", "Magenta (1)", "Azul (2)", "Rojo (3)", "Verde (4)" }; static private Color[] coloresCapa = { Color.yellow, Color.magenta, Color.blue, Color.red, Color.green }; public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con panel de capas"); JLayeredPane panelCapas=new JLayeredPane(); //tratamiento de eventos para el cierre de la ventana ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); //punto de origen Point origen = new Point(10, 20); //separacin entre etiquetas int separacion = 35; //se van creando las etiquetas de color a distinta profundidad for (int i = 0; i < textosCapa.length; i++) { JLabel etiqueta = creaEtiqueta(textosCapa[i],coloresCapa[i],origen); //se aade la etiqueta al panel de capas panelCapas.add(etiqueta,new Integer(i)); origen.x += separacion; origen.y += separacion; } //se indica un tamao para le panel panelCapas.setPreferredSize(new Dimension(300, 310)); //se aade el panel creado al panel de contenido de la ventana ventana.getContentPane().add(panelCapas); ventana.pack(); ventana.setVisible(true); } //mtodo para la creacin de etiquetas con un texto y un color en un punto //de origen. private static JLabel creaEtiqueta(String texto,Color color,Point origen) { JLabel etiqueta = new JLabel(texto); //se alinea el texto en la etiqueta etiqueta.setVerticalAlignment(JLabel.TOP); etiqueta.setHorizontalAlignment(JLabel.CENTER); etiqueta.setOpaque(true); etiqueta.setBackground(color); //se asigna un borde a la etiqueta etiqueta.setBorder(BorderFactory.createLineBorder(Color.black)); etiqueta.setBounds(origen.x, origen.y, 140, 140); return etiqueta; } }

177

Como se puede comprobar a la vista del cdigo se han creado dos arrays que contienen por un lado los textos de las etiquetas y por otro los colores de las etiquetas. Se ha utilizado un bucle for para ir recorriendo estos arrays y crear las etiquetas mediante el mtodo crearEtiqueta(). Como se puede ver el mtodo y los atributos utilizado son estticos, ya que los utilizamos directamente en el mtodo main() si lanzarlos sobre una instancia de una clase. Dentro de una capa, es decir, a una profundidad determinada, se puede especificar la posicin de un componente. Es posible por lo tanto definir la posicin de un componente respecto al resto de componentes dentro de la misma capa, para ello existe una versin del mtodo add() que posee un tercer parmetro para indicar esta posicin dentro de la capa. El valor de esta posicin va desde -1 hasta n-1, dnde n es el nmero de componentes dentro de la capa. Al contrario que las capas, cuanto menor es el nmero mayor es la profundidad del componente dentro de la capa. Utilizar -1 es equivalente a utilizar n-1, indica la posicin ms en el fondo. Si utilizamos 0 el componente se encontrar por encima del resto. Si al ejemplo anterior le aadimos las lneas que muestra el Cdigo siguiente, una vez creadas las etiquetas en las distintas capas.
ImageIcon icono = new ImageIcon("imagen.gif"); JLabel nuevaEtiqueta=new JLabel(icono); nuevaEtiqueta.setBounds(180,75,icono.getIconWidth(),icono.getIconHeight()); panelCapas.add(nuevaEtiqueta,new Integer(2),0);

Lo que se hace es aadir una nueva etiqueta con un icono en la capa 2 de modo que quede por encima de la etiqueta de color que ya exista en esa misma capa. El nuevo aspecto del panel de capas es el que aparece en la Figura.
178

Tambin es posible mover un componente de una capa a otra, para ello se utiliza el mtodo setLayer(). As si queremos mover la etiqueta con el icono a la capa 4 escribiremos lo que indica la lnea de codigo siguiente:
panelCapas.setLayer(nuevaEtiqueta,4,0);

El ltimo argumento del mtodo setLayer() es la posicin del componente dentro de la nueva capa. Para mover un componente dentro de una capa a la primera posicin se utiliza el mtodo moveToFront(), y para enviarlo al fondo el mtodo moveToBack(), ambos mtodos tiene como parmetro el componente al que se quiere cambiar de posicin dentro de una capa. Si utilizamos el mtodo moveToBack() en nuestro cdigo tendr un nuevo aspecto del ejemplo :.
panelCapas.moveToBack(nuevaEtiqueta);

179

Interfaces de usuario en Java: componentes atmicos de Swing


Componentes atmicos
Este grupo de componentes se corresponde con aquellos componentes cuya funcin es presentar o recibir informacin, en lugar de contener otros componentes, aunque es posible encontrar componentes atmicos que son la combinacin de distintos componentes. Un ejemplo de componente atmico podra ser un botn, una caja de texto, una casilla de verificacin, etc. Todos los componentes atmicos heredan de la clase JComponent, debido a esto todos ellos soportan caractersticas estndar de los componentes Swing, como pueden ser bordes y tooltips. Los componentes atmicos se subclasifican atendiendo a la labor que realizan. Componentes que cuya misin principal es la de obtener informacin relativa a la entrada del usuario. JButton: un botn comn. JCheckBox: una casilla de verificacin. JRadioButton: un botn de opcin que suelen utilizarse en grupos. JMenuItem: un elemento de un men. JCheckBoxMenuItem: un elemento de men que contiene una casilla de verificacin. JRadioButtonMenuItem: un elemento de men que contiene un botn de opcin. JMenuBar: una barra de men. JMenu: una opcin de men. JToggleButton: representa un botn con dos estados (pulsado/no pulsado). JComboBox: una lista desplegable. JList: una lista con elementos. JSlider: permite seleccionar al usuario un valor dentro de un rango determinado. JTextField: una caja de texto en la que el usuario puede escribir. Componentes que existen nicamente para mostrar informacin: JLabel: etiqueta que puede mostrar texto, un icono o ambos. JProgressBar: barra que muestra el progreso de un proceso. JToolTip: muestra una breve descripcin de un componente, aunque veremos en los distintos ejemplos que nunca vamos a utilizar directamente esta clase. Componentes que muestran una informacin estructurada o que permiten editarla: JColorChooser: permite realizar la seleccin de un color determinado. JFileChooser: permite seleccionar ficheros y directorios. JTable: muestra la informacin en formato de tabla. JTextComponent: de esta clase heredan distintas clases especializadas en el tratamiento de textos. JTree: muestra datos organizados de forma jerrquica. A lo largo de los siguientes apartados se van a mostrar algunos de estos componentes con sus respectivos ejemplos.

180

Componentes para obtener informacin


Vamos a comenzar con algunos de los componentes atmicos de Swing cuya misin es la de obtener informacin a travs de la entrada del usuario.

JButton
Esta clase hereda de la clase AbstractButton, al igual que lo hacen otras clases como pueden ser JCheckbox, JMenuItem o JToggleButton. Por lo tanto la clase JButton posee una serie de funcionalidades que son comunes a todas las clases que heredan de la clase AbstractButton. Un botn puede contener texto o imgenes o ambos elementos. El texto que contiene un botn se puede alinear con respecto a la imagen, tambin se pueden especificar teclas de teclado alternativas, que se indicarn mediante el subrayado de la letra del texto correspondiente. Cuando un botn se encuentra deshabilitado el Look and Feel correspondiente genera de forma automtica el aspecto del botn. Sin embargo, se puede indicar una imagen para que se muestre cuando el botn se encuentre deshabilitado. Para ver la clase JButton en accin vamos a utilizar un ejemplo muy sencillo que consiste en mostrar tres botones con imgenes y ayudas (tooltips), la pulsacin de dos de los tres botones activar o desactivar el botn central.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Botones extends JFrame implements ActionListener{ private JButton botonIzq; private JButton botonDer; private JButton botonCentro; private ImageIcon iconoIzq=new ImageIcon("icono3.gif"); private ImageIcon iconoDer=new ImageIcon("icono1.gif"); private ImageIcon iconoCentro=new ImageIcon("icono2.gif"); public Botones (){ super("Ventana con botones"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaBotones(){ //se instancia el botn indicando la imagen botonIzq=new JButton("Desactiva botn central",iconoIzq); //se da formato al texto botonIzq.setVerticalTextPosition(AbstractButton.CENTER); botonIzq.setHorizontalTextPosition(AbstractButton.LEFT); //se indica la tecla asociada al botn botonIzq.setMnemonic(KeyEvent.VK_D); //se indica el comando de accin que se utiliza cuando se pulsa el botn botonIzq.setActionCommand("desactiva"); //se asigna un tooltip botonIzq.setToolTipText("Desactivo el botn central"); botonIzq.setEnabled(false); botonDer=new JButton("Activa botn central",iconoDer); botonDer.setVerticalTextPosition(AbstractButton.CENTER);
181

botonDer.setHorizontalTextPosition(AbstractButton.RIGHT); botonDer.setMnemonic(KeyEvent.VK_A); botonDer.setActionCommand("activa"); botonDer.setToolTipText("Activo el botn central"); botonCentro=new JButton("Botn central",iconoCentro); botonCentro.setEnabled(false); botonCentro.setToolTipText("No hago nada"); //se registran los oyentes, que son la misma clase. botonIzq.addActionListener(this); botonDer.addActionListener(this); } public void aadeBotones(){ //creamos un panel para aadir los botones JPanel panelContenido=new JPanel(); panelContenido.add(botonIzq); panelContenido.add(botonCentro); panelContenido.add(botonDer); //establecemos este panel como panel de contenido de la ventana setContentPane(panelContenido); } public void actionPerformed(ActionEvent evento){ if (evento.getActionCommand().equals("desactiva")){ //se desactiva el botn central y se actualiza //el estado de los otros botones botonCentro.setEnabled(false); botonIzq.setEnabled(false); botonDer.setEnabled(true); }else{ //se activa el botn central botonCentro.setEnabled(true); botonIzq.setEnabled(true); botonDer.setEnabled(false); } } public static void main(String args[]) { Botones ventana = new Botones(); ventana.creaBotones(); ventana.aadeBotones(); ventana.pack(); ventana.setVisible(true); } }

Se puede comprobar si pulsamos las teclas Alt+D o Alt+A tienen el mismo efecto que pulsar el botn correspondiente, adems esta combinacin de teclas aparece descrita en el tooltip. Podemos indicar el botn activo por defecto de un contenedor de alto nivel mediante el mtodo setDefaultButton() de la clase JRootPane. El botn activo por defecto aparece destacado del resto y si el usuario pulsa la tecla Enter es equivalente a pulsar este botn. Podemos modificar el ejemplo anterior para que el botn activo por defecto sea el botn de la derecha.
getRootPane().setDefaultButton(botonDer);

182

Otra caracterstica que nos ofrece la clase JButton es la posibilidad de utilizar etiquetas HTML dentro del texto del botn, para ello se debe poner la etiqueta <html> al inicio del texto, y a continuacin se pueden utilizar las etiquetas HTML que se consideren necesarias para dar el formato conveniente al texto. As por ejemplo, si retomamos nuestro cdigo anterior y modificamos las lneas en las que se crean los botones, escribiendo:
botonIzq=new JButton("<html><b><i><u>D</u>esactiva botn central</i><b>",iconoIzq); .......... botonDer=new JButton("<html><font color='red' size='4'>"+ "<u>A</u>ctiva botn central</font>",iconoDer); ........... botonCentro=new JButton("<html><small><i>Botn central</i></small>",iconoCentro);

Como se puede apreciar aunque los botones se encuentran desactivas, ahora el texto no aparece en color gris atenuado. Adems hemos tenido que utilizar la etiqueta de subrayado de HTML (<u></u>) para indicar la tecla que se corresponde con cada botn.

JCheckbox
Esta clase representa a las casillas de verificacin, tambin se pueden utilizar estas casillas de verificacin dentro de elementos de men, mediante la clase JCheckBoxMenuItem. Debido a que la clase JCheckBox hereda de la clase AbstractButton, presenta una serie de caractersticas comunes a todos los tipos de botones, que ya hemos visto en la seccin anterior con la clase JButton. Estas caractersticas son la posibilidad de utilizar imgenes, tooltips, teclas alternativas, etc. Las casillas de verificacin se suelen agrupar y es posible seleccionar, una, algunas o ninguna de ellas. En el siguiente ejemplo se muestra la utilizacin de la clase JCheckBox para configurar el aspecto de un botn. Se dispone de tres casillas de verificacin, una para indicar que el botn va a tener un icono, otra para indicar que tiene un tooltip y otra para indicar que tiene texto.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Casillas extends JFrame implements ItemListener{ //cada una de las casillas private JCheckBox chkIcono; private JCheckBox chkTexto; private JCheckBox chkToolTip; //el botn del que indicamos el aspecto private JButton boton; //panel de las casillas private JPanel panelCasillas; //panel del botn private JPanel panelBoton; private ImageIcon icono=new ImageIcon("icono2.gif");
183

public Casillas (){ super("Ventana con casillas de verificacin"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); getContentPane().setLayout(new GridLayout(1,2)); } public void creaCasillas(){ panelCasillas=new JPanel(); panelCasillas.setLayout(new GridLayout(3,1)); chkIcono=new JCheckBox("Icono"); chkIcono.setMnemonic(KeyEvent.VK_I); chkIcono.setSelected(true); chkIcono.addItemListener(this); panelCasillas.add(chkIcono); chkTexto=new JCheckBox("Texto"); chkTexto.setMnemonic(KeyEvent.VK_T); chkTexto.setSelected(true); chkTexto.addItemListener(this); panelCasillas.add(chkTexto); chkToolTip=new JCheckBox("Tooltip"); chkToolTip.setMnemonic(KeyEvent.VK_L); chkToolTip.setSelected(true); chkToolTip.addItemListener(this); panelCasillas.add(chkToolTip); getContentPane().add(panelCasillas); } public void creaBoton(){ panelBoton=new JPanel(); panelBoton.setLayout(new FlowLayout()); boton=new JButton("Soy un botn",icono); boton.setToolTipText("Soy un botn"); panelBoton.add(boton); getContentPane().add(panelBoton); } public void itemStateChanged(ItemEvent evento) { Object fuente=evento.getSource(); int estado=evento.getStateChange(); if (fuente==chkIcono){ if(estado==ItemEvent.DESELECTED) boton.setIcon(null); else boton.setIcon(icono); } if (fuente==chkTexto){ if(estado==ItemEvent.DESELECTED) boton.setText(""); else boton.setText("Soy un botn"); } if (fuente==chkToolTip){ if(estado==ItemEvent.DESELECTED) boton.setToolTipText(""); else boton.setToolTipText("Soy un botn"); } } public static void main(String args[]) { Casillas ventana = new Casillas(); ventana.creaCasillas(); ventana.creaBoton();
184

ventana.pack(); ventana.setVisible(true); } }

Como se puede observar la clase JButton lanza un evento del tipo ItemEvent cuando se modifica el estado de un objeto. En el mtodo itemStateChanged() debemos averiguar primero la fuente del evento, es decir, la casilla que ha visto modificado su estado, y a continuacin si la casilla en cuestin ha sido seleccionada o no.

JRadioButton
Los botones de opcin se suelen encontrar en grupos en los que, por convencin, nicamente uno de los botones puede encontrarse seleccionado a un mismo tiempo. Tambin podemos utilizar botones de opciones en elementos de men mediante la clase JRadioButtonMenuItem. La clase JRadioButton tambin tiene como superclase o clase padre a la clase AbstractButton, por lo tanto presentar el comportamiento comn a todas las clases que heredan de AbstractButton, por ejemplo podemos indicar que un objeto JRadioButton muestre una imagen o un tooltip. Para esta clase vamos a mostrar un ejemplo que ofrecer un dibujo de un animal distinto segn el botn de opcin que se encuentre seleccionado en cada momento. Para agrupar los botones se utiliza la clase ButtonGroup. Nuestro ejemplo va a contener tres botones de opcin agrupados para mostrar tres dibujos distintos.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Opciones extends JFrame implements ActionListener{ //cada una de las Opciones private JRadioButton opGato; private JRadioButton opCerdo; private JRadioButton opConejo; //imagen private JLabel imagen; //panel de las Opciones private JPanel panelOpciones; //panel de la imagen private JPanel panelImagen; public Opciones (){ super("Ventana con opciones"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); }
185

}); getContentPane().setLayout(new GridLayout(1,2)); } public void creaOpciones(){ panelOpciones=new JPanel(); panelOpciones.setLayout(new GridLayout(3,1)); opGato=new JRadioButton("Gato"); opGato.setMnemonic(KeyEvent.VK_G); opGato.addActionListener(this); opGato.setActionCommand("gato.gif"); panelOpciones.add(opGato); opCerdo=new JRadioButton("Cerdo"); opCerdo.setMnemonic(KeyEvent.VK_C); opCerdo.setSelected(true); opCerdo.addActionListener(this); opCerdo.setActionCommand("cerdo.gif"); panelOpciones.add(opCerdo); opConejo=new JRadioButton("Conejo"); opConejo.setMnemonic(KeyEvent.VK_J); opConejo.addActionListener(this); opConejo.setActionCommand("conejo.gif"); panelOpciones.add(opConejo); //se agrupan las opciones ButtonGroup grupo=new ButtonGroup(); grupo.add(opGato); grupo.add(opCerdo); grupo.add(opConejo); getContentPane().add(panelOpciones); } public void creaImagen(){ panelImagen=new JPanel(); panelImagen.setLayout(new BorderLayout()); imagen = new JLabel(new ImageIcon("cerdo.gif")); imagen.setPreferredSize(new Dimension(177, 122)); panelImagen.add(imagen,BorderLayout.CENTER); getContentPane().add(panelImagen); } public void actionPerformed(ActionEvent evento){ String comando=evento.getActionCommand(); imagen.setIcon(new ImageIcon(comando)); } public static void main(String args[]) { Opciones ventana = new Opciones(); ventana.creaOpciones(); ventana.creaImagen(); ventana.pack(); ventana.setVisible(true); } }

La clase JRadioButton, en lo que a tratamiento de eventos se refiere, funciona igual que la clase JButton, el botn que se pulse (seleccione) va a lanzar un evento de la clase
186

ActionEvent, para diferenciar entre los distintos botones se utiliza el ActionCommand del evento ActionEvent. Para mostrar la imagen se utiliza una etiqueta (JLabel) con un icono (ImageIcon).

JComboBox
Esta clase representa una lista desplegable de opciones, que puede ser editable o no. Cuando el usuario pulsa la lista, el objeto JComboBox muestra un men de elementos para elegir. Una lista desplegable editable es similar a una caja de texto (JTextField) con un pequeo botn. El usuario puede escribir un valor en la caja de texto o elegir un valor del men.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Combo extends JFrame implements ActionListener{ //lista desplegable private JComboBox listaOpciones; //opciones de la lista String[] opciones={"Gato","Cerdo","Conejo"}; //imagen private JLabel imagen; public Combo(){ super("Ventana con lista desplegable"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaCombo(){ listaOpciones=new JComboBox(opciones); listaOpciones.setSelectedIndex(0); getContentPane().add(listaOpciones,BorderLayout.NORTH); listaOpciones.addActionListener(this); } public void creaImagen(){ imagen = new JLabel(new ImageIcon("gato.gif")); imagen.setPreferredSize(new Dimension(177, 122)); getContentPane().add(imagen,BorderLayout.CENTER); } public void actionPerformed(ActionEvent evento){ JComboBox fuente=(JComboBox)evento.getSource(); //se obtiene el elemento seleccionado String seleccion=(String)fuente.getSelectedItem(); //se cambia la imagen imagen.setIcon(new ImageIcon(seleccion+".gif")); } public static void main(String args[]) { Combo ventana = new Combo(); ventana.creaCombo(); ventana.creaImagen(); ventana.pack(); ventana.setVisible(true); } }

187

Al seleccionar un elemento del objeto JComBox se lanza un evento ActionEvent, es decir, el mismo evento que se lanza cuando se pulsa un botn. El constructor utilizado para instanciar un objeto de la clase JComboBox recibe como argumento un array de cadenas (String), que representa las opciones que muestra la lista desplegable. Para indicar la opcin seleccionada por defecto utilizamos el mtodo setSelectedIndex() sobre el objeto de la clase JComboBox, y para obtener la opcin seleccionada y as mostrar la imagen correspondiente se utiliza el mtodo getSelectedItem() de la clase JComboBox.

JMenu
Un men permite elegir al usuario entre mltiples opciones disponibles. Los mens parecen normalmente dentro de barras de men o como mens contextuales (men popup). Un elemento de men hereda tambin de la clase AbstractButton, como muchas de las clases que hemos visto hasta ahora, veamos en la Figura 92, la jerarqua que presentan los distintos componentes Swing relacionados con la creacin de mens.

A continuacin se comentan cada una de estas clases. JMenuBar: representa la barra de men que va a contener los distintos elementos de men, que sern objetos de la clase JMenu. JMenu: es una opcin de men determinada, que contiene varios elementos de men, que
188

sern objetos de la clase JMenuItem, y tambin puede contener submens, es decir, objetos de la clase JMenu. JMenuItem: es un elemento de men. JCheckBoxMenuItem: elemento de men que posee una casilla de verificacin. JRadioButtonMenuItem: elemento de men que posee un botn de opcin. JSeparator: elemento de men especial que ofrece una separacin entre elementos de men de una misma opcin de men. JPopupMenu: representa un men contextual o de aparicin sbita, contiene elementos de men (JMenuItem). En el Cdigo siguiente se muestra la utilizacin de todas estas clases, adems se va mostrando en un rea de texto (JTextArea) los distintos eventos que van generando los distintos componentes de men junto con la fuente que ha producido dichos eventos. Como ocurre en estos casos primero se muestra el cdigo fuente del ejemplo.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Menus extends JFrame implements ActionListener, ItemListener{ JMenuBar barraMenu; JMenu menu, submenu; JMenuItem elementoMenu; JRadioButtonMenuItem rbElementoMenu; JCheckBoxMenuItem cbElementoMenu; JTextArea texto; JScrollPane panelScroll; public Menus(){ super("Ventana con mltiples mens"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaMenu(){ //Se crea la barra de men barraMenu = new JMenuBar(); setJMenuBar(barraMenu); //se crea el primer men menu = new JMenu("Un Men"); menu.setMnemonic(KeyEvent.VK_E); barraMenu.add(menu); //unos cuantos elementos de men elementoMenu=new JMenuItem("Elemento de men de texto",KeyEvent.VK_E); elementoMenu.addActionListener(this); menu.add(elementoMenu); //se asigna tecla de acceso rpido elementoMenu.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_1, ActionEvent.ALT_MASK)); elementoMenu=new JMenuItem("Texto e icono",new ImageIcon("icono2.gif")); elementoMenu.setMnemonic(KeyEvent.VK_T); elementoMenu.addActionListener(this); menu.add(elementoMenu); elementoMenu=new JMenuItem(new ImageIcon("icono2.gif")); elementoMenu.setMnemonic(KeyEvent.VK_D); elementoMenu.addActionListener(this); menu.add(elementoMenu); //se aade un separador menu.addSeparator();
189

//un grupo de elementos de men de botones de opcin. ButtonGroup grupo=new ButtonGroup(); rbElementoMenu=new JRadioButtonMenuItem("Botn de opcin"); rbElementoMenu.setSelected(true); rbElementoMenu.setMnemonic(KeyEvent.VK_O); grupo.add(rbElementoMenu); rbElementoMenu.addActionListener(this); menu.add(rbElementoMenu); rbElementoMenu= new JRadioButtonMenuItem("Otro botn de opcin"); rbElementoMenu.setMnemonic(KeyEvent.VK_B); grupo.add(rbElementoMenu); rbElementoMenu.addActionListener(this); menu.add(rbElementoMenu); //Un grupo de casillas de verificacin menu.addSeparator(); cbElementoMenu=new JCheckBoxMenuItem("Casilla de verificacin"); cbElementoMenu.setMnemonic(KeyEvent.VK_C); cbElementoMenu.addItemListener(this); menu.add(cbElementoMenu); cbElementoMenu=new JCheckBoxMenuItem("Otro ms"); cbElementoMenu.setMnemonic(KeyEvent.VK_M); cbElementoMenu.addItemListener(this); menu.add(cbElementoMenu); //un submen menu.addSeparator(); submenu=new JMenu("Un submen"); submenu.setMnemonic(KeyEvent.VK_S); elementoMenu=new JMenuItem("Un elemento de men del submen"); elementoMenu.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_2, ActionEvent.ALT_MASK)); elementoMenu.addActionListener(this); submenu.add(elementoMenu); elementoMenu= new JMenuItem("Otro elemento de men"); elementoMenu.addActionListener(this); submenu.add(elementoMenu); menu.add(submenu); //Segndo men de la barra de men menu = new JMenu("Otro Men"); menu.setMnemonic(KeyEvent.VK_M); barraMenu.add(menu); } public void creaTexto(){ texto= new JTextArea(10, 50); texto.setEditable(false); panelScroll= new JScrollPane(texto); getContentPane().add(panelScroll, BorderLayout.CENTER); } public void actionPerformed(ActionEvent evento){ JMenuItem fuente=(JMenuItem)(evento.getSource()); String mensaje= "ActionEvent detectado.\n" + " Fuente del evento: " + fuente.getText()+"\n"; texto.append(mensaje); } public void itemStateChanged(ItemEvent evento) { String estado=""; JMenuItem fuente =(JMenuItem)(evento.getSource()); if (evento.getStateChange()==ItemEvent.SELECTED) estado="Seleccionado"; else estado="No seleccionado"; String mensaje="ItemEvent detectado.\n" + " Fuente del evento: " + fuente.getText()+"\n" + " Nuevo estado: "+estado+"\n";
190

texto.append(mensaje); } public static void main(String args[]) { Menus ventana = new Menus(); ventana.creaMenu(); ventana.creaTexto(); ventana.pack(); ventana.setVisible(true); } }

Los elementos de men lanzan eventos ActionEvent, y los elementos de men del tipo JCheckBoxMenuItem lanzan eventos de la clase ItemEvent. El rea de texto (JTextArea) encargada de ir mostrando los eventos que se producen se aade a un panel de scroll (ScrollPane). Como se puede ver se han aadido dos teclas rpidas mediante el mtodo setAccelerator(). Un contextual o de aparicin sbita (popup) se encuentra representado por la clase JPopupMenu y debe registrarse un oyente de ratn en cada componente que tenga asociado el men popup, el oyente debe detectar que el usuario a pulsado el botn derecho del ratn para mostrar el men contextual correspondiente. El oyente que se ocupa de mostrar el men contextual lanza el mtodo show() sobre la instancia correspondiente de la clase JPopupMenu. El mtodo show() recibe como parmetros el componente al que se asocia el men y las coordenadas de la pantalla en la que se quiere mostrar el men contextual. Vamos a modificar el ejemplo anterior en el que utilizbamos la barra de men y vamos a aadir un men contextual (JPopupMenu) que se asociar al rea de texto (JTextArea) en la que se muestran los eventos lanzados por los distintos elementos de men. Se va aadir un nuevo atributo a nuestra clase, llamado menuPopup que pertenece a la clase JPopupMenu. Tambin se va a aadir un nuevo mtodo llamado creaPopup().
public void creaPopup(){ menuPopup=new JPopupMenu(); //se crean y aadden los distinos elementos de men de la misma manera elementoMenu=new JMenuItem("Primer elemento del popup",KeyEvent.VK_P); elementoMenu.addActionListener(this); menuPopup.add(elementoMenu); elementoMenu=new JMenuItem("Segundo del popup",new ImageIcon("icono2.gif"));
191

elementoMenu.setMnemonic(KeyEvent.VK_S); elementoMenu.addActionListener(this); menuPopup.add(elementoMenu); menuPopup.addSeparator(); cbElementoMenu=new JCheckBoxMenuItem("Tercer elemento"); cbElementoMenu.setMnemonic(KeyEvent.VK_T); cbElementoMenu.addItemListener(this); menuPopup.add(cbElementoMenu); //se crea el oyente y se registra para el rea de texto PopupListener oyente=new PopupListener(); texto.addMouseListener(oyente); }

Este mtodo se puede lanzar una vez utilizado el mtodo creaTexto(). Como se puede observar se crea una instancia de un objeto de la clase PopupListener, esta clase es una clase interna que hereda de la clase MouseAdapter y que va a ser el oyente de nuestro men contextual y el que va a mostrarlo. El cdigo de esta clase adaptadora interna es:
class PopupListener extends MouseAdapter{ public void mousePressed(MouseEvent evento){ menuPopup.show(evento.getComponent(),evento.getX(),evento.getY()); } }

Y un ejemplo de la utilizacin del men contextual se puede observar en la Figura.

Como se puede comprobar a la vista de la figura anterior, seguimos recogiendo los eventos de los distintos elementos de men, incluso del men contextual. Pero si el lector prueba este ejemplo comprobar que el men contextual parecer tambin cuando se pulse el botn izquierdo del ratn, y no slo cuando se pulse el botn derecho, que sera lo deseable. La clase MouseEvent ofrece el mtodo isPopupTrigger() para averiguar si debemos mostrar el men popup o no. El mtodo isPopupTrigger() devolver verdadero si la pulsacin del ratn se corresponde con un evento que debe mostrar un men contextual, en el caso de Windows se corresponde con el botn derecho del ratn. As nuestro cdigo fuente del adaptador del ratn quedara como se muestra en el Cdigo :
class PopupListener extends MouseAdapter{ public void mousePressed(MouseEvent evento){ if (evento.isPopupTrigger()){ menuPopup.show(evento.getComponent(),evento.getX(),evento.getY());
192

} } }

La sorpresa es que ahora no se muestra nunca el men contextual, no con le botn derecho ni con el botn derecho del ratn, la verdad no he conseguido discernir porque no aparece.

JSlider
Este componente permite al usuario seleccionar un valor numrico entre un rango determinado, este componente se utiliza para restringir los valores que puede ofrecer el usuario y as evitar errores y el tratamiento de los mismos. Para mostrar el funcionamiento de este componente Swing se ha utilizado un ejemplo en el que segn se indique en el selector (JSlider) se dar un tamao determinado a un botn (JButton), el botn se redimensionar segn se indique en el selector.
import java.awt.*; import java.awt.event.*; import javax.swing.*; //eventos nuevos de Swing import javax.swing.event.*; public class Selector extends JFrame implements ChangeListener{ //selector de valores private JSlider selector; private JButton boton; public Selector(){ super("Ventana con selector (JSlider)"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaSelector(){ selector=new JSlider(JSlider.HORIZONTAL,0,300,50); selector.addChangeListener(this); selector.setMajorTickSpacing(100); selector.setMinorTickSpacing(10); selector.setPaintTicks(true); selector.setPaintLabels(true); getContentPane().add(selector,BorderLayout.NORTH); } public void creaBoton(){ boton=new JButton("Soy un botn"); JPanel panel=new JPanel(); panel.setLayout(new FlowLayout()); panel.add(boton); getContentPane().add(panel,BorderLayout.CENTER); boton.setSize(50,50); } public void stateChanged(ChangeEvent evento){ JSlider fuente=(JSlider)evento.getSource(); if (!fuente.getValueIsAdjusting()){ boton.setSize((int)fuente.getValue(),(int)fuente.getValue()); } } public static void main(String args[]) { Selector ventana = new Selector(); ventana.creaSelector();
193

ventana.creaBoton(); ventana.setSize(310,310); ventana.setVisible(true); } }

A la vista del cdigo se pueden hacer los siguientes comentarios y apreciaciones. El constructor utilizado para crear el objeto de la clase JSlider, posee un argumento para indicar la orientacin del selector correspondiente, se corresponde con las constantes HORIZONTAL y VERTICAL de la clase JSlider. Los siguientes argumentos, especifican respectivamente, el valor mnimo, el valor mximo y el valor seleccionado inicialmente del objeto JSlider. Si no indicamos estos valores, el valor mnimo del selector ser 0, el mximo 100 y el seleccionado inicialmente 50. Para configurar inicialmente el objeto de la clase JSlider, hemos utilizado una serie de mtodos: setMajorTickSpacing(int espaciado): este mtodo nos permite indicar las separaciones mayores entre el mximo y el mnimo del selector. setMinorTickSpacing(int espaciado); este mtodo indica las separaciones menores entre el rango indicado para el componente Swing JSlider. setPaintTicks(boolean): mediante este mtodo indicamos si deseamos o no que parezcan las separaciones del objeto JSlider. setPaintLabels(boolean): nos permite indicar si queremos que se pinten las diferentes etiquetas del rango. Para registrar el oyente del selector hemos utilizado el mtodo addChangeListener(), ya que la clase JSlider lanza eventos de la clase ChangeEvent. Este es un nuevo tipo de evento que se incluye en el paquete javax.swing.event y se lanza cuando se modifica el valor actual de un objeto de la clase JSlider. El mtodo que debe implementar un oyente de eventos ChangeEvent es el mtodo stateChanged(). En el ejemplo anterior hemos visto que se ha utilizado el mtodo getValueIsAdjusting() de la clase JSlider, este mtodo devuelve verdadero mientras se est
194

seleccionando el valor del objeto JSlider, de esta forma no se redimensionar el botn hasta que el usuario no haya finalizado de arrastrar el tirador del selector, es decir, cuando el usuario ha finalizado con el proceso de seleccin. Las etiquetas que aparecen en el selector las podemos personalizar con la ayuda del mtodo setLabelTable() de la clase JSlider y de un objeto de la clase Hashtable. Para utilizar la clase Hashtable debemos importar el paquete Primero crearemos un objeto que representa una tabla hash y mediante el mtodo put() vamos indicando la posicin que queremos que ocupe la etiqueta y el objeto JLabel que va a mostrar el texto correspondiente. Veamos, en el Cdigo siguiente, un sencillo ejemplo.
public void creaSelector(){ selector=new JSlider(JSlider.HORIZONTAL,0,300,50); selector.addChangeListener(this); selector.setMajorTickSpacing(100); selector.setPaintTicks(true); //tabla de etiquetas Hashtable etiquetas=new Hashtable(); etiquetas.put(new Integer(0),new JLabel("Minsculo")); etiquetas.put(new Integer(100),new JLabel("Pequeo")); etiquetas.put(new Integer(200),new JLabel("Mediano")); etiquetas.put(new Integer(300),new JLabel("Grande")); //asignamos las etiquetas al selector selector.setLabelTable(etiquetas); selector.setPaintLabels(true); getContentPane().add(selector,BorderLayout.NORTH); }

Con la clase JSlider se da por terminado el apartado dedicado al grupo de componentes Swing cuya funcin es la de recoger la entrada del usuario, a continuacin vamos a comentar algunos componentes atmicos cuya funcin es la de simplemente mostrar informacin al usuario, algunos de los representantes de este grupo que vamos a tratar a continuacin ya los conoceremos de ejemplos anteriores, como ocurre con el componente JLabel.
195

Componentes para mostrar informacin


Se ofrecen distintos ejemplos de componentes Swing encargados de mostrar informacin.

JLabel
Ya hemos utilizado numerosas veces este til y sencillo componente de Swing, como ya sabemos su labor es la de mostrar una informacin al usuario, ya sea textual o con imgenes o con ambos elementos. En el siguiente ejemplo se muestra la utilizacin de la clase JLabel creando cuatro objetos distintos de esta clase, se ha aadido un borde a cada etiqueta para que se distingan claramente unas de otras.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Etiquetas extends JFrame{ private JLabel etiqueta1; private JLabel etiqueta2; private JLabel etiqueta3; private JLabel etiqueta4; private ImageIcon icono; public Etiquetas(){ super("Ventana con etiquetas"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaEtiquetas(){ icono=new ImageIcon("icono2.gif"); etiqueta1=new JLabel("Imagen y texto",icono,JLabel.CENTER); etiqueta1.setVerticalTextPosition(JLabel.BOTTOM); etiqueta1.setHorizontalTextPosition(JLabel.CENTER); etiqueta1.setBorder(BorderFactory.createLineBorder(Color.red)); etiqueta2=new JLabel("Slo texto"); etiqueta2.setHorizontalAlignment(JLabel.RIGHT); etiqueta2.setVerticalAlignment(JLabel.TOP); etiqueta2.setBorder(BorderFactory.createLineBorder(Color.blue)); etiqueta3=new JLabel(icono); etiqueta3.setBorder(BorderFactory.createLineBorder(Color.green)); etiqueta4=new JLabel("Imagen y texto",icono,JLabel.CENTER); etiqueta4.setVerticalTextPosition(JLabel.TOP); etiqueta4.setHorizontalTextPosition(JLabel.CENTER); etiqueta4.setBorder(BorderFactory.createLineBorder(Color.pink)); } public void aadeEtiquetas(){ getContentPane().setLayout(new GridLayout(1,4,5,5)); getContentPane().add(etiqueta1); getContentPane().add(etiqueta2); getContentPane().add(etiqueta3); getContentPane().add(etiqueta4); } public static void main(String args[]) { Etiquetas ventana = new Etiquetas(); ventana.creaEtiquetas(); ventana.aadeEtiquetas(); ventana.pack(); ventana.setVisible(true); }}
196

Como se puede observar en el ejemplo anterior es posible especificar la alineacin del texto respecto a la imagen que contiene la etiqueta utilizando el mtodo setVerticalTextPosition() y setHorizontalTextPostion(), y mediante las constantes LEFT, CENTER, RIGHT, TOP y BOTTOM. El valor por defecto es centrado (CENTER). Tambin se puede indicar la alineacin de los componentes dentro de la etiqueta con los mtodos setHorizontalAlignment() y setVerticalAlignment(). Al igual que suceda con la clase JButton, con los componentes JLabel podemos utilizar cdigo HTML para indicar el formato del texto que se va a mostrar. Veamos un ejemplo:l
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Etiquetas extends JFrame{ private JLabel etiqueta1; private JLabel etiqueta2; private JLabel etiqueta3; private JLabel etiqueta4; private ImageIcon icono; public Etiquetas(){ super("Ventana con etiquetas"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaEtiquetas(){ icono=new ImageIcon("icono2.gif"); etiqueta1=new JLabel("<html><font size='4' color='red'><b><i>"+ "Imagen y texto</b></i></font>",icono,JLabel.CENTER); etiqueta1.setVerticalTextPosition(JLabel.BOTTOM); etiqueta1.setHorizontalTextPosition(JLabel.CENTER); etiqueta1.setBorder(BorderFactory.createLineBorder(Color.red)); etiqueta2=new JLabel("<html><ul><li>Elemento 1<li>Elemento2</ul>"); etiqueta2.setBorder(BorderFactory.createLineBorder(Color.blue)); etiqueta3=new JLabel("<html><table border='1'><tr><td>Celda 1</td>"+ "<td>Celda2</td></tr><tr><td align='center' colspan='2'>Celda 3</td>"+" </tr></table>", JLabel.CENTER); etiqueta3.setBorder(BorderFactory.createLineBorder(Color.green)); etiqueta4=new JLabel("<html><h2><i>Imagen y texto</i></h2><hr>", icono,JLabel.CENTER); etiqueta4.setVerticalTextPosition(JLabel.TOP); etiqueta4.setHorizontalTextPosition(JLabel.CENTER); etiqueta4.setBorder(BorderFactory.createLineBorder(Color.pink)); } public void aadeEtiquetas(){ getContentPane().setLayout(new GridLayout(4,1,15,15)); getContentPane().add(etiqueta1); getContentPane().add(etiqueta2); getContentPane().add(etiqueta3); getContentPane().add(etiqueta4); } public static void main(String args[]) {
197

Etiquetas ventana = new Etiquetas(); ventana.creaEtiquetas(); ventana.aadeEtiquetas(); ventana.setSize(300,450); ventana.setVisible(true); } }

JToolTip
Dentro del grupo de componentes atmicos de Swing encargados exclusivamente de mostrar informacin, tambin podemos encontrar la clase JToolTip, esta clase representa las pequeas ayudas que se muestran en forma de pequeo texto explicativo (tooltip) cuando nos situamos sobre un componente determinado. Aunque realmente no vamos a utilizar nunca directamente la clase JToolTip, sino que utilizaremos los mtodos setToolTipText() y getTootTipText() de la clase JComponent, ya hemos visto la utilizacin de estos mtodos en ejemplos anteriores, con el primero asignamos un tooltip a un componente Swing y con el segundo obtenemos el tooltip que tiene asignando un componente Swing determinado.

JProgressBar
Este componente Swing muestra grficamente el progreso de una operacin determinada, la barra de progreso va mostrando el porcentaje de tarea que se ha realizado, nos va informando de la evolucin de un proceso. Al crear una instancia de la clase JProgressBar podemos indicar la orientacin que va a tener la barra de progreso, para ello se utilizan las constantes HORIZONTAL y
198

VERTICAL de la clase JProgressBar. Tambin podemos indicar el mximo y el mnimo de la barra de progreso. Los mximos y mnimos los podemos manipular con los mtodos getMinumum()/setMinimum() y getMaximum()/setMaximum(). Para incrementar el valor actual de la barra de progreso, que se corresponde con el proceso actual, disponemos del mtodo setValue(). A continuacin se ofrece un sencillo ejemplo que consiste en ir pulsando un botn que incrementar por cada pulsacin el valor actual de la barra de progreso. Al llegar al mximo se emitir un sonido y despus se volver la barra de progreso a su estado inicial.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class BarraProgreso extends JFrame implements ActionListener{ private JProgressBar barra; //valor que se va incrementando private int valor; private JButton boton; public BarraProgreso(){ super("Ventana con barra de progreso"); this.valor=0; this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaBarra(){ //una barra de mnimo 0 y mximo 100 barra=new JProgressBar(JProgressBar.HORIZONTAL,0,100); //valor inicial barra.setValue(0); //indicamos que aparecen las etiquetas del progreso barra.setStringPainted(true); getContentPane().add(barra,BorderLayout.NORTH); } public void creaBoton(){ boton=new JButton("Incrementar"); JPanel panel=new JPanel(); panel.setLayout(new FlowLayout()); panel.add(boton); boton.addActionListener(this); getContentPane().add(panel,BorderLayout.CENTER); } public void actionPerformed(ActionEvent event){ //se incrementa el valor valor=valor+10; if (valor>barra.getMaximum()){ //hemos sobrepasado el mximo de la barra de progreso barra.setValue(barra.getMinimum()); valor=barra.getMinimum(); } else{ barra.setValue(valor); if(valor==barra.getMaximum()) //se emite un pitido al llegar al mximo Toolkit.getDefaultToolkit().beep(); } } public static void main(String args[]) {
199

BarraProgreso ventana = new BarraProgreso(); ventana.creaBarra(); ventana.creaBoton(); ventana.setSize(200,200); ventana.setVisible(true); } }

Como se puede comprobar en el ejemplo, la barra de progreso muestra el tanto por ciento (%) de la operacin realizada, este tanto por ciento lo calcula teniendo en cuenta el valor actual y sus valores mximo y mnimo. Para que se muestre el etiqueta del tanto por ciento se ha utilizado el mtodo setStringPainted() pasndole como argumento el valor true. Podemos mostrar en la barra de progreso cualquier texto mediante el mtodo setString() de la clase JProgressBar y as variar el comportamiento por defecto de la barra de progreso. Para obtener el tanto por ciento de la tarea realizada (completada) representada por la barra de progreso disponemos del mtodo getPercentComplete(). Este es el ltimo de los componentes Swing pertenecientes al grupo de componentes encargados de mostrar informacin, los siguientes componentes pertenecen al grupo de los componentes atmicos que muestran tambin una informacin pero de una forma ms compleja y estructurada, adems en algunos casos permiten editar esta informacin.

Componentes que muestran informacin estructurada


En este ltimo apartado se van a tratar dos componentes JColorChooser y JFileChooser que permiten seleccionar un color determinado y un fichero del sistema de ficheros, respectivamente.

JColorChooser
Este componente Swing adems de ofrecer una informacin estructurada, como pueden ser los distintos colores, nos permite seleccionar un elemento determinado de esa informacin. La clase JColorChooser representa un componente Swing atmico, pero bastante complejo y completo que permite seleccionar colores de distintas formas. Adems este componente permite mostrar de forma sencilla un dilogo que permite seleccionar tambin un color. Veremos dos ejemplos de las dos formas que podemos utilizar el componente JColorChooser: como si se tratara de otro componente ms o como un dilogo.
200

En el Look & Feel de Java (en el apartado correspondiente hablaremos de los distintos Look & Feel y como establecerlos) una instancia de la clase JColorChooser muestra el siguiente aspecto: se encuentra dividido en dos partes, un panel con pestaas (JTabbedPane) y un panel con la muestra de la seleccin (preview). El panel de seleccin con pestaas encuentra en la parte superior y presenta tres pestaas, cada una de ellas permite una forma distinta de seleccionar los colores. El panel de muestra se encuentra en la parte inferior y va mostrando el color seleccionado en cada momento del panel de seleccin. Para poder tratar la seleccin actual el componente JColorChooser hace uso de una clase llamada ColorSelectionModel que se encuentra en el subpaquete de Swing denominado javax.swing.colorchooser. El modelo de seleccin de color (instancia de la clase ColorSelectionModel), lanza un evento de la clase ChangeEvent (este evento vimos que tambin era lanzado por el componente JSlider) cada vez que se cambia la seleccin del color en el selector de color (JColorChooser). Si queremos detectar los cambios de seleccin de color del componente JColorChooser debemos registrar el oyente correspondiente al evento ChageEvent, pero no se debe hacer directamente sobre el componente JColorChooser, sino sobre su ColorSelectionModel. Para obtener la instancia de la clase ColorSelectionModel correspondiente a un objeto de la clase JColorChooser, debemos lanzar sobre el objeto JColorChooser el mtodo getSelectionModel(). Pasemos ahora a ver un cdigo de ejemplo que utiliza un objeto de la clase JColorChooser para configurar el color del texto de una etiqueta.
import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.colorchooser.*; public class SelectorColor extends JFrame implements ChangeListener{ private JLabel etiqueta; private JColorChooser selectColor; public SelectorColor() { super("Selector de color"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaEtiqueta(){ //Configuramos la etiqueta sobre la que se aplica la seleccin de color etiqueta= new JLabel("La Plataforma Java 2",JLabel.CENTER); etiqueta.setForeground(Color.green); etiqueta.setBackground(Color.white); etiqueta.setOpaque(true); etiqueta.setFont(new Font("SansSerif", Font.BOLD, 24));
201

etiqueta.setPreferredSize(new Dimension(80,45)); //panel con borde que va a contener la etiqueta JPanel etiquetaPanel = new JPanel(new BorderLayout()); etiquetaPanel.add(etiqueta, BorderLayout.CENTER); etiquetaPanel.setBorder(BorderFactory.createTitledBorder("etiqueta")); getContentPane().add(etiquetaPanel, BorderLayout.CENTER); } public void creaSelectorColor(){ selectColor= new JColorChooser(etiqueta.getForeground()); //se obtiene el modelo de selecin de color para registrar el oyente //de los eventos ChangeEvent selectColor.getSelectionModel().addChangeListener(this); selectColor.setBorder(BorderFactory.createTitledBorder ("Selecciona el color del texto")); getContentPane().add(selectColor,BorderLayout.SOUTH); } public void stateChanged(ChangeEvent e) { etiqueta.setForeground(selectColor.getColor()); } public static void main(String[] args) { SelectorColor ventana = new SelectorColor(); ventana.creaEtiqueta(); ventana.creaSelectorColor(); ventana.pack(); ventana.setVisible(true); } }

202

Como se puede comprobar en el ejemplo el constructor utilizado de la clase JColorChooser recibe como parmetro un color, este es el color seleccionado inicialmente dentro del selector de color. En nuestro caso le hemos indicado el color de la letra de la etiqueta sobre la que se va a aplicar las distintas selecciones de color. El mtodo stateChanged(), que se lanzar cada vez que se cambie la seleccin de color del selector de color, recupera el color actual mediante el mtodo getColor() de la clase JColorChooser, y se lo aplica al texto de la etiqueta mediante el mtodo setForeground(). En la ejecucin del ejemplo se puede observar las tres formas distintas de seleccionar el color que permite el componente JColorChooser, en la Figura 102 se puede ver otra seleccin distinta, utilizando el formato de color RGB. En este caso el componente JColorChooser lo hemos tratado como otro componente atmico ms, aadindolo al panel de contenido de la ventana, pero tambin podemos mostrarlo como un dilogo, para ello podemos utilizar el mtodo esttico showDialog() de la clase JColorChooser. La sintaxis de este mtodo es: JColorChooser.showDialog(Component padre, String ttulo, Color color)

203

El primer argumento de este mtodo es el componente padre del dilogo (el componente del que depende), el segundo el ttulo del dilogo y el tercero el color seleccionado por defecto en el dilogo de seleccin de color. Ahora vamos a modificar el ejemplo anterior para realizar una funcin similar pero utilizando el selector de colores como un dilogo modal. Para mostrar el dilogo vamos a utilizar un botn (JButton) y el evento ActionEvent. En este nuevo supuesto no se utiliza la clase ColorSelectionModel, sino que el color seleccionado lo obtenemos directamente cuando el usuario pulse el botn de OK del dilogo. Esto se ve mucho ms claro en el cdigo fuente del ejemplo
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class DialogoSelectorColor extends JFrame implements ActionListener{ private JLabel etiqueta; private JButton boton; public DialogoSelectorColor() { super("Selector de color con dilogo"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } public void creaEtiqueta(){ //Configuramos la etiqueta sobre la que se aplica la seleccin de color etiqueta= new JLabel("La Plataforma Java 2",JLabel.CENTER); etiqueta.setForeground(Color.green); etiqueta.setBackground(Color.white); etiqueta.setOpaque(true); etiqueta.setFont(new Font("SansSerif", Font.BOLD, 24)); etiqueta.setPreferredSize(new Dimension(80,45)); //panel con borde que va a contener la etiqueta JPanel etiquetaPanel = new JPanel(new BorderLayout()); etiquetaPanel.add(etiqueta, BorderLayout.CENTER); etiquetaPanel.setBorder(BorderFactory.createTitledBorder("etiqueta")); getContentPane().add(etiquetaPanel, BorderLayout.CENTER); } public void creaBoton(){ JPanel panel=new JPanel(); panel.setLayout(new FlowLayout()); boton=new JButton("Seleccin de color"); panel.add(boton); boton.addActionListener(this); getContentPane().add(panel,BorderLayout.SOUTH); } public void actionPerformed(ActionEvent evento) { Color color=JColorChooser.showDialog(this,"Selecciona un color", etiqueta.getForeground()); if (color!=null) etiqueta.setForeground(color); } public static void main(String[] args) { DialogoSelectorColor ventana = new DialogoSelectorColor(); ventana.creaEtiqueta(); ventana.creaBoton(); ventana.setSize(400,180);
204

ventana.setVisible(true); } }

Si el usuario cancela la seleccin de color, ya sea cerrando el dilogo o pulsando el botn de cancelacin, no se devolver ningn color, sino que se devolver el valor null (nulo). Al pulsar el botn seleccin de color aparece:

JFileChooser
Este es el ltimo componente atmico de Swing que vamos a ver. El componente JFileChooser es similar al visto anteriormente, podemos crearlo como un componente ms y aadirlo a un panel o bien mostrarlo como un dilogo modal.

205

Este componente nos permite navegar por el sistema de ficheros y seleccionar un fichero o directorio. Normalmente se utilizan estos componentes como dilogos modales. Pero la clase JFileChooser nicamente muestra el interfaz que nos permite seleccionar los ficheros, pero el tratamiento de los mismos corre de nuestra cuenta, es decir, el tratamiento de los ficheros debemos implementarlo en nuestra aplicacin. La clase JFileChooser ofrece dos mtodos para mostrar los dos tipos de selectores de ficheros distintos, el que se utiliza para abrir ficheros y el que se utiliza para grabar ficheros, estos mtodos son showOpenDialog() y showSaveDialog(). Ambos mtodos reciben como argumento un objeto Component que representa el padre del dilogo de seleccin de ficheros. Estos mtodos no son estticos como ocurra con el componente ColorChooser, sino que se deben lanzar sobre una instancia de la clase JFileChooser. Tanto el mtodo showOpenDialog() como showSaveDialog() devuelven un valor entero que se corresponde con las constantes que indican el resultado de la seleccin del usuario, indican si el usuario a pulsado el botn de cancelacin (CANCEL_OPTION) o no (APPROVE_OPTION), ambas constantes de encuentran definidas en la clase JFileChooser. El aspecto que muestra el dilogo para la apertura de fichero es muy similar al del dilogo para guardar ficheros. A continuacin vamos a mostrar un sencillo ejemplo que posee dos botones y un rea de texto (JTextArea), segn el botn que se pulse se mostrar un dilogo de seleccin de ficheros distinto, para abrir ficheros y para guardar ficheros. En el rea de texto se irn mostrando las selecciones de ficheros o cancelaciones realizadas por el usuario.
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; public class DialogoSelectorFichero extends JFrame implements ActionListener{ private JTextArea resultado; private JButton botonAbrir; private JButton botonGuardar; private JFileChooser selectorFichero; public DialogoSelectorFichero() { super("Dilogo para la seleccin de ficheros"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); selectorFichero=new JFileChooser(); } public void creaBotones(){ ImageIcon iconoAbrir = new ImageIcon("abrir.gif"); botonAbrir=new JButton("Abrir un fichero...", iconoAbrir); ImageIcon iconoGuardar = new ImageIcon("guardar.gif"); botonGuardar=new JButton("Guardar un fichero...", iconoGuardar); botonAbrir.addActionListener(this); botonGuardar.addActionListener(this); JPanel panelBotones=new JPanel(); panelBotones.add(botonAbrir); panelBotones.add(botonGuardar); getContentPane().add(panelBotones,BorderLayout.NORTH); } public void creaResultado(){
206

resultado = new JTextArea(5,20); resultado.setMargin(new Insets(5,5,5,5)); resultado.setEditable(false); JScrollPane panelResultado = new JScrollPane(resultado); getContentPane().add(panelResultado,BorderLayout.CENTER); } public void actionPerformed(ActionEvent evento) { if (evento.getSource()==botonAbrir){ int valorDevuelto= selectorFichero.showOpenDialog(this); if (valorDevuelto== JFileChooser.APPROVE_OPTION) { File fichero = selectorFichero.getSelectedFile(); resultado.append("Abierto: " + fichero.getName()+"\n"); } else resultado.append("Apertura cancelada por el usuario\n"); }else{ int valorDevuelto= selectorFichero.showSaveDialog(this); if (valorDevuelto== JFileChooser.APPROVE_OPTION) { File fichero = selectorFichero.getSelectedFile(); resultado.append("Guardado: " + fichero.getName()+"\n"); } else resultado.append("Operacin de grabado cancelada"+ "por el usuario\n"); } } public static void main(String[] args) { DialogoSelectorFichero ventana = new DialogoSelectorFichero(); ventana.creaBotones(); ventana.creaResultado(); ventana.pack(); ventana.setVisible(true); } }

Y la del dilogo de seleccin de ficheros para su apertura, es:

207

Como se puede comprobar en el ejemplo, para obtener el fichero que se ha seleccionado se utiliza el mtodo getSelectedFile(), que devuelve un objeto de la clase File. A travs de este objeto podemos manipular ficheros, en una aplicacin real se utilizara para guardar o abrir ficheros. El constructor de la clase JFileChooser puede recibir como argumento un objeto File o String para indicar el directorio inicial. El directorio inicial tambin se puede especificar mediante el mtodo setCurrentDirectory() al que se le pasa como argumento un objeto de la clase File. Cada vez que se muestra el dilogo para la seleccin de ficheros guarda el directorio actual de la seleccin anterior, es decir, va recordando las rutas de las selecciones anteriores. Adems permite la creacin de nuevos directorios. Este ha sido el componente Swing atmico que cierra este captulo, en el siguiente captulo trataremos el nuevo gestor de diseo de Swing y la caracterstica Pluggable Look & Feel.

Interfaces de usuario en Java: otras caractersticas de Swing


Introduccin
Este captulo cierra el ciclo de captulos dedicado a Swing, este captulo no va a ser tan extenso como los anteriores ya que trataremos dos aspectos muy determinados, el nuevo gestor de diseo de Swing, representado por la clase BoxLayout y la caracterstica especial que ofrece Swing denominada Pluggable Look & Feel.

208

El gestor de diseo BoxLayout


La clase BoxLayout, que podemos encontrar en el paquete javax.swing, representa a un nuevo gestor de diseo ofrecido por Swing. Este gestor de diseo organiza los componentes en una pila, uno encima del otro y tambin de izquierda a derecha uno al lado del otro. Se puede decir que es una versin ampliada del gestor de diseo FlowLayout que ofrece AWT. Antes de seguir comentando este gestor de diseo ofrecido por Swing, vamos a ver con un sencillo ejemplo la forma en la que distribuye los componentes en el panel al que se aplica.
import javax.swing.*; import java.awt.event.*; public class BoxLayoutSencillo{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout"); ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); JPanel panelContenido=new JPanel(); panelContenido.setLayout(new BoxLayout(panelContenido,BoxLayout.Y_AXIS)); panelContenido.add(new JButton("Soy un botn")); panelContenido.add(new JButton("Soy un botn ms")); panelContenido.add(new JButton("Otro botn")); panelContenido.add(new JButton("Soy el ltimo")); ventana.setContentPane(panelContenido); ventana.pack(); ventana.setVisible(true); } }

En el ejemplo anterior podemos comprobar la forma de crear un gestor de diseo para distribuir los componentes de arriba a abajo. El constructor de la clase BoxLayout posee dos argumentos, el panel (JPanel) sobre el que se va a aplicar el gestor de diseo y la orientacin en la que se van a distribuir los componentes dentro de dicho panel. Este ltimo parmetro es un entero que se corresponde con las siguientes constantes definidas en la clase BoxLayout: X_AXIS: los componentes se distribuyen de izquierda a derecha. Y_AXIS: los componentes se distribuyen de arriba a abajo. Si retomamos el ejemplo anterior y cambiamos la constante BoxLayout.Y_AXIS por BoxLayout.X_AXIS, se obtiene el resultado que muestra continuacin:

209

Conjuntamente con la clase BoxLayout, se suele utilizar la clase Box, esta clase ofrece una serie de mtodos para controlar de una forma ms precisa la forma en la que se distribuyen los componentes dentro de un panel al que se le ha aplicado este gestor de diseo BoxLayout.
import javax.swing.*; import java.awt.event.*; import java.awt.*; public class DosBoxLayout{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout"); ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); JPanel panelSuperior=new JPanel(); panelSuperior.setBorder(BorderFactory.createLineBorder(Color.red)); panelSuperior.setLayout(new BoxLayout(panelSuperior,BoxLayout.Y_AXIS)); panelSuperior.add(new JLabel("Esto es una etiqueta")); panelSuperior.add(Box.createRigidArea(new Dimension(0,5))); panelSuperior.add(new JTextArea(5,30)); JPanel panelInferior=new JPanel(); panelInferior.setBorder(BorderFactory.createLineBorder(Color.green)); panelInferior.setLayout(new BoxLayout(panelInferior,BoxLayout.X_AXIS)); panelInferior.add(Box.createHorizontalGlue()); panelInferior.add(new JButton("Botn 1")); panelInferior.add(Box.createRigidArea(new Dimension(10,0))); panelInferior.add(new JButton("Botn 2")); ventana.getContentPane().add(panelSuperior,BorderLayout.NORTH); ventana.getContentPane().add(panelInferior,BorderLayout.CENTER); ventana.pack(); ventana.setVisible(true); } }

El interfaz que genera este cdigo es:

A la vista del cdigo se puede decir que este interfaz est compuesto por dos paneles los que se han aplicado un gestor de diseo BoxLayout distinto, uno de arriba a abajo y otro de izquierda a derecha.
210

Se ha aplicado un borde a cada uno de estos dos paneles para distinguirlos ms claramente. En el primer panel se ha utilizado el mtodo createRigidArea() de la clase Box para crear una separacin fija (rea rgida) invisible entre la etiqueta (JLabel) y el rea de texto (JTextArea). En este caso se ha indicado una separacin vertical de cinco pixels. En el segundo panel se utiliza otro mtodo de la clase Box, el mtodo createHorizontalGlue(), este mtodo provoca que los dos botones que se aadan a continuacin se desplacen hacia la derecha, es decir, este mtodo rellena el espacio sobrante y desplaza a los componentes hacia la derecha. El mtodo createHorizontalGlue() crea un rea invisible que crece horizontalmente para rellenar todo el espacio libre disponible dentro del contenedor. En este panel inferior tambin se utiliza el mtodo createRigidArea() para separar los dos botones diez pixels, en este caso se ha utilizado un rea slo con componente horizontal. Si la llamada al mtodo createHorizontalGlue() la hubiramos realizado en el lugar en la que se encuentra la llamada al mtodo createRigidArea(), el resultado hubiera sido la Figura siguiente:

A la vista de la imagen se puede decir que el rea se ha extendido entre los dos botones, enviando al segundo botn a la derecha del todo. La clase Box ofrece tambin el mtodo createVerticalGlue(), en este caso el rea invisible se extiende de forma vertical enviando los componentes dentro del panel hacia la zona inferior. El gestor de diseo BoxLayout respeta el tamao mximo de los componentes que contiene y tambin la alineacin establecida por los mismos, aunque para que no existan problemas de alineacin todos los componentes deben tener la misma. Si modificamos el primer ejemplo visto en este apartado tenemos:
import java.awt.event.*; import java.awt.*; public class BoxLayoutSencillo{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout"); ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); JPanel panelContenido=new JPanel(); JButton boton1=new JButton("Soy un botn"); boton1.setAlignmentX(Component.CENTER_ALIGNMENT); JButton boton2=new JButton("Soy un botn ms"); JButton boton3=new JButton("Otro botn"); boton3.setAlignmentX(Component.RIGHT_ALIGNMENT); JButton boton4=new JButton("Soy el ltimo");
211

boton4.setAlignmentX(Component.CENTER_ALIGNMENT); panelContenido.setLayout(new BoxLayout(panelContenido,BoxLayout.Y_AXIS)); panelContenido.add(boton1); panelContenido.add(boton2); panelContenido.add(boton3); panelContenido.add(boton4); ventana.setContentPane(panelContenido); ventana.pack(); ventana.setVisible(true); } }

La alineacin se respetar en este otro ejemplo.


import javax.swing.*; import java.awt.event.*; import java.awt.*; public class BoxLayoutSencillo{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout"); ventana.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); JPanel panelContenido=new JPanel(); JButton boton1=new JButton("Soy un botn"); boton1.setAlignmentX(Component.CENTER_ALIGNMENT); JButton boton2=new JButton("Soy un botn ms"); boton2.setAlignmentX(Component.CENTER_ALIGNMENT); JButton boton3=new JButton("Otro botn"); boton3.setAlignmentX(Component.CENTER_ALIGNMENT); JButton boton4=new JButton("Soy el ltimo"); boton4.setAlignmentX(Component.CENTER_ALIGNMENT); panelContenido.setLayout(new BoxLayout(panelContenido,BoxLayout.Y_AXIS)); panelContenido.add(boton1); panelContenido.add(boton2); panelContenido.add(boton3); panelContenido.add(boton4); ventana.setContentPane(panelContenido); ventana.pack(); ventana.setVisible(true); } }

212

Estableciendo el Look & Feel


En el captulo anterior comentbamos que una caracterstica que ofrece Swing es el aspecto y comportamiento configurable de sus componentes, lo que se denomina Pluggable Look & Feel, es decir, a nuestra aplicacin Java le podemos asignar un aspecto especfico. Incluso, como veremos un poco ms adelante, podemos establecer y cambiar en tiempo de ejecucin de nuestra aplicacin su Look & Feel. Cuando no se indica ningn Look & Feel determinado Swing utiliza el Look & Feel por defecto, que es el Look & Feel de Java, tambin denominado Metal (fue el cdigo que se le dio al proyecto que lo desarroll). Se pueden distinguir cuatro Look & Feel estndar distintos: Java (Metal): se trata del Look & Feel por defecto y es el que se ha estado utilizando en todos los ejemplos de los componentes Swing, ya que en nuestros ejemplos no hemos especificado ningn Look & Feel. Se corresponde con la implementacin de la clase javax.swing.plaf.metal.MetalLookAndFeel. Este Look & Feel lo podemos utilizar en cualquier plataforma. Windows: es aspecto que presentan los sistemas Windows y lo encontramos implementado en la clase com.sun.java.swing.plaf.windows.WindowsLookAndFeel. A diferencia del anterior Look & Feel, ste nicamente puede ser utilizado en plataformas Windows. Motif: representa el aspecto CDE/Motif, que es el aspecto que tienen las plataformas de Sun. Este Look & Feel es implementado por la clase com.sun.java.swing.plaf.motif.MotifLookAndFeel, y al igual que el Look & Feel de Java puede ser utilizado en cualquier plataforma. Mac: este ltimo Look & Feel es el correspondiente a los sistemas Macintosh. Se encuentra en al clase javax.swing.plaf.mac.MacLookAndFeel. Al igual que ocurra con el Look & Feel de Windows, este Look & Feel est restringido a una plataforma especfica, esta plataforma es la plataforma Mac OS. Todas estas clases que implementan un Look & Feel determinado, tienen la caracterstica comn que todas ellas heredan de la clase abstracta javax.swing.LookAndFeel. Para indicar un Look & Feel determinado en una aplicacin Java utilizaremos la clase UIManager (gestor de interfaz grfico) que se encuentra en el paquete javax.swing. La clase UIManager ofrece un mtodo esttico llamado setLookAndFeel(), que recibe como parmetro una cadena que especifica la clase que se corresponde con el Look & Feel que deseamos asignar a nuestra aplicacin Java.
213

El mtodo setLookAndFeel() de la clase UIManager lanza una excepcin de la clase UnsupportedLookAndFeelException, que se encuentra tambin dentro del paquete javax.swing, cuando el Look & Feel que queremos utilizar no es soportado por la plataforma actual, deberemos atrapar por lo tanto esta excepcin cuando invoquemos el mtodo setLookAndFeel(). De esta forma si queremos que nuestra aplicacin posea el Look & Feel de Motif deberemos incluir el siguiente cdigo dentro del mtodo main() de la aplicacin.
try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); } catch (Exception e) { } new AplicacionSwing();

Pero el Look & Feel, como ya hemos adelantado al comienzo de este apartado, se puede establecer una vez que la aplicacin ya se ha iniciado y el interfaz de usuario es visible, es decir, podemos establecer el Look & Feel de la aplicacin en tiempo de ejecucin. En este caso adems de utilizar el mtodo setLookAndFeel() de la clase UIManager, deberemos utilizar el mtodo esttico updateComponentTreeUI() de la clase SwingUtilities, presente tambin en el paquete javax.swing. El mtodo updateComponentTreeUI() recibe como argumento un objeto de la clase Component que representa el contenedor de alto nivel sobre el que se quiere actualizar su Look & Feel. El mtodo updateComponentTreeUI() de la clase SwingUtilities debe utilizarse para cada uno de los contenedores de alto nivel del interfaz de usuario de la aplicacin, para que de esta forma todos actualicen su Look & Feel en tiempo de ejecucin. Adems es recomendable lanzar el mtodo pack() sobre cada uno de los contenedores de alto nivel del interfaz de usuario, para que se redimensionen todos los componentes segn su nuevo Look & Feel. La utilizacin de este mtodo, conjuntamente con el mtodo setLookAndFee() se puede ver en el Cdigo siguiente.
try { UIManager.setLookAndFeel(lf); SwingUtilities.updateComponentTreeUI(contenedor); this.pack(contenedor); } catch (Exception excepcion) { System.out.println("No se pudo asignar el Look & Feel"); }

En este caso se supone que slo existe un contenedor de alto nivel. La clase UIManager, adems del mtodo setLookAndFeel(), nos ofrece otros dos mtodos que pueden resultar de utilidad, estos mtodos son getSystemLookAndFeelClassName() y getCrossPlatformLooAndFeelClassName(). Ambos mtodos devuelven una cadena, en el primer caso se corresponde con el nombre de la clase del Look & Feel de la plataforma sobre la que se est ejecutando la aplicacin, y el segundo caso se corresponde con el nombre de la clase que ofrece el Look & Feel de Java, que es el Look & Feel que se garantiza funcionar correctamente para todas las plataformas. Por lo tanto estos dos mtodos se pueden utilizar como argumento para el mtodo setLookAndFeel(), as por ejemplo si deseamos aplicar el Look & Feel de la plataforma sobre la que se ejecuta la aplicacin, utilizaramos el Cdigo:
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { }

214

Para mostrar la caracterstica Pluggable Look & Feel de Swing se va a utilizar un sencillo ejemplo que consiste una aplicacin con una ventana (JFrame) que permite especificar el Look & Feel en tiempo de ejecucin. LA ventana de la aplicacin del ejemplo, adems de contener diversos componentes Swing, contiene cinco botones de opcin (JOptionButton) agrupados, y que al seleccionar cada uno de ellos se aplicar sobre la aplicacin el Look & Feel que representa cada opcin. Es decir, existen cinco botones de opcin, cuatro de ellos para cada uno de los distintos Look & Feel (Java, Windows, Motif y Mac) y otro para seleccionar el Look & Feel de la plataforma sobre la que se ejecuta la aplicacin. Antes de seguir comentando ms detalles de esta aplicacin de ejemplo, vamos a mostrar el aspecto que tendra esta aplicacin. Al ejecutar la aplicacin el aspecto que muestra es el de la Figura, y se corresponde con el Look & Feel de Java.

Al iniciarse su ejecucin dentro del constructor de la aplicacin, se establece el Look & Feel mediante el nombre de la clase devuelto por el mtodo getCrossPlatformLookAndFeelClassName() de la clase UIManager, debido a esto al arrancar la aplicacin comienza a ejecutarse utilizando el Look & Feel de Java. El cdigo fuente perteneciente al constructor de nuestra clase se puede observar :
public LF(){ super("Pluggable Look & Feel"); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { } getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS)); }

En el constructor adems de establecer el Look & Feel de Java se establece el gestor de diseo que va a utilizar el panel de contenido de la ventana. El gestor de diseo que se

215

utiliza es BoxLayout, descrito en el apartado anterior, en su variante de distribucin de componentes de arriba abajo (BoxLayout.Y_AXIS). De la figura anterior tambin se puede comprobar que la opcin seleccionada por defecto se corresponde lgicamente con el Look & Feel que presenta la aplicacin inicialmente. Podemos ir asignando a la aplicacin los distintos Look & Feel seleccionando la opcin correspondiente. A continuacin se muestran otras dos figuras, cada una con otro nuevo Look & Feel. La primera corresponde al Look & Feel de Windows y la segunda al Look & Feel de Motif.

En mi caso no he podido utilizar el Look & Feel de Mac, ya que he utilizado una plataforma Windows para ejecutar la aplicacin de ejemplo, por lo tanto ste Look & Feel no estar disponible, y adems en mi caso particular el Look & Feel del sistema (Look & Feel de la plataforma actual) ser por lo tanto el de Windows. No vamos hacer como en otros ejemplos de este curso, en los que se ha ofrecido el cdigo completo del ejemplo, slo vamos a mostrar y comentar aquellos fragmentos que resultan ms interesantes (sobre todo desde el punto de vista de la caracterstica Pluggable Look & Feel), ya que el cdigo fuente de la aplicacin de ejemplo es bastante sencillo y se utilizan componentes Swing vistos en los captulos anteriores.
216

public class LF extends JFrame implements ActionListener{

Como se puede ver es una sencilla ventana que va a contener una serie de componentes Swing. No vamos a detenernos en la creacin de los distintos componentes del interfaz de usuario, pero si que es interesante mostrar como se crean las opciones que permiten seleccionar el Look & Feel de la aplicacin. Para crear las opciones que representan los distintos LooK & Feel se ha utilizado el mtodo creaOpciones(), cuyo cdigo veremos:
public void creaOpciones(){ panelOpciones=new JPanel(); panelOpciones.setLayout(new GridLayout(1,5,5,5)); opWindows=new JRadioButton("Windows"); opWindows.setMnemonic(KeyEvent.VK_W); opWindows.addActionListener(this); panelOpciones.add(opWindows); opJava=new JRadioButton("Java/Metal"); opJava.setMnemonic(KeyEvent.VK_J); opJava.addActionListener(this); panelOpciones.add(opJava); opJava.setSelected(true); opMotif=new JRadioButton("Motif"); opMotif.setMnemonic(KeyEvent.VK_M); opMotif.addActionListener(this); panelOpciones.add(opMotif); opMac=new JRadioButton("Mac"); opMac.setMnemonic(KeyEvent.VK_C); opMac.addActionListener(this); panelOpciones.add(opMac); opSistema=new JRadioButton("Sistema"); opSistema.setMnemonic(KeyEvent.VK_S); opSistema.addActionListener(this); panelOpciones.add(opSistema); panelOpciones.setBorder(BorderFactory.createLoweredBevelBorder()); //se agrupan las opciones ButtonGroup grupo=new ButtonGroup(); grupo.add(opWindows); grupo.add(opJava); grupo.add(opMotif); grupo.add(opMac); grupo.add(opSistema); getContentPane().add(panelOpciones); }

La pulsacin de cada opcin ser tratada dentro del mtodo actionPerformed() que realmente, conjuntamente con el constructor de la clase, es dnde utilizamos mtodos y clases relacionados directamente con el mecanismo Pluggable Look & Feel. A continuacin se ofrece el cdigo fuente perteneciente al mtodo actionPerformed(). Ser en este mtodo dnde se establezca el Look & Feel seleccionado a travs de los distintos botones de opcin.
public void actionPerformed(ActionEvent evento){ Object fuente=evento.getSource(); String lf=""; if (fuente==opWindows) lf="com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; else if(fuente==opJava) lf="javax.swing.plaf.metal.MetalLookAndFeel"; else if(fuente==opMotif) lf="com.sun.java.swing.plaf.motif.MotifLookAndFeel";
217

else if(fuente==opMac) lf="javax.swing.plaf.mac.MacLookAndFeel"; else if(fuente==opSistema) lf=UIManager.getSystemLookAndFeelClassName(); try { UIManager.setLookAndFeel(lf); SwingUtilities.updateComponentTreeUI(this); this.pack(); } catch (UnsupportedLookAndFeelException excepcion) { texto.append("Look & Feel no soportado.\n"+excepcion+"\n"); } catch (ClassNotFoundException excepcion){ texto.append("Clase no encontrada.\n"+excepcion+"\n"); } catch (InstantiationException excepcion){ texto.append("Excepcin de instanciacin.\n"+excepcion+"\n"); } catch(IllegalAccessException e){ texto.append("Excepcin de acceso.\n"+e+"\n"); } }

Como se puede comprobar a la vista del cdigo anterior, se decidimos atrapar la excepcin UnsupportedLookAndFeelException, se nos obligar a atrapar tres excepciones ms. Por lo dems, si se tiene en cuenta lo explicado hasta el momento, el cdigo de este mtodo es bastante sencillo, lo nico que se hace es establecer el Look & Feel que se corresponde con la opcin pulsada. Si ocurre una excepcin se muestra en el rea de texto (JTextArea) de la aplicacin. Si en mi caso particular, utilizando una plataforma Windows, selecciono la opcin correspondiente al Look & Feel de Mac, obtendr una excepcin lanzada por el mtodo setLooAndFeel(), esto se puede observar en la Figura.

218

Ejemplos de Swing
Campos de Texto. import java.awt.*; import javax.swing.*; //import java.swing.text.*; public class CampoTexto extends JApplet { JPasswordField jtf; public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); jtf = new JPasswordField(15); jtf.setEchoChar('*'); cp.add(jtf); } } JcomboBox (Combos) import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ComboImagen extends JApplet implements ItemListener { JLabel jl; ImageIcon open, save, delete; public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); JComboBox jc = new JComboBox(); jc.addItem("open"); jc.addItem("save"); jc.addItem("delete"); jc.addItemListener(this); cp.add(jc); jl = new JLabel(new ImageIcon("open.gif")); cp.add(jl); } public void itemStateChanged(ItemEvent ie) { String s = (String)ie.getItem(); jl.setIcon(new ImageIcon(s + ".gif")); } } Jbutttton (Botones) import java.awt.*; import java.awt.event.*; import javax.swing.*; public class BotoneImagen extends JApplet implements ActionListener { JTextField jtf; public void init() { Container cp = getContentPane();
219

cp.setLayout(new FlowLayout()); ImageIcon abrir = new ImageIcon("open.gif"); JButton jb = new JButton(abrir); jb.setActionCommand("abrir"); jb.addActionListener(this); cp.add(jb); ImageIcon salvar = new ImageIcon("save.gif"); jb = new JButton(salvar); jb.setActionCommand("salvar"); jb.addActionListener(this); cp.add(jb); ImageIcon borrar = new ImageIcon("delete.gif"); jb = new JButton(borrar); jb.setActionCommand("borrar"); jb.addActionListener(this); cp.add(jb); jtf = new JTextField(15); cp.add(jtf); } public void actionPerformed(ActionEvent ae) { jtf.setText(ae.getActionCommand()); } } Componentes ficha (Tabbed Pane) import javax.swing.*; public class JTabbedPaneEjemplo extends JApplet { public void init() { JTabbedPane jtp = new JTabbedPane(); jtp.addTab("Ciudad", new Ciudades()); jtp.addTab("Color", new Colores()); jtp.addTab("Savor", new Savores()); getContentPane().add(jtp); } } class Ciudades extends JPanel { public Ciudades() { JButton b1 = new JButton("San Vicente"); add(b1); JButton b2 = new JButton("San Salvador"); add(b2); JButton b3 = new JButton("Santa Ana"); add(b3); JButton b4 = new JButton("San Miguel"); add(b4); } } class Colores extends JPanel { public Colores() {
220

JCheckBox cb1 = new JCheckBox("Rojo"); add(cb1); JCheckBox cb2 = new JCheckBox("Verde"); add(cb2); JCheckBox cb3 = new JCheckBox("Azul"); add(cb3); } } class Savores extends JPanel { public Savores() { JComboBox jcb = new JComboBox(); jcb.addItem("Vainilla"); jcb.addItem("Chocolate"); jcb.addItem("Fresa"); add(jcb); } } Arboles (Trees) import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; /* <applet code="Arboles" width=400 height=200> </applet> */ public class Arboles extends JApplet { JTree arbol; JTextField jtf; public void init() { // Obtener el content pane Container contentPane = getContentPane(); // Configurando el layout contentPane.setLayout(new BorderLayout()); // Creando el nodo raiz DefaultMutableTreeNode tope = new DefaultMutableTreeNode("Opciones"); // Creando el subarbol "A" DefaultMutableTreeNode a = new DefaultMutableTreeNode("A"); tope.add(a); DefaultMutableTreeNode a1 = new DefaultMutableTreeNode("A1"); a.add(a1); DefaultMutableTreeNode a2 = new DefaultMutableTreeNode("A2"); a.add(a2); // Creando el subarbol "B" DefaultMutableTreeNode b = new DefaultMutableTreeNode("B"); tope.add(b);
221

DefaultMutableTreeNode b1 = new DefaultMutableTreeNode("B1"); b.add(b1); DefaultMutableTreeNode b2 = new DefaultMutableTreeNode("B2"); b.add(b2); DefaultMutableTreeNode b3 = new DefaultMutableTreeNode("B3"); b.add(b3); // Creando el arbol arbol = new JTree(tope); // agregando al scroll pane int v = ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; int h = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; JScrollPane jsp = new JScrollPane(arbol, v, h); // agregando el scroll pane al content pane contentPane.add(jsp, BorderLayout.CENTER); // agregando text field to applet jtf = new JTextField("", 20); contentPane.add(jtf, BorderLayout.SOUTH); // implementar la clase interna para eventos de raton arbol.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent me) { doMouseClicked(me); } }); } void doMouseClicked(MouseEvent me) { TreePath tp = arbol.getPathForLocation(me.getX(), me.getY()); if(tp != null) jtf.setText(tp.toString()); else jtf.setText(""); } } Tablas(Tables) import java.awt.*; import javax.swing.*; public class Tablas extends JApplet { public void init() { Container contentPane = getContentPane(); // Manejador layout contentPane.setLayout(new BorderLayout()); // Inicializando las columnas final String[] cabecera = { "Nombre", "Edad", "Sexo" }; // Inicializando los datos final Object[][] datos = { { "Luis", "25", "M" }, { "Maria", "24", "F" }, { "Roberto", "28", "M" },
222

{ "Cesar", "24", "M" }, { "Laura", "20", "F" }, { "Elena", "19", "F" } }; // Crear la tabla JTable tabla = new JTable(datos, cabecera); // agregando scroll pane int v = ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; int h = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; JScrollPane jsp = new JScrollPane(tabla, v, h); // agregando al content pane contentPane.add(jsp, BorderLayout.CENTER); } } Empezando a dibujar con Swing A continuacin vamos a ver qu tenemos que hacer para dibujar en Swing. Lo primero es hacerse con el objeto grfico del componente sobre el cual queremos dibujar. El objeto grfico es un objeto que posee toda la informacin que un componente necesita para dibujarse en pantalla. Para obtener el objeto grfico empleamos el mtodo getGraphics(), de JComponent. Graphics g = Componente.getGraphics() Una vez obtenido dicho objeto grfico procedemos a dibujar empleando los mtodos que dicho objeto nos proporciona. Estos mtodos son tan intuitivos como numerosos, por lo que no haremos aqu una revisin de ellos. Si deseamos dibujar algo lo que ms normal es acudir a la documentacin de las librera y buscar en ella los mtodos que necesitemos. A modo de ejemplo, si queremos dibujar una lnea y vamos a la documentacin de la clase Graphics encontraremos un mtodo drawLine(int x1, int x2, int x3 int x4) en el cual (x1, x2) es el punto de inicio de la lnea y (x3, x4) el fin de la lnea. Veamos un ejemplo de esto. Ejemplo 1. import java.awt.event.*; import javax.swing.*; import java.awt.*; public class Dibujar1 extends JFrame implements ActionListener{ public Dibujar1(){ setSize(400,400); setTitle("Dibujo"); Container contentpane = this.getContentPane(); contentpane.setLayout (new BorderLayout()); JPanel p =new JPanel(); contentpane.add(p, BorderLayout.SOUTH); JButton boton = new JButton("Dibujar"); boton.addActionListener(this); boton.setActionCommand("Dibujar"); p.add(boton); JButton boton2 = new JButton("Salir"); boton2.addActionListener(this); boton2.setActionCommand("Salir"); p.add(boton2); contentpane.add(panel, BorderLayout.CENTER); this.setVisible(true);
223

} public void actionPerformed(ActionEvent e){ String comando = e.getActionCommand(); if(comando.equals("Salir")){ System.exit(0); } Graphics g = panel.getGraphics(); g.setColor(Color.blue); g.drawLine(0,0, 100, 100); g.drawLine(150, 150,(int) (this.getSize()).getWidth(),(int)(this.getSize()).getHeight()); g.drawRect(100, 80, 200, 200); g.setColor(Color.red); g.fillRect(110, 90, 150, 150); } JPanel panel = new JPanel(); public static void main(String[] args){ new Dibujar1(); } } El mtodo paintComponent Cuando cualquier componente de las libreras Swing por cualquier razn necesita redibujarse en pantalla llama al mtodo paintComponent. En este mtodo se halla toda la informacin que se necesita para dibujar el componente en pantalla. Causas externas a un componente que fuerzan que este se redibuje son que la ventana en la que est se minimiza y luego se maximiza o que otra ventana se ponga encima de la ventana que lo contiene. Adems el componente se dibujar cuando se crea y cuando se invoque el mtodo repaint(). Cuando este mtodo es invocado lo nico que aparecer en el componente es lo que se dibuje desde el, todo lo dems es borrado. Este es el motivo por el cual en nuestro ejemplo anterior al minimizar y luego maximizar la pantalla dejamos de ver lo que habamos dibujado. Si queremos que siempre que el componente se redibuje apareciesen nuestros dibujos hemos de sobrescribir el mtodo paintComponent y escribir en el cdigo necesario para realizar estos dibujos. Veamos como haramos esto sobre el ejemplo Dibujar1: Ejemplo 2. import java.awt.event.*; import javax.swing.*; import java.awt.*; public class Dibujar2 extends JFrame implements ActionListener{ public Dibujar2(){ setSize(400,400); setTitle("Dibujo"); Container contentpane = this.getContentPane(); contentpane.setLayout (new BorderLayout()); JPanel p =new JPanel(); contentpane.add(p, BorderLayout.SOUTH); JButton boton = new JButton("Dibujar"); boton.addActionListener(this); boton.setActionCommand("Dibujar"); p.add(boton); JButton boton2 = new JButton("Salir");
224

boton2.addActionListener(this); boton2.setActionCommand("Salir"); p.add(boton2); contentpane.add(panel, BorderLayout.CENTER); this.setVisible(true); } public void actionPerformed(ActionEvent e){ String comando = e.getActionCommand(); if(comando.equals("Salir")){ System.exit(0); } Graphics g = panel.getGraphics(); g.setColor(Color.red); g.fillRect(110, 90, 150, 150); panel.paintComponent(g); } MyPanel panel = new MyPanel(); public static void main(String[] args){ new Dibujar2(); }} class MyPanel extends JPanel{ public void paintComponent(Graphics g){ g.setColor(Color.blue); g.drawLine(0,0, 100, 100); g.drawLine(150, 150,(int) (this.getSize()).getWidth(),(int)(this.getSize()).getHeight()); g.drawRect(100, 80, 200, 200); }}

225

226

1. INTRODUCCION JDBC
Java Database Connectivity (JDBC) es una interfase de acceso a bases de datos estndar SQL que proporciona un acceso uniforme a una gran variedad de bases de datos relacionales. JDBC tambin proporciona una base comn para la construccin de herramientas y utilidades de alto nivel.

Drivers JDBC Para usar JDBC con un sistema gestor de base de datos en particular, es necesario disponer del driver JDBC apropiado que haga de intermediario entre sta y JDBC. Dependiendo de varios factores, este driver puede estar escrito en Java puro, o ser una mezcla de Java y mtodos nativos JNI (Java Native Interface).

1.1 Qu es JDBC?
JDBC es el API para la ejecucin de sentencias SQL. (Como punto de inters JDBC es una marca registrada y no un acrnimo, no obstante a menudo es conocido como Java Database Connectivity). Consiste en un conjunto de clases e interfases escritas en el lenguaje de programacin Java. JDBC suministra un API estndar para los desarrolladores y hace posible escribir aplicaciones de base de datos usando un API puro Java. Usando JDBC es fcil enviar sentencias SQL virtualmente a cualquier sistema de base de datos. En otras palabras, con el API JDBC, no es necesario escribir un programa que acceda a una base de datos Sybase, otro para acceder a Oracle y otro para acceder a Informix. Un nico programa escrito usando el API JDBC y el programa ser capaz de enviar sentencias SQL a la base de datos apropiada. Y, con una aplicacin escrita en el lenguaje de programacin Java, tampoco es necesario escribir diferentes aplicaciones para ejecutar en diferentes plataformas. La combinacin de Java y JDBC permite al programador escribir una sola vez y ejecutarlo en cualquier entorno. Java, siendo robusto, seguro, fcil de usar, fcil de entender, y descargable automticamente desde la red, es un lenguaje base excelente para aplicaciones de base de datos.
227

JDBC expande las posibilidades de Java. Por ejemplo, con Java y JDBC API, es posible publicar una pgina web que contenga un applet que usa informacin obtenida de una base de datos remota. O una empresa puede usar JDBC para conectar a todos sus empleados (incluso si usan un conglomerado de mquinas Windows, Macintosh y UNIX) a una base de datos interna va intranet. Con cada vez ms y ms programadores desarrollando en lenguaje Java, la necesidad de acceso fcil a base de datos desde Java contina creciendo.

1.1.1 Qu hace JDBC?


Simplemente JDBC hace posible estas tres cosas: Establece una conexin con la base de datos. Enva sentencias SQL Procesa los resultados.

El siguiente fragmento de cdigo nos muestra un ejemplo bsico de estas tres cosas:
Connection con = DriverManager.getConnection ( "jdbc:odbc:wombat", "login", "password"); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1"); while (rs.next()) { int x = rs.getInt("a"); String s = rs.getString("b"); float f = rs.getFloat("c"); }

1.1.2 JDBC es un API de bajo nivel y una base para APIs de alto nivel.
JDBC es una interfase de bajo nivel, lo que quiere decir que se usa para invocar o llamar a comandos SQL directamente. En esta funcin trabaja muy bien y es ms fcil de usar que otros APIs de conexin a bases de datos, pero est diseado de forma que tambin sea la base sobre la cual construir interfaces y herramientas de alto nivel. Una interfase de alto nivel es amigable, usa un API mas entendible o ms conveniente que luego se traduce en la interfase de bajo nivel tal como JDBC.

228

1.1.3 JDBC frente a ODBC y otros APIs


En este punto, el ODBC de Microsoft (Open Database Connectvity), es probablemente el API ms extendido para el acceso a bases de datos relacionales. Ofrece la posibilidad de conectar a la mayora de las bases de datos en casi todas las plataformas. Por qu no usar, entonces, ODBC, desde Java?. La respuesta es que se puede usar ODBC desde Java, pero es preferible hacerlo con la ayuda de JDBC mediante el puente JDBC-ODBC. La pregunta es ahora por qu necesito JDBC?. Hay varias respuestas a estas preguntas: 1.- ODBC no es apropiado para su uso directo con Java porque usa una interface C. Las llamadas desde Java a cdigo nativo C tienen un nmero de inconvenientes en la seguridad, la implementacin, la robustez y en la portabilidad automtica de las aplicaciones. 2.- Una traduccin literal del API C de ODBC en el API Java podra no ser deseable. Por ejemplo, Java no tiene punteros, y ODBC hace un uso copioso de ellos, incluyendo el notoriamente propenso a errores void * . Se puede pensar en JDBC como un ODBC traducido a una interfase orientada a objeto que es el natural para programadores Java. 3. ODBC es difcil de aprender. Mezcla caractersticas simples y avanzadas juntas, y sus opciones son complejas para querys simples. JDBC por otro lado, ha sido diseado para mantener las cosas sencillas mientras que permite las caractersticas avanzadas cuando stas son necesarias. 4. Un API Java como JDBC es necesario en orden a permitir una solucin Java pura. Cuando se usa ODBC, el gestor de drivers de ODBC y los drivers deben instalarse manualmente en cada mquina cliente. Como el driver JDBC esta completamente escrito en Java, el cdigo JDBC es automticamente instalable, portable y seguro en todas las plataformas Java. En resumen, el API JDBC es el interfase natural de Java para las abstracciones y conceptos bsicos de SQL. JDBC retiene las caractersticas bsicas de diseo de ODBC; de hecho, ambos interfaces estn basados en el X/Open SQL CLI (Call Level Interface). Ms recientemente Microsoft ha introducido nuevas API detrs de ODBC. RDO, ADO y OLE DB. Estos diseos se mueven en la misma direccin que JDBC en muchas maneras, puesto que se les da una orientacin a objeto basndose en clases que se implementan sobre ODBC.

229

1.1.4 Modelos en dos y tres pisos. El API JDBC soporta los modelos en dos y tres pisos de acceso a base de datos. En el modelo de dos-pisos, un applet Java o una aplicacin habla directamente con la base de datos. Esto requiere un driver JDBC que pueda comunicar con el gestor de base de datos particular al que se pretende acceder. Las sentencias SQL de usuario se envan a la base de datos, y el resultado de estas sentencias se envan al usuario. La base de datos puede estar localizada en otra mquina a la que el usuario se conecta mediante la red. Esta es una configuracin Cliente/Servidor en la que la mquina del usuario es el cliente y la mquina que hospeda a la base de datos es el servidor. La red puede ser una intranet, por ejemplo, que conecta a los empleados dentro de la corporacin, o puede ser Internet.

Aplicacion Java

Mquina cliente

JDBC Protocolo propietario DBMS

DBMS

Servidor de BD.

En el modelo de tres-pisos, los comandos se envan a un piso intermedio de servicios, que enva las sentencias SQL a la base de datos. La base de datos procesa las sentencias SQL y devuelve los resultados a el piso intermedio, que a su vez lo enva al usuario. Los directores de IS encuentran este modelo muy atractivo por que el piso intermedio hace posible mantener el control sobre los datos y los tipos de actualizaciones que pueden hacerse en los datos corporativos. Otra ventaja es que el usuario puede emplear un API de alto nivel ms sencillo que es traducido por el piso intermedio en las llamadas de bajo nivel apropiadas. Finalmente en muchos casos la arquitectura de tres niveles puede proporcionar ventajas de rendimiento. Hasta ahora, este nivel intermedio ha sido escrito en lenguajes como C C++, que ofrecen un rendimiento ms rpido. De cualquier modo, con la introduccin de compiladores optimizadores que traducen el bytecode en cdigo mquina eficiente, se est convirtiendo en prctico desarrollar este nivel intermedio en Java.
230

Esta es una gran ventaja al hacer posible aprovechar las caractersticas de robustez, multiproceso y seguridad de Java.

Aplicacion Java

Mquina cliente

llamadas HTTP, RML CORBA

Aplicacion Servidora (Java) JDBC

Servidor (Lgica de negocio)

Protocolo propietario del DBMS

DBMS

Servidor de BD.

1.1.5 SQL Conforme


SQL es el lenguaje estndar para el acceso a las bases de datos relacionales. Una de las reas de dificultad es que aunque muchas DBMSs (Data Base Management Systems) usan un formato estndar de SQL para la funcionalidad bsica, estos no conforman la sintaxis ms recientemente definidas o semnticas para funcionalidades ms avanzadas. Por ejemplo, no todas las bases de datos soportan procedimientos almacenados o joins de salida, y aquellas que lo hacen no son consistentes con otras. Es de esperar que la porcin de SQL que es verdaderamente estndar se expandir para incluir ms y ms funcionalidad. Entretanto, de cualquier modo, el API de JDBC debe soportar SQL tal como es. Una forma en que el API JDBC trata este problema es permitir que cualquier cadena de bsqueda se pase al driver subyacente del DBMS. Esto quiere decir que una aplicacin es libre de usar la sentencia SQL tal como quiera, pero se corre el riesgo de recibir un error en el DBMS. De hecho una consulta de una aplicacin incluso no tiene por que ser SQL, o puede ser una derivacin especializada de SQL

231

diseada para especificas DBMS (para consultas a imgenes o documentos por ejemplo).
Una segunda forma en que JDBC trata este problema es proveer clusulas de escape al estilo de ODBC , que se discutirn en el 4.1.5. Sintaxis de escape en Sentencias Objetos.

La sintaxis de escape provee una sintaxis JDBC estndar para varias de las reas ms comunes de divergencia SQL. Por ejemplo, ah escapes para literales de fecha o procedimientos almacenados. Para aplicaciones complejas, JDBC trata la conformidad SQL de una tercera manera. Y es proveer informacin descriptiva sobre el DBMS por medio de la interfase DatabaseMetaData por la que las aplicaciones pueden adaptarse a
los requerimientos y posibilidades de cada DBMS:

Como el API JDBC se usar como base para el desarrollo de herramientas de acceso y APIs de alto nivel , direccionar el problema de la conformidad a cualquiera de estas. La designacin JDBC COMPLIANT se cre para situar un nivel estndar de funcionalidad JDBC en la que los usuarios puedan confiar. En orden a usar esta designacin, un driver debe soportar al menos el nivel de entrada ANSI SQL-2 Entry Level. Los desarrolladores de drivers pueden cerciorarse que sus drivers cumplan estas especificaciones mediante la suite de test disponible en el API JDBC. La designacin JDBC COMPLIANT indica que la implementacin JDBC de un vendedor ha pasado los tests de conformidad suministrados por JavaSoft. Estas pruebas de conformidad chequean la existencia de todas las clases y mtodos definidos en el API JDBC, y chequean tanto como es posible que la funcionalidad SQL Entry Level est disponible. Tales tests no son exhaustivos, por supuesto, y JavaSoft no esta distribuyendo implementaciones de vendedores, pero esta definicin de compliance tiene algn grado de seguridad en una implementacin JDBC. Con la mayor aceptacin de JDBC por parte de vendedores de bases de datos, de servicios de Internet y desarrolladores, JDBC se est convirtiendo en el estndar de acceso a bases de datos.

1.2 Productos JDBC.


Existen una serie de productos basados en JDBC que ya han sido desarrollados. Por supuesto la informacin de este apartado ser rpidamente obsoleta. http://java.sun.com/products/jdbc

232

1.2.1 JavaSoft Framework


JavaSoft suministra tres componentes JDBC como parte del JDK El gestor de drivers JDBC La suite de testeo de drivers JDBC El puente JDBC-ODBC

El gestor de drivers es la espina dorsal de la arquitectura JDBC. Actualmente es bastante pequea y simple; su funcin principal es conectar las aplicaciones Java al driver JDBC correcto y despus soltarlo.

La suite de testeo JDBXC suministra seguridad y confianza en los drivers JDBC que se ejecutarn en el programa. Solo los drivers que pasan el test pueden ser designados JDBC COMPLIANT. El puente JDBC-ODBC permite a los drivers ODBC usarse como drivers JDBC. Fue implementado como una forma de llegar rpidamente al fondo de JDBC y para proveer de acceso a los DBMS menos populares si no existen drivers JDBC para ellos.

233

1.2.2 Tipos de drivers JDBC


Los drivers que son susceptibles de clasificarse en una de estas cuatro categoras. 1.- puente JDBC-ODBC ms driver ODBC: El producto de JavaSoft suministra acceso va drivers ODBC. Ntese que el cdigo binario ODBC, y en muchos casos el cdigo cliente de base de datos, debe cargarse en cada mquina cliente que use este driver. Como resultado, este tipo de driver es el ms apropiado en un red corporativa donde las instalaciones clientes no son un problema mayor, o para una aplicacin en el servidor escrito en Java en una arquitectura en tres-niveles. 2.- driver Java parcialmente Nativo. Este tipo de driver convierte llamadas JDBC en llamadas del API cliente para Oracle, Sybase, Informix, DB2 y otros DBMS. Ntese que como el driver puente, este estilo de driver requiere que cierto cdigo binario sea cargado en cada mquina cliente. 3.- driver Java nativo JDBC-Net. Este driver traduce llamadas JDBC al protocolo de red independiente del DBMS que despus es traducido en el protocolo DBMS por el servidor. Este middleware en el servidor de red es capaz de conectar a los clientes puros Java a muchas bases de datos diferentes. El protocolo especfico usado depender del vendedor. En general esta es la alternativa ms flexible. 4.- driver puro Java y nativo-protocolo.. Este tipo de driver convierte llamadas JDBC en el protocolo de la red usado por DBMS directamente. Esto permite llamadas directas desde la mquina cliente al servidor DBMS y es la solucin ms prctica para accesos en intranets. Dado que muchos de estos protocolos son propietarios, los fabricantes de bases de datos sern los principales suministradores. Esperamos que las alternativas 3 y 4 sean las formas preferidas de acceder a las bases de datos desde JDBC. Las categoras 1 y 2 son soluciones interinas cuando no estn disponibles drivers directos puros Java.

234

2. CONEXIN
2.1 Vista Preliminar
Un objeto Connection representa una conexin con una base de datos. Una sesin de conexin incluye las sentencias SQL que se ejecutan y los resultados que son devueltos despus de la conexin. Una nica aplicacin puede tener una o ms conexiones con una nica base de datos, o puede tener varias conexiones con varias bases de datos diferentes.

2.1.1 Apertura de una conexin


La forma estndar de establecer una conexin a la base de datos es mediante la llamada al mtodo DriverManager.getConnection. Este mtodo toma una cadena que contiene una URL. La clase DriverManager, referida como la capa de gestin JDBC, intenta localizar un driver que pueda conectar con la base de datos representada por la URL. La clase DriverManager mantiene una lista de clases
Driver registradas y cuando se llama al mtodo getConnection, se chequea con cada driver de la lista hasta que encuentra uno que pueda conectar con la base de datos especificada en la URL. El mtodo connect de Driver usa esta URL para establecer la conexin. Un usuario puede evitar la capa de gestin de JDBC y llamar a los mtodos de Driver directamente. Esto puede ser til en el caso raro que dos drivers puedan conectar con la base de datos y el usuario quiera seleccionar uno explcitamente. Normalmente, de cualquier modo, es mucho ms fcil dejar que la clase DriverManager maneje la apertura de la conexin. El siguiente cdigo muestra como ejemplo una conexin a la base de datos localizada en la URL jdbc:odbc:wombat con un user ID de oboy y password 12java. String url = "jdbc:odbc:wombat"; Connection con = DriverManager.getConnection(url, "oboy", "12Java");

2.1.2 Uso general de URLs


Dado que URLs causan a menudo cierta confusin, daremos primero una breve explicacin de URL en general y luego entraremos en una discusin sobre URLs de JDBC. Una URL (Uniform Resource Locator) da informacin para localizar un recurso en Internet. Puede pensarse en ella como una direccin.
235

La primera parte de una URL especifica el protocolo usado para acceder a la informacin y va siempre seguida por dos puntos. Algunos protocolos comunes son ftp, que especifica file transfer protocol y http que especifica hypertext transfer protocol. Si el protocolo es file indica que el recurso est en un sistema de ficheros local mejor que en Internet : veamos unos ejemplos:
ftp://javasoft.com/docs/JDK-1_apidocs.zip http://java.sun.com/products/jdk/CurrentRelease file:/home/haroldw/docs/books/tutorial/summary.html

El resto de la URL, todo despus de los dos puntos, da informacin sobre donde se encuentra la fuente de los datos. Si el protocolo es file, el resto de la URL es el path al fichero. Para los protocolos ftp y http, el resto de la URL identifica el host y puede opcionalmente dar un path ms especfico al sitio. Por ejemplo, el siguiente es la URL para la home page de JavaSoft. Esta URL identifica solo al host:
http://java.sun.com

2.1.3 JDBC y URLs


Una URL JDBC suministra una forma de identificar una base de datos para que el driver apropiado pueda reconocerla y establecer la conexin con ella. Los desarrolladores de drivers son los que determinan actualmente que JDBC URL identifica su driver particular. Los usuarios no tienen por que preocuparse sobre como se forma una URL JDBC; ellos simplemente usan la URL suministrada con el driver que usan. El rol de JDBC es recomendar algunas convenciones a los fabricantes de drivers para su uso. Dado que los JDBC URL se usan con varios tipos de drivers, las convenciones son necesariamente muy flexibles. Primero, permiten a diferentes drivers usar diferentes esquemas para nombrar las bases de datos. EL subprotocolo odbc, por ejemplo, permite que las URL contengan valores de atributos (pero no los requieren). Segundo, las URLs JDBC permiten a los desarrolladores de drivers codificar toda la informacin de la conexin dentro de ella. Esto hace posible, por ejemplo, para un applet que quiera hablar con una base de datos dada el abrir la conexin sin necesitar que el usuario realice ninguna tarea de administracin de sistemas. Tercero, las URLs JDBC permiten un nivel de indireccin. Esto quiere decir que la URL JDBC puede referirse a una base de datos lgica o un host lgico que se traduce dinmicamente al nombre actual por el sistema de nombramiento de la red.

236

Esto permite a los administradores de sistemas evitar dar especificaciones de sus hosts como parte del nombre JDBC. Hay una variedad de servicios de nomenclatura de red diferentes (tales como DNS, NIS y DCE), y no hay restriccin acerca de cual usar. La sintaxis para las URLs JDBC que se muestra a continuacin tiene tres partes separadas por dos puntos:
jdbc:<subprotocol>:<subname>

Las tres partes se descomponen como sigue: 1 2 jdbc el protocolo. El protocolo en una URL JDBC es siempre jdbc - <subprotocol> - el nombre del driver o el nombre del mecanismo de conectividad con la base de datos, que puede estar soportado por uno o ms drivers. Un ejemplo sobresaliente de un subprotocolo es odbc, que ha sido reservado para URLs que especifican nombres de fuentes de datos estilo ODBC. Por ejemplo para acceder a la base de datos a travs del puente JDBC-ODBC, la URL a usar podra ser algo as como lo siguiente:
jdbc:odbc:fred

En este ejemplo, el subprotocolo es odbc y el subnombre fred es el nombre de la fuente de datos ODBC local. Si se quiere usar un servicio de nombre de la red (ya que el nombre de la base de datos en la URL JDBC no tiene por que ser su nombre actal), el servicio de nombre puede ser el subprotocolo. Por tanto, para el ejemplo, podra tener una URL como : jdbc:dcenaming:accounts-payable En este ejemplo, la URL especifica que el servicio local DCE resolver el nombre de la base de datos accounts-payable para poder conectar con la base de datos real. 3 <subname> - una forma de identificar la base de datos. El subnombre puede variar dependiendo del subprotocolo, y puede tener un subnombre con cualquier sintaxis interna que el fabricante del driver haya escogido. El punto de un subnombre es para dar informacin suficiente para localizar la base de datos. En el ejemplo anterior fred es suficiente porque ODBC suministra la informacin restante. Una base de datos en un servidor remoto requiere ms informacin. Si la base de datos va a ser accesible a travs de Internet, por ejemplo, la direccin de red debera incluirse en la URL JDBC como parte del

237

subnombre y debera seguir a la convencin estndar de nomenclatura de URL.


//hostname:port/subsubname

Suponiendo que dbnet es un protocolo para conectar a un host, la URL JDBC debera parecerse a algo como:
jdbc:dbnet://wombat:356/fred

2.1.4 El subprotocolo odbc


El subprotocolo odbc es un caso especial. Ha sido reservado para URLs que especifican el estilo ODBC de nombres de fuentes de datos y que tiene la caracterstica de permitir especificar cualquier nmero de valores de atributos despus del subnombre (el nombre de la fuente de datos) La sintaxis completa para el protocolo odbc es:
jdbc:odbc:<data-source-name>[;<attribute-name>=<attribute-value>]*

Todos los siguientes son nombres vlidos jdbc:odbc


jdbc:odbc:qeor7 jdbc:odbc:wombat jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER jdbc:odbc:qeora;UID=kgh;PWD=fooey

2.1.5 Registro de subprotocolos


Un desarrollador de drivers puede reservar un nombre para usar como el subprotocolo en una URL JDBC. Cuando la clase DriverManager presenta este
nombre a su lista de drivers registrados, el driver para el que este nombre est reservado debera reconocerlo y establecer una conexin a la base de datos que lo identifica. Por ejemplo odbc est reservado para el puente JDBC-ODBC. Si fuera, por poner otro ejemplo, Miracle Corporation, y quisiera registrar miracle como el subprotocolo para el driver JDBC que conecte a su DBMS Miracle no tiene que usar sino ese nombre.

2.1.6 Envo de Sentencias SQL


Una vez que la conexin se haya establecido, se usa para pasar sentencias SQL a la base de datos subyacente. JDBC no pone ninguna restriccin sobre los tipos de sentencias que pueden enviarse: esto da un alto grado de flexibilidad, permitiendo el uso de sentencias especficas de la base de datos o incluso sentencias no SQL. Se requiere de cualquier modo, que el usuario sea responsable de asegurarse que la base de datos subyacente sea capaz de procesar las sentencias SQL que le estn siendo enviadas y soportar las consecuencias si no es as. Por ejemplo, una aplicacin que intenta enviar una llamada a un procedimiento almacenado a una DBMS que no soporta procedimientos almacenados no tendr xito y generar una excepcin. JDBC requiere que un driver cumpla al menos ANSI SQL-2 Entry
238

Level para ser designado JDBC COMPLIANT. Esto significa que los usuarios pueden contar al menos con este nivel de funcionalidad. JDBC suministra tres clases para el envo de sentencias SQL y tres mtodos en la interfaz Connection para crear instancias de estas tres clases. Estas clases y mtodos son los siguientes: 1.- Statement creada por el mtodo createStatement. Un objeto Statement
se usa para enviar sentencias SQL simples 2.- PreparedStatement creada por el mtodo prepareStatement- Un objeto PreparedStatement se usa para sentencias SQL que toman uno o ms parmetros como argumentos de entrada (parmetros IN). PreparedStatement tiene un grupo de mtodos que fijan los valores de los parmetros IN, los cuales son enviados a la base de datos cuando se procesa la sentencia SQL. Instancias de PreparedStatement extienden Statement y por tanto heredan los mtodos de Statement. Un objeto PreparedStatement es potencialmente ms eficiente que un objeto Statement porque este ha sido precompilado y almacenado para su uso futuro.

3.- CallableStatement creado por el mtodo prepareCall. Los objetos CallableStatement se usan para ejecutar procedimientos almacenados SQL un grupo de sentencias SQL que son llamados mediante un nombre, algo parecido a una funcin - . Un objeto CallableStatement hereda mtodos para el manejo de los parmetros IN de PreparedStatement, y aade mtodos para el manejo de los parmetros OUT e INOUT. La lista siguiente da una forma rpida de determinar que mtodo Connection es el apropiado para crear los diferentes tipos de sentencias SQL. El mtodo createStatement se usa para: Sentencias SQL simples (sin parmetros).

El mtodo prepareStatement se usa para: Sentencias SQL con uno ms parmetros IN Sentencias SQL simples que se ejecutan frecuentemente

El mtodo prepareCall se usa para: Llamar a procedimientos almacenados.

2.1.7 Transacciones
Una transaccin consiste en una o ms sentencias que han sido ejecutadas, completas y, o bien se ha hecho commit o bien roll-back. Cuando se llama al mtodo commit o rollback , la transaccin actal finaliza y comienza otra.
239

Una conexin nueva se abre por defecto en modo auto-commit, y esto significa que cuando se completa se llama automticamente al mtodo commit. En este caso, cada sentencia es commitada individualmente, por tanto una transaccin se compone de una nica sentencia. Si el modo auto-commit es desactivado, la transaccin no terminar hasta que se llame al mtodo commit o al mtodo rollback explcitamente, por lo tanto incluir todas las sentencias que han sido ejecutadas desde la ltima invocacin a uno de los mtodos commit o rollback. En este segundo caso, todas las sentencias de la transaccin son commitadas o deshechas en grupo. El mtodo commit hace permanente cualquier cambio que una sentencia SQL realiza en la base de datos, y libera cualquier bloqueo mantenido por la transaccin. El mtodo rollback descarta estos cambios. A veces un usuario no quiere que tenga efecto un determinado cambio a menos que se efectu otro. Esto puede hacerse desactivando el modo auto-commit y agrupando ambas actualizaciones en una transaccin. Si ambas actualizaciones tienen xito se llama al mtodo commit haciendo estos cambios permanentes, si uno o los dos fallan, se llama al mtodo rollback, restaurando los valores existentes l inicio de la transaccin. Muchos drivers JDBC soportan transacciones. De hecho, un driver JDBC-compliant debe soportar transacciones. DatabaseMetaData suministra informacin que describe el nivel de transaccin soportado por el DBMS.

2.1.8 Niveles de aislamiento de transacciones


Si un DBMS soporta el proceso de transacciones, tendr que manejar de alguna forma los potenciales conflictos que puedan surgir cuando dos transacciones estn operando sobre una base de datos concurrentemente. El usuario puede especificar un nivel de aislamiento para indicar que nivel de precaucin debera ejercitar el DBMS para la resolucin de estos conflictos. Por ejemplo, que ocurrir cuando una transaccin cambia un valor y una segunda transaccin lee el valor antes de que el cambio haya sido commitado o descartado?. Debera permitirse, dado que el valor cambiado ledo por la segunda transaccin ser invalido si la primera transaccin ha hecho rollback?. Un usuario JDBC puede instruir a la DBMS para que un valor que ha sido ledo antes del commit (dirty reads) con el siguiente cdigo donde con es el objeto de la actual conexin: con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED); El nivel de aislamiento ms alto, el que ms cuidado toma para evitar conflictos. La interfase Connection define cinco niveles de aislamiento con el nivel ms bajo que especifica que no soporte transacciones hasta el ms alto que especifica que mientras una transaccin est abierta ninguna otra transaccin puede realizar cambios en los datos ledos por esa transaccin. Normalmente, el nivel de transaccin ms alto es el ms lento en la ejecucin de la aplicacin.(debido a que
240

se incrementan los bloqueos y se disminuye la concurrencia de los usuarios). El desarrollador debe balancear la necesidad de rendimiento con la necesidad de la consistencia de los datos al tomar la decisin del nivel de aislamiento a usar. Por supuesto, el nivel de aislamiento que pueda soportarse depende de las posibilidades de la base de datos subyacente. Cuando se crea un nuevo objeto Connection, su nivel de aislamiento depende del driver, pero normalmente por defecto es el de la DBMS subyacente. Un usuario puede llamar al mtodo setIsolationLevel para cambiar el nivel de aislamiento de la transaccin, y este nuevo nivel estar efectivo durante la sesin de conexin. Para cambiar el nivel de aislamiento solo para una transaccin, es necesario fijar este antes de la transaccin comience y volverlo a situar en su valor anterior una vez que la transaccin haya terminado. No se recomienda cambiar el nivel de aislamiento de transaccin en medio de una puesto que lanzar una llamada inmediata al mtodo commit, provocando que los cambios hasta ese punto se hagan permanentes en la base de datos.

3. LA CLASE DriverManager
3.1 Vista preliminar
La clase DriverManager implementa la capa de gestin de JDBC, y trabaja como intermediaria entre el usuario y los drivers. Guarda la lista de los drivers que estn disponibles y establece la conexin entre la base de datos y el driver apropiado. Adems la clase DriverManager se ocupa de cosas cmo gestionar los lmites de tiempo de login en el driver y de la salida de los mensajes de traza y log. Para aplicaciones simples, el nico mtodo en esta clase que necesita un programador general para su uso directamente es DriverManager.getConnection. Como su nombre indica, este mtodo establece una conexin con la base de datos. JDBC permite al usuario llamar a los mtodos de DriverManager getDriver, getDrivers y registerDriver as como al mtodo de Driver connect, pero en la mayora de los casos es preferible dejar que la clase DriverManager gestione los detalles al establecer la conexin.

3.1.1 Mantenimiento de la lista de drivers disponibles.


La clase DriverManager mantiene una lista de clases disponibles que han sido registrados mediante el mtodo DriverManager.registerDriver. Todas las clases Driver deben escribirse con una seccin esttica que cree una instancia de la clase y luego la registre en la clase DriverManager cuando se cargue. Adems el usuario normalmente no debera llamar a DriverManager.registerDriver directamente; debera llamarse
241

automticamente por el driver cuando est se carga,. Una clase Driver se carga, y por tanto se registra, de dos formas diferentes: 1 Mediante una llamada al mtodo Class.forName. Este carga explcitamente la clase driver. Dado que no depende de ningn setup externo, esta forma de cargar drivers es la recomendada. El siguiente cdigo carga la clase acme.db.Driver:
Class.forName("acme.db.Driver");

Si acme.db.Driver se ha escrito para que al cargar produzca una instancia y llame al mtodo DriverManager.registerDriver con esta instancia como argumento (es lo que debera hacer), entonces este estar en la lista de drivers disponibles para efectuar la conexin.
2

Mediante la adicin del driver a la propiedad jdbc.drivers de java.lang.System- Esta es una lista de nombres de clases de drivers, separadas por dos puntos, que es la que carga la clase DriverManager. Cuando la clase DriverManager se inicializa, mira en la propiedad jdbc.drivers, y si el usuario ha introducido uno o ms drivers, la clase DriverManager intenta cargarlos. El siguiente cdigo ilutsra como un programador debera introducir estas tres clases en ~/.hotjava/properties
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;

La primera llamada a el mtodo DriverManager har que estos drivers se carguen automticamente. Notese que en esta segunda manera de cargar los drivers es necesario una preparacin del entorno que es persistente. Si existe alguna duda sobre esto es preferible y ms seguro llamar al mtodo Class.forName para cargar explicitamente cada driver. Este es tambin el mtodo que se usa para traer un driver particular puesto que una vez que la clase DriverManager ha sido inicializada no chequear la lista de propiedades jdbc.drivers. En ambos casos, es responsabilidad de la clase Driver recin cargada registrarse a si misma mediante una llamada a DriverManager.registerDriver. Como se ha mencionado anteriormente, esto debera hacerse automticamente al cargar la clase. Por razones de seguridad, la capa de gestin de JDBC guardar traza de que clases de cargadores provee cada driver. Entonces cuando la clase DriverManager abre una conexin solo usar los drivers que vienen desde el sistema de ficheros local o desde las mismas clases cargadoras como el cdigo que solicita la conexin.

242

3.1.2 Establecer una conexin


Una vez que la clase Driver ha sido cargada y registrada con la clase DriverManager, se est en condiciones de establecer la conexin con la base de datos. La solicitud de la conexin se realiza mediante una llamada al mtodo DriverManager.getConnection, y DriverManager testea los drivers regsitrados para ver si puede establecer la conexin. A veces puede darse el caso de que ms de un driver JDBC pueda establecer la conexin para una URL dada. Por ejemplo, cuando conectamos con una base de datos remota, podra ser posible usar un driver puente JDBC-ODBC, o un driver JDBC de protocolo genrico de red, o un driver suministrado por el vendedor. En tales casos, el orden en que los driver son testeados es significante porque DriverManager usar el primer driver que encuentre que pueda conectar con xito a la base de datos. Primero DriverManager intenta usar cada driver en el orden en que ha sido registrado ( los drivers listados en la propiedad jdbc.drivers son siempre los registrados primero). Saltar cualquier driver con cdigo untrusted, a menos que se cargue desde el mismo cdigo fuente que el cdigo que intenta abrir la conexin. Testea los drivers mediante la llamada al mtodo Driver.connect cada uno por turno, pasndole como argumento la URL que el usuario ha pasado originalmente al mtodo DriverManager.getConnection. El primer driver que reconozca la URL realiza la conexin. Una primera ojeda puede parecer insuficiente, pero son necesarias solo unas pocas llamadas a procedimientos y comparaciones de cadena por conexin puesto que no es probable que haya docenas de drivers se carguen concurrentemente. El siguiente cdigo es un ejemplo de todo lo necesario normalmente para establecer una conexin con un driver puente JDBC-ODBC:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //loads the driver String url = "jdbc:odbc:fred"; DriverManager.getConnection(url, "userID", "passwd");

4. LA CLASE Statement
4.1 Vista Preliminar
Un objeto Statement se usa para enviar sentencias SQL a la base de datos. Actualmente hay tres tipos de objetos Statement, todos los cuales actan como contenedores para la ejecucin de sentencias en una conexin dada: Statement,
243

PreparedStatement que hereda de Statement y CallableStatement que hereda de PreparedStatement. Estas estn especializadas para enviar tipos particulares de sentencias SQL, Un objeto Statement se usa para ejecutar una sentencia SQL simple sin parmetros. Un objeto PreparedStatement se usa para ejecutar sentencias SQL precompiladas con o sin parmetros IN; y un objeto CallableStatement se usa para ejecutar un procedimieno de base de datos almacenado. La interfase Statement suminstra mtodos bsicos para ejecutar sentencias y devolver resultados. La interfase PreparedStatement aade mtodos para trabajat con los parmetros IN; y la interfase CallableStatement aade mtodos para trabajar con parameters OUT.

4.1.1 Creacin de objetos Statement


Una vez establecida la conexin con una base de datos particular, esta conexin puede usarse para enviar sentencias SQL. Un objeto Statement se crea mediante el mtodo de Connection createStatement, como podemos ver en el siguiente fragmento de cdigo.
Connection con = DriverManager.getConnection(url, "sunny", ""); Statement stmt = con.createStatement();

La sentencia SQL que ser enviada a la base de datos es alimentada como un argumento a uno de los mtodos de ejecucin del objeto Statement. Por ejemplo:
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");

4.1.2 Ejecucin de sentencias usando objetos Statement.


La interfase Statement nos suministra tres mtodos diferentes para ejecutar sentencias SQL, executeQuery, executeUpdate y execute. El mtodo a usar esta determinado por el producto de la sentencia SQL El mtodo executeQuery esta diseado para sentencias que producen como resultado un nico result set tal como las sentencias SELECT. El mtodo executeUpdate se usa para ejecutar sentencias INSERT, UPDATE DELETE as como sentencias SQL DDL (Data Definition Language) como CREATE TABLE o DROP TABLE. El efecto de una sentencia INSERT, UPDATE o DELETE es una modificacin de una o ms columnas en cero o ms filas de una tabla. El valor devuelto de executeUpdate es un entero que indica el nmero de filas que han sido afectadas (referido como update count). Para sentencias tales como CREATE TABLE o DROP TABLE, que no operan sobre filas, le valor devuelto por executeUpdate es siempre cero.
244

El mtodo execute se usa para ejecutar sentencias que devuelven ms de un result set, ms que un update count o una combinacin de ambos. Como es esta una caracterstica avanzada que muchos programadores no necesitaran nunca se ver en su propia seccin. Todos los mtodos que ejecutan sentencias cierran los objetos Resultset abiertos como resultado de las llamadas a Statement. Esto quiere decir que es necesario completar el proceso con el actual objeto Resulset antes de reejecutar una sentencia Statement. Debe notarse que la interfase PreparedStatement, que hereda los mtodos de la interfase Statement, tiene sus propias versiones de los mtodos executeQuery, executeUpdate y execute. Los objetos Statement en si mismos no contienen una sentencia SQL, por tanto debe suministrarse como un argumento a los mtodos Statement.execute. Los objetos PreparedStatement no suministran una sentencia SQL como argumento a estos mtodos puesto que ya tienen la sentencia precompilada. Los objetos CallableStatement heredan las formas de estos mtodos de PreparedStatement. Usar un parametro de query con las versiones de los mtodos de PreparedStatement o CallableStatement produciri una SQLException,.

4.1.3 Realizacin de Statement


Cuando una conexin est en modo auto-commit, las sentencias ejecutadas son comitadas o rechazadas cuando se completan. Un sentencia se considera completa cuando ha sido ejecutada y se han devuelto todos los resultados. Pare el mtodo executeQuery, que devuelve un nico result set, la sentencia se completa cuando todas las filas del objeto ResultSet se han devuelto. Para el mtodo executeUpdate, un sentencia se completa cuando se ejecuta. En los raros casos en que se llama al mtodo execute, de cualquier modo, no se completa hasta que los result sets o update counts que se generan han sido devueltos. Algunos DBMS tratan cada sentencia en un procedimiento almacenado como sentencias separadas. Otros tratan el procedimiento entero como una sentencia compuesta. Esta diferencia se convierte en importante cuando est activo el modo auto-commit porque afecta cuando se llama al mtodo commit. En el primer caso, cada sentencia individual es commitada. En el segundo, se commiten todas juntas.

4.1.4 Cerrar objetos Statement.


Los objetos Statement se cerrarn automticamente por el colector de basura de Java (garbage collector). No obstante se recomienda como una buena prctica de programacin que se cierren explicitamente cuando no sean ya necesarios. Esto
245

libera recursos DBMS inmediatamente y ayuda a evitar potenciales problemas de memoria.

5.0 CONEXIONES A BASES DE DATOS CON JAVA

Seleccionar una Base de Datos


A lo largo de la seccin asumiremos que la base de datos COFFEES ya existe. (crear una base de datos no es nada dficil, pero requiere permisos especiales y normalmente lo hace un administrador de bases de datos). Para ser un poco manejable se tendr un nmero pequeo de tablas. Primero veremos como abrir una conexin con nuestro controlador de base de datos, y luego, ya que JDBC puede enviar codigo SQL a nuestro controlador, demostraremos algn cdigo SQL. Despus, veremos lo sencillo que es utilizar JDBC para pasar esas sentencias SQL a nuestro controlador de bases de datos y procesar los resultados devueltos. Este cdigo ha sido probado en la mayora de los controladores de base de datos. Sin embargo, podramos encontrar algunos problemas de compatibilidad su utilizamos antiguos drivers ODB con el puente JDBC.ODBC.

Recuperar Valores desde una Hoja de Resultados


Ahora veremos como enviar la sentencia SELECT de la pgina anterior desde un programa escrito en Java y como obtener los resultados que hemos mostrado. JDBC devuelve los resultados en un objeto ResultSet, por eso necesitamos declarar un ejemplar de la clase ResultSet para contener los resultados. El siguiente cdigo presenta el objeto ResultSet: rs y le asigna el resultado de una consulta anterior.
ResultSet rs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES");

Utilizar el Mtodo next La variable rs, que es un ejemplar de ResultSet, contiene las filas de cafs y sus precios mostrados en el juego de resultados de la pgina anterior. Para acceder a los nombres y los precios, iremos a la fila y recuperaremos los valores de acuerdo con sus tipos. El mtodo next mueve algo llamado cursor a la siguiente fila y hace que esa fila (llamada fila actual) sea con la que podamos operar. Como el cursor inicialmente se posiciona justo encima de la primera fila de un objeto ResultSet, primero debemos llamar al mtodo next para mover el cursor a la primera fila y convertirla en la fila actual. Sucesivas invocaciones del mtodo next movern el cursor de lnea en lnea de arriba a abajo. Observa que con el JDBC 2.0, cubierto en la siguiente seccin, se puede mover el cursor hacia atrs, hacia posiciones especficas y a posiciones relativas a la fila actual adems de mover el cursor hacia adelante. Utilizar los mtodos getXXX Los mtodos getXXX del tipo apropiado se utilizan para recuperar el valor de cada columna. Por ejemplo, la primera columna de cada fila de rs es COF_NAME, que almacena un valor del tipo VARCHAR de SQL. El mtodo para recuperar un valor VARCHAR es getString. La segunda columna de cada fila almacena un valor del tipo FLOAT de SQL, y el mtodo para recuperar valores de ese tipo es getFloat. El siguiente
246

cdigo accede a los valores almacenados en la fila actual de rs e imprime una lnea con el nombre seguido por tres espacios y el precio. Cada vez que se llama al mtodo next, la siguiente fila se convierte en la actual, y el bucle contina hasta que no haya ms filas en rs.
String query = "SELECT COF_NAME, PRICE FROM COFFEES"; ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String s = rs.getString("COF_NAME"); Float n = rs.getFloat("PRICE"); System.out.println(s + " " + n); }

La salida se parecer a esto.


Colombian 7.99 French_Roast 8.99 Espresso 9.99 Colombian_Decaf 8.99 French_Roast_Decaf 9.99

Veamos cmo funcionan los mtodos getXXX examinando las dos sentencias getXXX de este cdigo. Primero examinaremos getString.
String s = rs.getString("COF_NAME");

El mtodo getString es invocado sobre el objeto ResultSet: rs, por eso getString recuperar (obtendr) el valor almacenado en la columna COF_NAME de la fila actual de rs. El valor recuperado por getString se ha convertido desde un VARCHAR de SQL a un String de Java y se ha asignado al objeto String s. Observa que utilizamos la variable s en la expresin println mostrada arriba, de esta forma: println(s + " " + n) La situacin es similar con el mtodo getFloat excepto en que recupera el valor almacenado en la columna PRICE, que es un FLOAT de SQL, y lo convierte a un float de Java antes de asignarlo a la variable n. JDBC ofrece dos formas para identificar la columna de la que un mtodo getXXX obtiene un valor. Una forma es dar el nombre de la columna, como se ha hecho arriba. La segunda forma es dar el ndice de la columna (el nmero de columna), con un 1 significando la primera columna, un 2 para la segunda, etc. Si utilizramos el nmero de columna en vez del nombre de columna el cdigo anterior se podra parecer a esto.
String s = rs.getString(1); float n = rs.getFloat(2);

La primera lnea de cdigo obtiene el valor de la primera columna de la fila actual de rs (columna COF_NAME), convirtindolo a un objeto String de Java y asignndolo a s. La segunda lnea de cdigo obtiene el valor de la segunda columna de la fila actual de rs, lo convierte a un float de Java y lo asigna a n. Recuerda que el nmero de columna se refiere al nmero de columna en la hoja de resultados no en la tabla original. En suma, JDBC permite utilizar tanto el nombre cmo el nmero de la columna como argumento a un mtodo getXXX. Utilizar el nmero de columna es un poco ms eficiente, y hay algunos casos donde es necesario utilizarlo.

247

JDBC permite muchas lateralidades para utilizar los mtodos getXXX para obtener diferentes tipos de datos SQL. Por ejemplo, el mtodo getInt puede ser utilizado para recuperar cualquier tipo numrico de caracteres. Los datos recuperados sern convertidos a un int; esto es, si el tipo SQL es VARCHAR, JDBC intentar convertirlo en un entero. Se recomienda utilizar el mtodo getInt slo para recuperar INTEGER de SQL, sin embargo, no puede utilizarse con los tipos BINARY, VARBINARY, LONGVARBINARY, DATE, TIME, o TIMESTAMP de SQL. Mtodos para Recuperar Tipos SQL muestra qu mtodos pueden utilizarse legalmente para recuperar tipos SQL, y ms importante, qu mtodos estn recomendados para recuperar los distintos tipos SQL. Observa que esta tabla utiliza el trmino "JDBC type" en lugar de "SQL type." Ambos trminos se refieren a los tipos genricos de SQL definidos en java.sql.Types, y ambos son intercambiables. Utilizar el mtodo getString Aunque el metodo getString est recomendado para recuperar tipos CHAR y VARCHAR de SQL, es posible recuperar cualquier tipo bsico SQL con l. (Sin embargo, no se pueden recuperar los nuevos tipos de datoas del SQL3. Explicaremos el SQL3 ms adelante). Obtener un valor con getString puede ser muy til, pero tiene sus limitaciones. Por ejemplo, si se est utilizando para recuperar un tipo numrico, getString lo convertir en un String de Java, y el valor tendr que ser convertido de nuevo a nmero antes de poder operar con l. Utilizar los mtodos de ResultSet.getXXX para Recuperar tipos JDBC Una "x" indica que el mtodo getXXX se puede utilizar legalmente para recuperar el tipo JDBC dado. Una "X" indica que el mtodo getXXX est recomendado para recuperar el tipo JDBC dado.

Actualizar Tablas
Supongamos que despus de una primera semana exitosa, el propietario de "The Coffee Break" quiere actualizar la columna SALES de la tabla COFFEES introduciendo el nmero de libras vendidas de cada tipo de caf. La sentencia SQL para actualizar una columna se podra parecer a esto. String updateString = "UPDATE COFFEES " + "SET SALES = 75 " + "WHERE COF_NAME LIKE 'Colombian'"; Uitlizando el objeto stmt, este cdigo JDBC ejecuta la sentencia SQL contenida en updateString. stmt.executeUpdate(updateString); La tablaCOFFEES ahora se parecer a esto.

COF_NAME Colombian French_Roast Espresso Colombian_Decaf French_Roast_Decaf

SUP_ID 101 49 150 101 49


248

PRICE 7.99 8.99 9.99 8.99 9.99

SALES 75 0 0 0 0

TOTAL 0 0 0 0 0

Observa que todava no hemos actualizado la columna TOTAL, y por eso tiene valor 0. Ahora seleccionaremos la fila que hemos actualizado, recuperando los valores de las columnas COF_NAME y SALES, e imprimiendo esos valores.
String query = "SELECT COF_NAME, SALES FROM COFFEES " + "WHERE COF_NAME LIKE 'Colombian'"; ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String s = rs.getString("COF_NAME"); int n = rs.getInt("SALES"); System.out.println(n + " pounds of " + s + " sold this week.") }

Esto imprimira lo siguiente.


75 pounds of Colombian sold this week.

Cmo la clasula WHERE lmita la seleccin a una sla lnea, slo hay una lnea en la ResultSet: rs y una lnea en la salida. Por lo tanto, sera posible escribir el cdigo sin un bucle while.
rs.next(); String s = rs.getString(1); int n = rs.getInt(2); System.out.println(n + " pounds of " + s + " sold this week.")

Aunque hay una sla lnea en la hoja de resultados, necesitamos utilizar el mtodo next para acceder a ella. Un objeto ResultSet se crea con un cursor apuntando por encima de la primera fila. La primera llamada al mtodo next posiciona el cursor en la primera fila (y en este caso, la nica) de rs. En este cdigo, slo se llama una vez a next, si sucediera que existiera una lnea, nunca se accedera a ella. Ahora actualizaremos la columna TOTAL aadiendo la cantidad vendida durante la semana a la cantidad total existente, y luego imprimiremos el nmero de libras vendidas hasta la fecha.
String updateString = "UPDATE COFFEES " + "SET TOTAL = TOTAL + 75 " + "WHERE COF_NAME LIKE 'Colombian'"; stmt.executeUpdate(updateString); String query = "SELECT COF_NAME, TOTAL FROM COFFEES " + "WHERE COF_NAME LIKE 'Colombian'"; ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String s = rs.getString(1); int n = rs.getInt(2); System.out.println(n + " pounds of " + s + " sold to date.") }

Observa que en este ejemplo, utilizamos el ndice de columna en vez del nombre de columna, suministrando el ndice 1 a getString (la primera columna de la hoja de resultados es COF_NAME), y el ndice 2 a getInt (la segunda columna de la hoja de resultados es TOTAL). Es importante distinguir entre un ndice de columna en la tabla de la base de datos como opuesto al ndice en la tabla de la hoja de resultados. Por ejemplo, TOTAL es la quinta columna en la tabla COFFEES pero es la segunda columna en la hoja de resultados generada por la peticin del ejemplo anterior.
249

Utilizar Sentencias Preparadas


Algunas veces es ms conveniente o eficiente utilizar objetos PreparedStatement para enviar sentencias SQL a la base de datos. Este tipo especial de sentencias se deriva de una clase ms general, Statement, que ya conocemos. Cundo utilizar un Objeto PreparedStatement Si queremos ejecutar muchas veces un objeto Statement, reduciremos el tiempo de ejecucin si utilizamos un objeto PreparedStatement, en su lugar. La caractersitca principal de un objeto PreparedStatement es que, al contrario que un objeto Statement, se le entrega una sentencia SQL cuando se crea. La ventaja de esto es que en la mayora de los casos, esta sentencia SQL se enviar al controlador de la base de datos inmediatamente, donde ser compilado. Como resultado, el objeto PreparedStatement no slo contiene una sentencia SQL, sino una sentencia SQL que ha sido precompilada. Esto significa que cuando se ejecuta la PreparedStatement, el controlador de base de datos puede ejecutarla sin tener que compilarla primero. Aunque los objetos PreparedStatement se pueden utilizar con sentencias SQL sin parmetros, probablemente nosotros utilizaremos ms frecuentemente sentencias con parmetros. La ventajA de utilizar sentencias SQL que utilizan parmetros es que podemos utilizar la misma sentencia y suministrar distintos valores cada vez que la ejecutemos. Veremos un ejemplo de esto en las pgina siguientes. Crear un Objeto PreparedStatement Al igual que los objetos Statement, creamos un objeto PreparedStatement con un objeto Connection. Utilizando nuestra conexin con abierta en ejemplos anteriores, podramos escribir lo siguiente para crear un objeto PreparedStatement que tome dos parmetros de entrada.
PreparedStatement updateSales = con.prepareStatement( "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");

La variable updateSales contiene la sentencia SQL, "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?", que tambin ha sido, en la mayora de los casos, enviada al controlador de la base de datos, y ha sido precompilado. Suministrar Valores para los Parmetros de un PreparedStatement Necesitamos suministrar los valores que se utilizarn en los luegares donde estn las marcas de interrogacin, si hay alguno, antes de ejecutar un objeto PreparedStatement. Podemos hacer esto llamado a uno de los mtodos setXXX definidos en la clase PreparedStatement. Si el valor que queremos sustituir por una marca de interrogacin es un int de Java, podemos llamar al mtodo setInt. Si el valor que queremos sustituir es un String de Java, podemos llamar al mtodo setString, etc. En general, hay un mtodo setXXX para cada tipo Java. Utilizando el objeto updateSales del ejemplo anterior, la siguiente lnea de cdigo selecciona la primera marca de interrogacin para un int de Java, con un valor de 75.
updateSales.setInt(1, 75);

250

Cmo podramos asumir a partir de este ejemplo, el primer argumento de un mtodo setXXX indica la marca de interrogacin que queremos seleccionar, y el segundo argumento el valor que queremos ponerle. El siguiente ejemplo selecciona la segunda marca de interrogacin con el string "Colombian".
updateSales.setString(2, "Colombian");

Despus de que estos valores hayan sido asignados para sus dos parmetros, la sentencia SQL de updateSales ser equivalente a la sentencia SQL que hay en string updateString que utilizando en el ejemplo anterior. Por lo tanto, los dos fragmentos de cdigo siguientes consiguen la misma cosa. Cdigo 1.
String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE 'Colombian'"; stmt.executeUpdate(updateString);

Cdigo 2.
PreparedStatement updateSales = con.prepareStatement( "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? "); updateSales.setInt(1, 75); updateSales.setString(2, "Colombian"); updateSales.executeUpdate().

Utilizamos el mtodo executeUpdate para ejecutar ambas sentencias stmt updateSales. Observa, sin embargo, que no se suministran argumentos a executeUpdate cuando se utiliza para ejecutar updateSales. Esto es cierto porque updateSales ya contiene la sentencia SQL a ejecutar. Mirando esto ejemplos podramos preguntarnos por qu utilizar un objeto PreparedStatement con parmetros en vez de una simple sentencia, ya que la sentencia simple implica menos pasos. Si actualizramos la columna SALES slo una o dos veces, no sera necesario utilizar una sentencia SQL con parmetros. Si por otro lado, tuvieramos que actualizarla frecuentemente, podra ser ms fcil utilizar un objeto PreparedStatement, especialmente en situaciones cuando la utilizamos con un bucle while para seleccionar un parmetro a una sucesin de valores. Veremos este ejemplo ms adelante en esta seccin. Una vez que a un parmetro se ha asignado un valor, el valor permanece hasta que lo resetee otro valor o se llame al mtodo clearParameters. Utilizando el objeto PreparedStatement: updateSales, el siguiente fragmento de cdigo reutiliza una sentencia prepared despus de resetar el valor de uno de sus parmetros, dejando el otro igual.
updateSales.setInt(1, 100); updateSales.setString(2, "French_Roast"); updateSales.executeUpdate(); // changes SALES column of French Roast row to 100 updateSales.setString(2, "Espresso"); updateSales.executeUpdate(); // changes SALES column of Espresso row to 100 (the first // parameter stayed 100, and the second parameter was reset // to "Espresso")

251

Utilizar una Bucle para asignar Valores Normalmente se codifica ms sencillo utilizando un bucle for o while para asignar valores de los parmetros de entrada. El siguiente fragmento de cdigo demuestra la utilizacin de un bucle for para asignar los parmetros en un objeto PreparedStatement: updateSales. El array salesForWeek contiene las cantidades vendidas semanalmente. Estas cantidades corresponden con los nombres de los cafs listados en el array coffees, por eso la primera cantidad de salesForWeek (175) se aplica al primer nombre de caf de coffees ("Colombian"), la segunda cantidad de salesForWeek (150) se aplica al segundo nombre de caf en coffees ("French_Roast"), etc. Este fragmento de cdigo demuestra la actualizacin de la columna SALES para todos los cafs de la tabla COFFEES PreparedStatement updateSales; String updateString = "update COFFEES " + "set SALES = ? where COF_NAME like ?"; updateSales = con.prepareStatement(updateString);int [] salesForWeek = {175, 150, 60, 155, 90}; String [] coffees = {"Colombian", "French_Roast", "Espresso", "Colombian_Decaf", "French_Roast_Decaf"}; int len = coffees.length; for(int i = 0; i < len; i++) { updateSales.setInt(1, salesForWeek[i]); updateSales.setString(2, coffees[i]); updateSales.executeUpdate(); }

Cuando el propietario quiera actualizar las ventas de la semana siguiente, puede utilizar el mismo cdigo como una plantilla. Todo lo que tiene que haces es introducir las nuevas cantidades en el orden apropiado en el array salesForWeek. Los nombres de cafs del array coffees permanecen constantes, por eso no necesitan cambiarse. (En una aplicacin real, los valores probablemente seran introducidos por el usuario en vez de desde un array inicializado). Valores de retorno del mtodo executeUpdate Siempre que executeQuery devuelve un objeto ResultSet que contiene los resultados de una peticin al controlador de la base datos, el valor devuelto por executeUpdate es un int que indica cuntas lneas de la tabla fueron actualizadas. Por ejemplo, el siguiente cdigo muestra el valor de retorno de executeUpdate asignado a la variable n.
updateSales.setInt(1, 50); updateSales.setString(2, "Espresso"); int n = updateSales.executeUpdate(); // n = 1 because one row had a change in it

La tabla COFFEES se ha actualziado poniendo el valor 50 en la columna SALES de la fila correspondiente a Espresso. La actualizacin afecta slo a una lnea de la tabla, por eso n es igual a 1. Cuando el mtodo executeUpdate es utilizado para ejecutar una sentecia DDL, como la creacin de una tabla, devuelve el int: 0. Consecuentemente, en el siguiente fragmento de cdigo, que ejecuta la sentencia DDL utilizada pra crear la tabla COFFEES, n tendr el valor 0.
int n = executeUpdate(createTableCoffees); // n = 0

252

Observa que cuando el valor devuelto por executeUpdate sea 0, puede significar dos cosas: (1) la sentencia ejecutada no ha actualizado ninguna fila, o (2) la sentencia ejecutada fue una sentencia DDL.

Utilizar Uniones
Algunas veces necesitamos utilizar una o ms tablas para obtener los datos que queremos. Por ejemplo, supongamos que el propietario del "The Coffee Break" quiere una lista de los cafs que le compra a Acme, Inc. Esto implica informacin de la tabla COFFEES y tambin de la que vamos a crear SUPPLIERS. Este es el caso en que se necesitan los "joins" (unin). Una unin es una operacin de base de datos que relaciona dos o ms tablas por medio de los valores que comparten. En nuestro ejemplo, las tablas COFFEES y SUPPLIERS tienen la columna SUP_ID, que puede ser utilizada para unirlas. Antes de ir ms all, necesitamos crear la tabla SUPPLIERS y rellenarla con valores. El siguiente cdigo crea la tabla SUPPLIERS.
String createSUPPLIERS = "create table SUPPLIERS " + "(SUP_ID INTEGER, SUP_NAME VARCHAR(40), " + "STREET VARCHAR(40), CITY VARCHAR(20), " + "STATE CHAR(2), ZIP CHAR(5))"; stmt.executeUpdate(createSUPPLIERS);

El siguiente cdigo inserta filas para tres suministradores dentro de SUPPLIERS.


stmt.executeUpdate("insert into SUPPLIERS values (101, " + "'Acme, Inc.', '99 Market Street', 'Groundsville', " + "'CA', '95199'"); stmt.executeUpdate("Insert into SUPPLIERS values (49," + "'Superior Coffee', '1 Party Place', 'Mendocino', 'CA', " + "'95460'"); stmt.executeUpdate("Insert into SUPPLIERS values (150, " + "'The High Ground', '100 Coffee Lane', 'Meadows', 'CA', " + "'93966'");

El siguiente cdigo selecciona la tabla y nos permite verla.


ResultSet rs = stmt.executeQuery("select * from SUPPLIERS");

Ahora que tenemos las tablas COFFEES y SUPPLIERS, podremos proceder con el escenario en que el propietario quera una lista de los cafs comprados a un suministrador particular. Los nombres de los suminstradores estn en la tabla SUPPLIERS, y los nombres de los cafs en la tabla COFFEES. Como ambas tablas tienen la columna SUP_ID, podemos utilizar esta columna en una unin. Lo siguiente que necesitamos es la forma de distinguir la columna SUP_ID a la que nos referimos. Esto se hace precediendo el nombre de la columna con el nombre de la tabla, "COFFEES.SUP_ID" para indicar que queremos referirnos a la columna SUP_ID de la tabla COFFEES. En el siguiente cdigo, donde stmt es un objeto Statement, seleccionamos los cafs comprados a Acme, Inc..
String query = " SELECT COFFEES.COF_NAME " + "FROM COFFEES, SUPPLIERS " + "WHERE SUPPLIERS.SUP_NAME LIKE 'Acme, Inc.'" + "and SUPPLIERS.SUP_ID = COFFEES.SUP_ID"; ResultSet rs = stmt.executeQuery(query); System.out.println("Coffees bought from Acme, Inc.: "); while (rs.next()) { String coffeeName = getString("COF_NAME"); System.out.println(" " + coffeeName); }
253

Esto producir la siguiente salida.


Coffees bought from Acme, Inc.. Colombian Colombian_Decaf

Crear Aplicaciones JDBC Completas


Hasta ahora slo hemos visto fragmentos de cdigo. Ms adelante veremos programas de ejemplo que son aplicaciones completas que podremos ejecutar. El primer cdigo de ejemplo crea la tabla COFFEES; el segundo inserta valores en la tabla e imprime los resultados de una peticin. La terecera aplicacin crea la tabla SUPPLIERS, y el cuarto la rellena con valores. Despus de haber ejecutado este cdigo, podemos intentar una peticin que una las tablas COFFEES y SUPPLIERS, como en el quinto cdigo de ejemplo. El sexto ejemplo de cdigo es una aplicacin que demuestra una transacin y tambin muestra como configurar las posiciones de los parmetros en un objeto PreparedStatement utilizando un bucle for. Como son aplicaciones completas, incluyen algunos elementos del lenguaje Java que no hemos visto en los fragmentos anteriores. Aqu explicaremos estos elementos brevemente. Poner Cdigo en una Definicin de Clase En el lenguaje Java, cualquier cdigo que querramos ejecutar debe estar dentro de una definicin de clase. Tecleamos la definicin de clase en un fichero y a ste le damos el nombre de la clase con la extensin .java. Por eso si tenemos una clase llamada MySQLStatement, su definicin debera estar en un fichero llamado MySQLStatement.java Importar Clases para Hacerlas Visibles Lo primero es importar los paquetes o clases que se van a utilizar en la nueva clase. Todas las clases de nuestros ejemplos utilizan el paquete java.sql (el API JDBC), que se hace visible cuando la siguiente lnea de cdigo precede a la definicin de clase.
import java.sql.*;

El asterisco (*) indica que todas las clases del paquete java.sql sern importadas. Importar una clase la hace visible y significa que no tendremos que escribir su nombre totalmente cualificado cuando utilicemos un mtodo o un campo de esa clase. Si no incluimos "import java.sql.*;" en nuestro cdigo, tendramos que escribir "java.sql." ms el nombre de la clase delante de todos los campos o mtodos JDBC que utilicemos cada vez que los utilicemos. Observa que tambin podemos importar clases individuales selectivamente en vez de importar un paquete completo. Java no requiere que importemos clases o paquetes, pero al hacerlo el cdigo se hace mucho ms conveniente. Cualquier lnea que importe clases aparece en la parte superior de los ejemplos de cdigo, que es donde deben estar para hacer visibles las clases importadas a la clase que est siendo definida. La definicin real de la clase sigue a cualquier lnea que importe clases. Utilizar el Mtodo main() Si una clase se va a ejecutar, debe contener un mtodo static public main. Este mtodo viene justo despus de la lnea que declara la clase y llama a los otros mtodos de la clase.
254

La palabra clave static indica que este mtodo opera a nivel de clase en vez sobre ejemplares individuales de la clase. La palabra clave public significa que los miembros de cualquier clase pueden acceder a este mtodo. Como no estamos definiendo clases slo para ser ejecutadas por otras clases sino que queremos ejecutarlas, las aplicaciones de ejemplo de este captulo incluyen un mtodo main. Utilizar bloques try y catch Algo que tambin incluyen todas las aplicaciones de ejemplo son los bloques try y catch. Este es un mecanismo del lenguaje Java para manejar excepciones. Java requiere que cuando un mtodo lanza un excepcin exista un mecanismo que la maneje. Generalmente un bloque catch capturar la excepcin y especificar lo que suceder (que podra ser no hacer nada). En el cdigo de ejemplo, utilizamos dos bloques try y dos bloques catch. El primer bloque try contiene el mtodo Class.forName, del paquete java.lang. Este mtodo lanza una ClassNotFoundException, por eso el bloque catch que le sigue maneja esa excepcin. El segundo bloque try contiene mtodos JDBC, todos ellos lanzan SQLException, por eso el bloque catch del final de la aplicacin puede manejar el resto de las excepciones que podran lanzarse ya que todas seran objetos SQLException. Recuperar Excepciones JDBC permite ver los avisos y excepciones generados por nuestro controlador de base de datos y por el compilador Java. Para ver las excepciones, podemos tener un bloque catch que las imprima. Por ejemplo, los dos bloques catch del siguiente cdigo de ejemplo imprimen un mensaje explicando la excepcin.
try { // Aqu va el cdigo que podra generar la excepcin. // Si se genera una excepcin, el bloque catch imprimir // informacin sobre ella. } catch(SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } try { Class.forName("myDriverClassName"); } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); }

Si ejecutarmos CreateCOFFEES.java dos veces, obtendramos un mensaje de error similar a ste.


SQLException: There is already an object named 'COFFEES' in the database. Severity 16, State 1, Line 1

Este ejemplo ilustra la impresin del componente mensaje de un objeto SQLException, lo que es suficiente para la mayora de las situaciones. Sin embargo, realmente existen tres componentes, y para ser completos, podemos imprimirlos todos. El siguiente fragmento de cdigo muestra un bloque catch que se ha completado de dos formas. Primero, imprime las tres partes de un objeto SQLException: el
255

mensaje (un string que describe el error), el SQLState (un string que identifica el error de acuerdo a los convenciones X/Open de SQLState), y un cdigo de error del vendedor (un nmero que es el cdigo de error del vendedor del driver). El objeto SQLException, ex es capturado y se accede a sus tres componentes con los mtodos getMessage, getSQLState, y getErrorCode. La segunda forma del siguiente bloque catch completo obtiene todas las exepciones que podran haber sido lanzada. Si hay una segunda excepcin, sera encadenada a ex, por eso se llama a ex.getNextException para ver si hay ms excepciones. Si las hay, el bucle while contina e imprime el mensaje de la siguiente excecpcin, el SQLState, y el cdigo de error del vendedor. Esto contina hasta que no haya ms excepciones.
try { // Aqu va el cdigo que podra generar la excepcin. // Si se genera una excepcin, el bloque catch imprimir // informacin sobre ella. } catch(SQLException ex) { System.out.println("\n--- SQLException caught ---\n"); while (ex != null) { System.out.println("Message: " + ex.getMessage ()); System.out.println("SQLState: " + ex.getSQLState ()); System.out.println("ErrorCode: " + ex.getErrorCode ()); ex = ex.getNextException(); System.out.println(""); } }

Si hubieramos sustituido el bloque catch anterior en el Cdigo de ejemplo 1 (CreateCoffees) y lo hubieramos ejecutado despus de que la tabla COFFEES ya se hubiera creado, obtendramos la siguiente informacin.
--- SQLException caught --Message: There is already an object named 'COFFEES' in the database. Severity 16, State 1, Line 1 SQLState: 42501 ErrorCode: 2714

SQLState es un cdigo definido en X/Open y ANSI-92 que identifica la excepcin. Aqu podemos ver dos ejemplos de cdigos SQLState.
08001 -- No suitable driver HY011 -- Operation invalid at this time

El cdigo de error del vendedor es especfico de cada driver, por lo que debemos revisar la documentacin del driver buscando una lista con el significado de estos cdigos de error. Recuperar Avisos Los objetos SQLWarning son una subclase de SQLException que trata los avisos de accesos a bases de datos. Los Avisos no detienen la ejecucin de una aplicacin, como las excepciones; simplemente alertan al usuario de que algo no ha salido como se esperaba. Por ejemplo, un aviso podra hacernos saber que un privilegio que queriamos revocar no ha fue revocado. O un aviso podra decirnos que ha ocurrido algn error durante una peticin de desconexin. Un aviso puede reportarse sobre un objeto Connection, un objeto Statement (incluyendo objetios PreparedStatement y CallableStatement), o un objeto ResultSet. Cada una de
256

esas clases tiene un mtodo getWarnings, al que debemos llamar para ver el primer aviso reportado en la llamada al objeto. Si getWarnings devuelve un aviso, podemos llamar al mtodo getNextWarning de SQLWarning para obtener avisos adicionales. Al ejecutar una sentencia se borran automticamente los avisos de la sentencia anterior, por eso no se apilan. Sin embargo, esto significa que si queremos recuperar los avisos reportados por una sentencia, debemos hacerlo antes de ejecutar otra sentencia. El siguiente fragmento de cdigo ilustra como obtener informacin completa sobre los avisos reportados por el objeto Statement, stmt y tambin por el objeto ResultSet, rs.
Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("select COF_NAME from COFFEES"); while (rs.next()) { String coffeeName = rs.getString("COF_NAME"); System.out.println("Coffees available at the Coffee Break: "); System.out.println(" " + coffeeName); SQLWarning warning = stmt.getWarnings(); if (warning != null) { System.out.println("\n---Warning---\n"); while (warning != null) { System.out.println("Message: " + warning.getMessage()); System.out.println("SQLState: " + warning.getSQLState()); System.out.print("Vendor error code: "); System.out.println(warning.getErrorCode()); System.out.println(""); warning = warning.getNextWarning(); } } SQLWarning warn = rs.getWarnings(); if (warn != null) { System.out.println("\n---Warning---\n"); while (warn != null) { System.out.println("Message: " + warn.getMessage()); System.out.println("SQLState: " + warn.getSQLState()); System.out.print("Vendor error code: "); System.out.println(warn.getErrorCode()); System.out.println(""); warn = warn.getNextWarning(); } } }

Los avisos no son muy comunes, De aquellos que son reportados, el aviso ms comn es un DataTruncation, una subclase de SQLWarning. Todos los objetos DataTruncation tienen un SQLState 01004, indicando que ha habido un problema al leer o escribir datos. Los mtodos de DataTruncation permiten encontrar en que columna o parmetro se truncaron los datos, si la ruptura se produjo en una operacin de lectura o de escritura, cuntos bytes deberan haber sido transmitidos, y cuntos bytes se transmitieron realmente.

El API de JDBC 2..0


El paquete java.sql que est incluido en la versin JDK 1.2 (conocido como el API JDBC 2.0) incluye muchas nuevas caractersticas no incluidas en el paquete java.sql que forma parte de la versin JDK 1.1 (referenciado como el API JDBC 1.0).
257

Con el API JDBC 2.0, podremos hacer las siguientes cosas: Ir hacia adelante o hacia atrs en una hoja de resultados o movernos a un fila especfica. Hacer actualizaciones de las tablas de la base datos utilizando mtodos Java en lugar de utilizar comandos SQL. Enviar mltiples secuencias SQL a la base de datos como una unidad, o batch. Uitlizar los nuevos tipos de datos SQL3 como valores de columnas.

Inicializacin para Utilizar JDBC 2.0


Si queremos ejecutar cdigo que emplee alguna de las caractersticas del JDBC 2.0, necesitamos hacer lo siguiente. 1. Descargar el JDK 1.2 siguiendo las instrucciones de descarga 2. Instalar un Driver JDBC que implemente las caractersticas del JDBC 2.0 utilizadas en el cdigo. 3. Acceder a un controlador de base de datos que implemente las caractersticas del JDBC utilizadas en el cdigo. En el momento de escribir esto, no se haba implementado ningn driver que soportara las nuevas caractersticas, pero hay muchos en desarrollo. Como consecuencia, no es posible probar el cdigo de desmostracin de las caractersticas del JDBC 2.0. Podemos aprender de los ejemplos, pero no se asegura que stos funcionen.

Mover el Cursor por una Hoja de Resultados


Una de las nuevas caractersticas del API JDBC 2.0 es la habilidad de mover el cursor en una hoja de resultados tanto hacia atrs como hacia adelante. Tambin hay mtodos que nos permiten mover el cursor a una fila particular y comprobar la posicin del cursor. La hoja de resultados Scrollable hace posible crear una herramienta GUI (Interface Grfico de Usuario) para navegar a travs de ella, lo que probablemente ser uno de los principales usos de esta caracterstica. Otro uso ser movernos a una fila para actualizarla. Antes de poder aprovechar estas ventajas, necesitamos crear un objeto ResultSet Scrollable.
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES");

Este cdigo es similar al utilizado anteriormente, excepto en que aade dos argumentos al mtodo createStatement. El primer argumento es una de las tres constantes aadidas al API ResultSet para indicar el tipo de un objeto ResultSet: TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE, y TYPE_SCROLL_SENSITIVE. El segundo argumento es una de las dos constantes de ResultSet para especificar si la hoja de resultados es de slo lectura o actualizable:CONCUR_READ_ONLY y CONCUR_UPDATABLE. Lo que debemos recordar aqu es que si especificamos un tipo, tambin debemos especificar si es de slo lectura o actualizable. Tambin, debemos especificar primero el tipo, y como ambos parmetros son int, el compilador no comprobar si los hemos intercambiado.
258

Especificando la constante TYPE_FORWARD_ONLY se crea una hoja de resultados no desplazable, es decir, una hoja en la que el cursor slo se mueve hacia adelante. Si no se especifican constantes para el tipo y actualizacin de un objeto ResultSet, obtendremos automticamente una TYPE_FORWARD_ONLY y CONCUR_READ_ONLY (exactamente igual que en el API del JDBC 1.0). Obtendremos un objeto ResultSet desplazable si utilizamos una de estas constantes:TYPE_SCROLL_INSENSITIVE o TYPE_SCROLL_SENSITIVE. La diferencia entre estas dos es si la hoja de resultados refleja los cambios que se han hecho mientras estaba abierta y si se puede llamar a ciertos mtodos para detectar estos cambios. Generalmente hablando, una hoja de resultados TYPE_SCROLL_INSENSITIVE no refleja los cambios hechos mientras estaba abierta y en una hoja TYPE_SCROLL_SENSITIVE si se reflejan. Los tres tipos de hojas de resultados harn visibles los resultados si se cierran y se vuelve a abrir. En este momento, no necesitamos preocuparnos de los puntos delicados de las capacidades de un objeto ResultSet, entraremos en ms detalle ms adelante. Aunque deberamos tener en mente el hecho de que no importa el tipo de hoja de resultados que especifiquemos, siempre estaremos limitados por nuestro controlador de base de datos y el driver utilizados. Una vez que tengamos un objeto ResultSet desplazable, srs en el ejemplo anterior, podemos utilizarlo para mover el cursor sobre la hoja de resultados. Recuerda que cuando creabamos un objeto ResultSet anteriormente, tena el cursor posicionado antes de la primera fila. Incluso aunque una hoja de resultados se seleccione desplazable, el cursor tambin se posiciona inicialmente delante de la primera fila. En el API JDBC 1.0, la nica forma de mover el cursor era llamar al mtodo next. Este mtodo todava es apropiado si queremos acceder a las filas una a una, yendo de la primera fila a la ltima, pero ahora tenemos muchas ms formas para mover el cursor. La contrapartida del mtodo next, que mueve el cursor una fila hacia delante (hacia el final de la hoja de resultados), es el nuevo mtodo previous, que mueve el cursor una fila hacia atrs (hacia el inicio de la hoja de resultados). Ambos mtodos devuelven false cuando el cursor se sale de la hoja de resultados (posicin antes de la primera o despus de la ltima fila), lo que hace posible utilizarlos en un bucle while. Ye hemos utilizado un mtodo next en un bucle while, pero para refrescar la memoria, aqu tenemos un ejemplo que mueve el cursor a la primera fila y luego a la siguiente cada vez que pasa por el bucle while. El bucle termina cuando alcanza la ltima fila, haciendo que el mtodo next devuelva false. El siguiente fragmento de cdigo imprime los valores de cada fila de srs, con cinco espacios en blanco entre el nombre y el precio.
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES"); while (srs.next()) { String name = srs.getString("COF_NAME"); float price = srs.getFloat("PRICE"); System.out.println(name + " " + price); }

La salida se podra parecer a esto.


Colombian 7.99 French_Roast 8.99
259

Espresso 9.99 Colombian_Decaf 8.99 French_Roast_Decaf 9.99

Al igual que en el fragmento anterior, podemos procesar todas las filas de srs hacia atrs, pero para hacer esto, el cursor debe estar detrs de la ltima fila. Se puede mover el cursor explcitamente a esa posicn con el mtodo afterLast. Luego el mtodo previous mueve el cursor desde la posicn detrs de la ltima fila a la ltima fila, y luego a la fila anterior en cada iteraccin del bucle while. El bucle termina cuando el cursor alcanza la posicin anterior a la primera fila, cuando el mtodo previous devuelve false.
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES"); srs.afterLast(); while (srs.previous()) { String name = srs.getString("COF_NAME"); float price = srs.getFloat("PRICE"); System.out.println(name + " " + price); }

La salida se podra parecer a esto.


French_Roast_Decaf 9.99 Colombian_Decaf 8.99 Espresso 9.99 French_Roast 8.99 Colombian 7.99

Como se puede ver, las dos salidas tienen los mismos valores, pero las filas estn en orden inverso. Se puede mover el cursor a una fila particular en un objeto ResultSet. Los mtodos first, last, beforeFirst, y afterLast mueven el cursor a la fila indicada en sus nombres. El mtodo absolute mover el cursor al nmero de fila indicado en su argumento. Si el nmero es positivo, el cursor se mueve al nmero dado desde el principio, por eso llamar a absolute(1) pone el cursor en la primera fila. Si el nmero es negativo, mueve el cursor al nmero dado desde el final, por eso llamar a absolute(-1) pone el cursor en la ltima fila. La siguiente lnea de cdigo mueve el cursor a la cuarta fila de srs.
srs.absolute(4);

Si srs tuviera 500 filas, la siguiente lnea de cdigo movera el cursor a la fila 497.
srs.absolute(-4);

Tres mtodos mueven el cursor a una posicin relativa a su posicin actual. Como hemos podido ver, el mtodo next mueve el cursor a la fila siguiente, y el mtodo previous lo mueve a la fila anterior. Con el mtodo relative, se puede especificar cuntas filas se mover desde la fila actual y tambin la direccin en la que se mover. Un nmero positivo mueve el cursor hacia adelante el nmero de filas dado; un nmero negativo mueve el cursor hacia atrs el nmero de filas dado. Por ejemplo, en el siguiente fragmente de cdigo, el cursor se mueve a la cuarta fila, luego a la primera y por ltimo a la tercera.
srs.absolute(4); // cursor est en la cuarta fila . . . srs.relative(-3); // cursor est en la primera fila . . . srs.relative(2); // cursor est en la tercera fila
260

El mtodo getRow permite comprobar el nmero de fila donde est el cursor. Por ejemplo, se puede utilizar getRow para verificar la posicin actual del cursor en el ejemplo anterior.
srs.absolute(4); int rowNum = srs.getRow(); // rowNum debera ser 4 srs.relative(-3); int rowNum = srs.getRow(); // rowNum debera ser 1 srs.relative(2); int rowNum = srs.getRow(); // rowNum debera ser 3

Existen cuatro mtodos adicionales que permiten verificar si el cursor se encuentra en una posicin particular. La posicin se indica en sus nombres:isFirst, isLast, isBeforeFirst, isAfterLast. Todos estos mtodos devuelven un boolean y por lo tanto pueden ser utilizados en una sentencia condicional. Por ejemplo, el siguiente fragmento de cdigo comprueba si el cursor est despus de la ltima fila antes de llamar al mtodo previous en un bucle while. Si el mtodo isAfterLast devuelve false, el cursor no estar despus de la ltima fila, por eso se llama al mtodo afterLast. Esto garantiza que el cursor estra despus de la ltima fila antes de utilizar el mtodo previous en el bucle while para cubrir todas las filas de srs.
if (srs.isAfterLast() == false) { srs.afterLast(); } while (srs.previous()) { String name = srs.getString("COF_NAME"); float price = srs.getFloat("PRICE"); System.out.println(name + " " + price); }

En la siguiente pgina, veremos cmo utilizar otros dos mtodos de ResultSet para mover el cursor, moveToInsertRow y moveToCurrentRow. Tambin veremos ejemplos que ilustran por qu podramos querer mover el cursor a ciertas posiciones.

Hacer Actualizaciones en una Hoja de Resultados


Otra nueva caracterstica del API JDBC 2.0 es la habilidad de actualizar filas en una hoja de resultados utilizando mtodos Java en vez de tener que enviar comandos SQL. Pero antes de poder aprovechar esta capacidad, necesitamos crear un objeto ResultSet actualizable. Para hacer esto, suministramos la constante CONCUR_UPDATABLE de ResulSet al mtodo createStatement, como se ha visto en ejemplos anteriores. El objeto Statement creado producir un objeto ResultSet actualizable cada vez que se ejecute una peticin. El siguiente fragmento de cdigo ilustra la creaccin de un objeto ResultSet actualizable, uprs. Observa que el cdigo tambin lo hace desplazable. Un objeto ResultSet actualizable no tiene porque ser desplazable, pero cuando se hacen cambios en una hoja de resultados, generalmente queremos poder movernos por ella. Con una hoja de resultados desplazable, podemos movernos a las filas que queremos cambiar, y si el tipo es TYPE_SCROLL_SENSITIVE, podemos obtener el nuevo valor de una fila despus de haberlo cambiado.
Connection con = DriverManager.getConnection("jdbc:mySubprotocol:mySubName"); Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
261

ResultSet uprs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES");

El objeto ResultSet, uprs resultante se podra parecer a esto.


COF_NAME PRICE ------------------ ----Colombian 7.99 French_Roast 8.99 Espresso 9.99 Colombian_Decaf 8.99 French_Roast_Decaf 9.99

Podemos utilizar los nuevos mtodos del JDBC 2.0 en el interface ResultSet para insertar una nueva fila en uprs, borrar una fila de uprs, o modificar un valor de una columna de uprs.

Actualizar una Hoja de Resultados Programticamente


Una actualizacin es la modificacin del valor de una columna de la fila actual. Supongamos que queremos aumentar el precio del caf "French Roast Decaf" a 10.99. utilizando el API JDBC 1.0, la actualizacin podra ser algo como esto.
stmt.executeUpdate("UPDATE COFFEES SET PRICE = 10.99" + "WHERE COF_NAME = FRENCH_ROAST_DECAF");

El siguiente fragmento de cdigo muesta otra forma de realizar la actualizacin, esta vez utilizando el API JDBC 2.0.
uprs.last(); uprs.updateFloat("PRICE", 10.99);

Las operaciones de actualizacin en el API JDBC 2.0 afectan a los valores de columna de la fila en la que se encuentra el cursor, por eso en le primera lnea se llama al mtodo last para mover el cursor a la ltima fila (la fila donde la columna COF_NAME tiene el valor FRENCH_ROAST_DECAF). Una vez situado el cursor, todos los mtodos de actualizacin que llamemos operarn sobre esa fila hasta que movamos el cursor a otra fila. La segunda lnea de cdigo cambia el valor de la columna PRICE a 10.99 llamando al mtodo updateFloat. Se utiliza este mtodo porque el valor de la columna que queremos actualizar es un float Java. Los mtodos updateXXX de ResultSet toman dos parmetros: la columna a actualizar y el nuevo valor a colocar en ella. Al igual que en los mtodos getXXX de ResultSet., el parmetro que designa la columna podra ser el nombre de la columna o el nmero de la columna. Existe un mtodo updateXXX diferente para cada tipo (updateString, updateBigDecimal, updateInt, etc.) En este punto, el precio en uprs para "French Roast Decaf" ser 10.99, pero el precio en la tabla COFFEES de la base de datos ser todava 9.99. Para que la actualizacin tenga efecto en la base de datos y no slo en la hoja de resultados, debemos llamar al mtodo updateRow de ResultSet. Aqu est el cdigo para actualizar tanto uprs como COFFEES.
uprs.last(); uprs.updateFloat("PRICE", 10.99); uprs.updateRow();

262

Si hubiramos movido el cursor a una fila diferente antes de llamar al mtodo updateRow, la actualizacin se habra perdido. Si, por el contrario, nos damos cuenta de que el precio debera haber sido 10.79 en vez de 10.99 podramos haber cancelado la actualizacin llamando al mtodo cancelRowUpdates. Tenemos que llamar al mtodo cancelRowUpdates antes de llamar al mtodo updateRow; una vez que se llama a updateRow, llamar a cancelRowUpdates no har nada. Observa que cancelRowUpdates cancela todas las actualizaciones en una fila, por eso, si haba muchas llamadas a mtodo updateXXX en la misma fila, no podemos cancelar slo una de ellas. El siguiente fragmento de cdigo primero cancela el precio 10.99 y luego lo actualiza a 10.79.
uprs.last(); uprs.updateFloat("PRICE", 10.99); uprs.cancelRowUpdates(); uprs.updateFloat("PRICE", 10.79); uprs.updateRow();

En este ejemplo, slo se haba actualizado una columna, pero podemos llamar a un mtodo updateXXX apropiado para cada una de las columnas de la fila. El concepto a recordar es que las actualizaciones y las operaciones relacionadas se aplican sobre la fila en la que se encuentra el cursor. Incluso si hay muchas llamadas a mtodos updateXXX, slo se hace una llamada al mtodo updateRow para actualizar la base de datos con todos los cambios realizados en la fila actual. Si tambin queremos actualizar el precio de COLOMBIAN_DECAF, tenemos que mover el cursor a la fila que contiene ese caf. Cmo la fila de COLOMBIAN_DECAF precede inmediatamente a la fila de FRENCH_ROAST_DECAF, podemos llamar al mtodo previous para posicionar el cursor en la fila de COLOMBIAN_DECAF. El siguiente fragmento de cdigo cambia el precio de esa fila a 9.79 tanto en la hoja de resultados como en la base de datos.
uprs.previous(); uprs.updateFloat("PRICE", 9.79); uprs.updateRow();

Todos los movimientos de cursor se refieren a filas del objeto ResultSet, no a filas de la

263

tabla de la base de datos. Si una peticin selecciona cinco filas de la tabla de la base de datos, habr cinco filas en la hoja de resultados, con la primera fila siendo la fila 1, la sengunda siendo la fila 2, etc. La fila 1 puede ser identificada como la primera, y, en una hoja de resultados con cinco filas, la fila 5 ser la ltima. El orden de las filas en la hoja de resultados no tiene nada que ver con el orden de las filas en la tablas de la base de datos. De hecho, el orden de las filas en la tabla de la base de datos es indeterminado. El controlador de la base de datos sigue la pista de las filas seleccionadas, y hace las actualizaciones en la fila apropiada, pero podran estar localizadas en cualquier lugar de la tabla. Cuando se inserta una fila, por ejemplo, no hay forma de saber donde ser insertada dentro de la tabla.

Insertar y Borrar filas Programticamente


En la seccin anterior hemos visto cmo modificar el valor de una columna utilizando mtodos del API JDBC 2.0 en vez de utilizar comandos SQL. Con el API JDBC 2.0 tambin podemos insertar una fila en una tabla o borrar una fila existente programticamente. Supongamos que nuestro propietario del caf ha obtenido una nueva variedad de caf de uno de sus suministradores. El "High Ground", y quiere aadirlo a su base de datos. Utilizando el API JDBC 1,0 podra escribir el cdigo que pasa una sentencia insert de SQL al controlador de la bse de datos. El siguiente fragmento de cdigo, en el que stmt es un objeto Statement, muestra esta aproximacin.
stmt.executeUpdate("INSERT INTO COFFEES " + "VALUES ('Kona', 150, 10.99, 0, 0)");

Se puede hacer esto mismo sin comandos SQL utilizando los mtodos de ResultSet del API JDBC 2.0. Bsicamente, despus de tener un objeto ResultSet con los resultados de la tabla COFFEES, podemos constuir una nueva fila insertndola tanto en la hoja de resultados como en la tabla COFFEES en un slo paso. Se construye una nueva fila en una llamada "fila de insercin", una fila especial asociada con cada objeto ResultSet. Esta fila realmente no forma parte de la hoja de resultados; podemos pensar en ella como un buffer separado en el que componer una nueva fila. El primer paso ser mover el cursor a la fila de insercin, lo que podemos hacer llamando al mtodo moveToInsertRow. El siguiente paso ser seleccionar un valor para cada columna de la fila. Hacemos esto llamando a los mtodos updateXXX apropiados para cada valor. Observa que estos son los mismos mtodos updateXXX utilizados en la pgina anterior para cambiar el valor de una columna. Finalmente, podemos llamar al mtodo insertRow para insertar la fila que hemos rellenado en la hoja de resultados. Este nico mtodo inserta la fila simultneamente tanto en el objeto ResultSet como en la tabla de la base de datos de la que la hoja de datos fue seleccionada. El siguiente fragmento de cdigo crea un objeto ResultSet actualizable y desplazable, uprs, que contiene todas las filas y columnas de la tabla COFFEES.

Connection con = DriverManager.getConnection("jdbc:mySubprotocol:mySubName"); Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet uprs = stmt.executeQuery("SELECT * FROM COFFEES");

El siguiente fragmento de cdigo utiliza el objeto ResultSet, uprs para insertar la fila para "kona", mostrada en el ejemplo SQL. Mueve el cursor a la fila de insercin, selecciona los cinco valores de columna e inserta la fila dentro de uprs y COFFEES.
uprs.moveToInsertRow(); uprs.updateString("COF_NAME", "Kona"); uprs.updateInt("SUP_ID", 150); uprs.updateFloat("PRICE", 10.99); uprs.updateInt("SALES", 0); uprs.updateInt("TOTAL", 0); uprs.insertRow();

Como podemos utilizar el nombre o el nmero de la columna para indicar la columna seleccionada, nuestro cdigo para seleccionar los valores de columna se podra parecer a esto.
uprs.updateString(1, "Kona"); uprs.updateInt(2, 150); uprs.updateFloat(3, 10.99); uprs.updateInt(4, 0); uprs.updateInt(5, 0);

Podramos habernos preguntado por qu los mtodos updateXXX parecen tener un comportamiento distinto a como lo hacan en los ejemplos de actualizacin. En aquellos ejemplos, el valor seleccionado con un mtodo updateXXX reemplazaba inmediatamente el valor de la columna en la hoja de resultados. Esto era porque el cursor estaba sobre una fila de la hoja de resultados. Cuando el cursor est sobre la fila de insercin, el valor seleccionado con un mtodo updateXXX tambin es automticamente seleccionado, pero lo es en la fila de insercin y no en la propia hoja de resultados. Tanto en actualizaciones como en inserciones, llamar a los mtodos updateXXX no afectan a la tabla de la base de datos. Se debe llamar al mtodo updateRow para hacer que las actualizaciones ocurran en la base de datos. Para el inserciones, el mtodo insertRow inserta la nueva fila en la hoja de resultados y en la base de datos al mismo tiempo. Podramos preguntarnos que sucedera si insertramos una fila pero sin suministrar los valores para cada columna. Si no suministramos valores para una columna que estaba definida para aceptar valores NULL de SQL, el valor asignado a esa columna es NULL. Si la columna no acepta valores null, obtendremos una SQLException. Esto tambin es cierto si falta una columna de la tabla en nuestro objeto ResultSet. En el ejemplo anterior, la peticin era SELECT * FROM COFFEES, lo que produca una hoja de resultados con todas las columnas y todas las filas. Cuando queremos insertar una o ms filas, nuestra peticin no tiene porque seleccionar todas las filas, pero s todas las columnas. Especialmente si nuestra tabla tiene cientos o miles de filas, querremos utilizar una clasula WHERE para lmitar el nmero de filas devueltas por la sentencia SELECT.

265

Despus de haber llamado al mtodo insertRow, podemos construir otra fila, o podemos mover el cursor de nuevo a la hoja de resultados. Por ejemplo, podemos, llamar a cualquier mtodo que ponga el cursor en una fila especfica, como first, last, beforeFirst, afterLast, y absolute. Tambin podemos utilizar los mtodos previous, relative, y moveToCurrentRow. Observa que slo podemos llamar a moveToCurrentRow cuando el cursor est en la fila de insercin. Cuando llamamos al mtodo moveToInsertRow, la hoja de resultados graba la fila en la que se encontraba el cursor, que por definicin es la fila actual. Como consecuencia, el mtodo moveToCurrentRow puede mover el cursor desde la fila de insercin a la fila en la que se encontraba anteriormente. Esto tambin explica porque podemos utilizar los mtodos previous y relative, que requieren movimientos relativos a la fila actual.

Insertar una Fila


El siguiente ejemplo de cdigo es un programa completo que debera funcionar si tenemos un driver JDBC 2.0 que implemente una hoja de resultados desplazable. Hay algunas cosas que podramos observar sobre el cdigo. 1. El objeto ResultSet, uprs es actualizable, desplazable y sensible a los cambios hechos por ella y por otros. Aunque es TYPE_SCROLL_SENSITIVE, es posible que los mtodos getXXX llamados despus de las inserciones no recuperen los valores de la nuevas filas. Hay mtodos en el interface DatabaseMetaData que nos dirn qu es visible y qu ser detectado en los diferentes tipos de hojas de resultados para nuestro driver y nuestro controlador de base de datos. 2. Despus de haber introducido los valores de una fila con los mtodos updateXXX, el cdigo inserta la fila en la hoja de resultados y en la base de datos con el mtodo insertRow. Luego, estndo todava en la "fila de insercin", selecciona valores para otra nueva fila.
import java.sql.*; public class InsertRows { public static void main(String args[]) { String url = "jdbc:mySubprotocol:myDataSource"; Connection con; Statement stmt; try { Class.forName("myDriver.ClassName"); } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); } try { con = DriverManager.getConnection(url, "myLogin", "myPassword"); stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet uprs = stmt.executeQuery("SELECT * FROM COFFEES"); uprs.moveToInsertRow(); uprs.updateString("COF_NAME", "Kona"); uprs.updateInt("SUP_ID", 150); uprs.updateFloat("PRICE", 10.99f); uprs.updateInt("SALES", 0); uprs.updateInt("TOTAL", 0);
266

uprs.insertRow(); uprs.updateString("COF_NAME", "Kona_Decaf"); uprs.updateInt("SUP_ID", 150); uprs.updateFloat("PRICE", 11.99f); uprs.updateInt("SALES", 0); uprs.updateInt("TOTAL", 0); uprs.insertRow(); uprs.beforeFirst(); System.out.println("Table COFFEES after insertion:"); while (uprs.next()) { String name = uprs.getString("COF_NAME"); int id = uprs.getInt("SUP_ID"); float price = uprs.getFloat("PRICE"); int sales = uprs.getInt("SALES"); int total = uprs.getInt("TOTAL"); System.out.print(name + " " + id + " " + price); System.out.println(" " + sales + " " + total); } uprs.close(); stmt.close(); con.close(); } catch(SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } }

Borrar una Fila


Hasta ahora, hemos visto cmo actualizar un valor y cmo insertar una nueva fila. Borrar una fila es la tercera forma de modificar un objeto ResultSet, y es la ms simple. Todo lo que tenemos que hacer es mover el cursor a la fila que queremos borrar y luego llamar al mtodo deleteRow. Por ejemplo, si queremos borrar la cuarta fila de la hoja de resultados uprs, nuestro cdigo se parecera a esto.
uprs.absolute(4); uprs.deleteRow();

La cuarta fila ha sido eliminada de uprs y de la base de datos. El nico problema con las eliminaciones es lo que ResultSet realmente hace cuando se borra una fila. Con algunos driver JDBC, una lnea borrada es eliminada y ya no es visible en una hoja de resultados. Algunos drives JDBC utilizan una fila en blanco en su lugar pone (un "hole") donde la fila borrada fuera utilizada. Si existe una fila en blanco en lugar de la fila borrada, se puede utilizar el mtodo absolute con la posicin original de la fila para mover el cursor, porque el nmero de filas en la hoja de resultados no ha cambiado. En cualquier caso, deberamos recordar que los drivers JDBC manejan las eliminaciones de forma diferente. Por ejemplo, si escribimos una aplicacin para ejecutarse con diferentes bases de datos, no deberamos escribir cdigo que dependiera de si hay una fila vaca en la hoja de resultados. Ver los cambios en una Hoja de Resultados Si modificamos los datos en un objeto ResultSet, los cambios se harn visibles si lo cerramos y lo abrimos de nuevo. En otras palabras, si re-ejecutamos la misma peticin,

267

producir una nueva hoja de resultados, basada en los datos actuales de la tabla. Esta hoja de resultados reflejara naturalmente los cambios que hayamos hecho anteriormente. La cuestin es si podemos ver los cambios que hayamos realizado mientras el objeto ResultSet est todava abierto. (Generalmente, estaremos ms interesados en los cambios hechos por otros). La respuesta depende del controlador de la base de datos, del driver, y del tipo del objeto ResultSet utilizado. Con un objeto ResultSet que sea TYPE_SCROLL_SENSITIVE, siempre podremos ver las actualizaciones que alguien haga en los valores de las columnas. Normalmente veremos inserciones y eliminaciones, pero la nica forma de estar seguros es utilizar los mtodos DatabaseMetaData que devuelven esta informacin. Podemos regular la extensin de que los cambios sean visibles aumentando o bajando el nivel de aislamiento de la transacin con la base de datos. Por ejemplo, la siguiente lnea de cdigo, donde con es un objeto Connection activo, selecciona el nivel de aislamiento de la conexin a TRANSACTION_READ_COMMITTED.
con.setTransactionIsolation(TRANSACTION_READ_COMMITTED);

Con este nivel de aislamiento, nuestro objeto ResultSet no mostrar ningn cambio antes de ser enviado, pero puede mostrar los cambios que podran tener problemas de consistencia. Para permitir menores niveles de inconsistencia, podramos subir el nive de aislamiento a TRANSACTION_REPEATABLE_READ. El problema es que a niveles ms altos de aislamiento, el rendimiento se empobrece. Y siempre estamos limitados por lo que proporcionan las bases de datos y los drivers. En un objeto ResultSet que sea TYPE_SCROLL_INSENSITIVE, generalmente no podremos ver los cambios hechos mientras est abierta. Algunos programadores utilizan slo este tipo de objeto ResultSet porque quieren una vista consistente de los datos y no quieren ver los cambios hechos por otros. Se puede utilizar el mtodo refreshRow para obtener los ltimos valores de una fila en la base de datos. Este mtodo puede utilizar muchos recursos, especialmente si el controlador de la base de datos devuelve mltiples filas cada vez que se llama a refreshRow. De todas formas, puede utilizarse cuando es crtico tener los ltimos datos. Incluso aunque una hoja de resultados sea sensible y los cambios sean visibles, una aplicacin no podra ver siempre los ltimos cambios si el driver recupera varias filas a la vez y las almacena. Por eso, utilizar el mtodo refreshRow es el nico mtodo para asegurarnos que estamos viendo los ltimos datos. El siguiente cdigo ilustra cmo una aplicacin podra utilizar el mtodo refreshRow cuando es absolutamente crtico ver los ltimos valores. Observa que la hoja se resultados debera ser sensible; si queremos utilizar el mtodo refreshRow con un objeto ResultSet que sea TYPE_SCROLL_INSENSITIVE, no har nada. (La urgencia de obtener los ltimos datos es bastante improvable en la tabla COFFEES, pero la fortuna de un inversor, depende de conocer los ltimos precios en la amplia fluctuacin del mercado del caf. O,

268

por ejemplo, querriamos asegurarnos de que el nuestro asiento reservado en el avin de regreso todava est disponible).
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet uprs = stmt.executeQuery(SELECT COF_NAME, PRICE FROM COFFEES); uprs.absolute(4); Float price1 = uprs.getFloat("PRICE"); // do something. . . uprs.absolute(4); uprs.refreshRow(); Float price2 = uprs.getFloat("PRICE"); if (price2 > price1) { // do something. . . }

269

S-ar putea să vă placă și