RECOPILADO POR: ING.

HERBERT ORLANDO MONGE

1

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

Se han utilizado como apuntes bases los siguientes manuales: 1. El lenguaje de programación 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 documentación de java Sdk en www.sun.com.

2

INDICE 1.0 INTRODUCCIÓN. ............................................. 9
1.1 Historia del lenguaje. ................................................................................................ 9 1.2 ¿Qué es Java? ........................................................................................................... 9 1.3 ¿Qué lo hace distinto de los demás lenguajes? .......................................................... 9 1.3.1 ¿Qué se puede programar con Java?................................................................. 10 1.3.2 ¿Es fácil de aprender? ...................................................................................... 10 1.3.3 Características del lenguaje. ............................................................................. 10 1.4 La Máquina Virtual Java (JVM). ............................................................................ 12 1.5 El entorno de desarrollo JDK. ................................................................................. 13 1.5.1 ¿Dónde conseguirlo? ....................................................................................... 13 1.6 CARACTERÍSTICAS 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 Características de los Objetos .......................................................................... 15 2.2.2 Definición 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 VISIÓN GENERAL Y ELEMENTOS BÁSICOS ................................................................. 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 Aritméticos ................................................................................... 23 3.4.2 Operadores Relacionales y Condicionales ........................................................ 25
3

3.4.3 Operadores de Desplazamiento ........................................................................ 25 3.4.4 Operadores de Asignación ............................................................................... 27 3.4.5 Expresiones ..................................................................................................... 27 3.5 CADENAS DE CARACTERES ............................................................................ 29 3.5.1 Concatenación ................................................................................................. 29 3.5.2 Subcadenas ...................................................................................................... 29 3.5.3 Comparación 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 INTRODUCCIÓN ................................................................................................. 36 5.2 CLASES Y HERENCIA ........................................................................................ 36 5.2.1 Definición de una clase .................................................................................... 37 5.2.1.1 Modificadores de clases ................................................................................ 37 5.2.2 Modificadores de métodos y variables ............................................................. 39 5.2.2.1 Modificadores de variables ........................................................................... 39 5.2.2.2 Modificadores de un método ......................................................................... 39 5.2.3 Herencia .......................................................................................................... 39 5.2.4 Creación 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 Tamaño 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 Introducción ........................................................................................................... 49 6.2 Ciclo de vida .......................................................................................................... 49 6.3 Argumentos............................................................................................................ 51 6.4 Gráficas .................................................................................................................. 53 6.5 Tipo de letra ........................................................................................................... 55 6.6 Color ...................................................................................................................... 56 6.7 Imágenes ................................................................................................................ 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 Diálogos .......................................................................................................... 80

8.0 Gestores de diseño ............................................ 83
8.1 Gestores de diseño .................................................................................................. 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 delegación de eventos ............................................................................. 98 Gestión de eventos en Java. .......................................................................................... 99

10.0 EXCEPCIONES ........................................... 112
10.1- ¿QUÉ SON LAS EXCEPCIONES? .................................................................. 112 10.2.- DETECCIÓN DE ERRORES .......................................................................... 113 10.3.- MANEJO DE EXCEPCIONES ........................................................................ 113 10.4.- FINALLY ........................................................................................................ 115 10.5.- LANZAMIENTO DE EXCEPCIONES ........................................................... 117 10.6.- JERARQUÍA DE EXCEPCIONES .................................................................. 117 10.7.- CREACIÓN DE EXCEPCIONES .................................................................... 119 10.8.- OBTENCIÓN DE INFORMACIÓN DE UNA EXCEPCIÓN .......................... 120 10.9.- RESUMEN EXCEPCIONES EXPLÍCITAS-IMPLÍCITAS ............................. 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 más. ..................................................................................... 129 11.2 Estado de un thread. ........................................................................................... 130 11.3 Creación de threads. ........................................................................................... 131 11.4.1 currentThread(). ........................................................................................... 132 11.4.2 isAlive(). ..................................................................................................... 132 11.4.3 sleep(). ......................................................................................................... 133
5

.................5 wait()........................................................................... 198 JProgressBar ... 153 JFrame .......................................................6 yield()..........5 Threads y sincronismo............................................................................................................ 135 11............................................................. 180 Componentes atómicos .......................................... .................................................................................. 185 JComboBox ............................................... JOptionPane ................................ 196 JLabel ........................................8 Grupos de threads.. 168 JTabbedPane .................... 144 11............7 Threads Daemon............................................ ......................................................................... 188 JSlider .................................................................................................... 151 Contenedores de alto nivel ....... ............................ 150 JFC y Swing ................................................................. 171 JToolBar ............................................................................................................................................................................................................................................................................. . 134 11............................................................................................4......................................................................................................................................................................................................................................................................................................................................................................... .......................................................... 180 componentes atómicos de Swing .................4 suspend().......0 Swing ..5............................................................................................4............................ 136 11............................... 208 El gestor de diseño BoxLayout ...11............................................. 136 11.................................6 Prioridad de un thread........................................................................... 181 JCheckbox................................. .2 Sección crítica............. 208 características de Swing .......................3 Monitores............................. 209 6 .................................................................................................................................................................. 136 11.............................................................5........................................................................................ 167 Contenedores intermedios .................................................... 134 11....... 180 Componentes para obtener información ............................................................................................................................................................ 200 JFileChooser ................ 208 Introducción ............... 205 Interfaces de usuario en Java: otras ................................................................................. 181 JButton............................................................................................................1 Creación de grupos de threads...................................8........................ 198 Componentes que muestran información estructurada ..................................................................................7 join()................................................................................................ 157 JDialog.............................................. 159 JApplet ........ 146 12.... 175 JLayeredPane .............. 193 Componentes para mostrar información ...................................................................................... 145 11................................................................ 176 Interfaces de usuario en Java: ................................................................................ 146 11....................................................................4.................................................................... 139 11....................... 183 JRadioButton .................................................................................................... 187 JMenu .............................................................................. 200 JColorChooser...................................................................5.................................... 133 11............................................. 167 JPanel.............1 El paradigma productor/consumidor ..........................4............... 150 Componentes Swing frente a componentes AWT ............................................................................................................................................ .. 196 JToolTip.................................. ..............................................................................

.......5 SQL Conforme .............................................................................................................................................................1 JavaSoft Framework ................................................... 235 2.5 Registro de subprotocolos .......... 229 1... 232 1........................................................ 245 5........................1.........................1......................................................1............................... 235 2.......................................1 ¿Qué hace JDBC? ..... 235 2................................ ....................................3 JDBC y URL‟s ................1.........1........................1...................................... 243 4............................................................1........1.........................................................................................2 Ejecución de sentencias usando objetos Statement...........1............. 241 3...................... ........................6 Envío de Sentencias SQL . 236 2. 243 4.........1 Vista Preliminar .......................1..... 238 2.0 CONEXIONES A BASES DE DATOS CON JAVA ....1.............................................7 Transacciones ....2 Tipos de drivers JDBC ..................... LA CLASE DriverManager ........... 245 4....1 Mantenimiento de la lista de drivers disponibles............................... 239 2.............................1.................................................................... 228 1.............. 243 4...................................................................................... 244 4......... 241 3.........................................................2 JDBC es un API de bajo nivel y una base para API‟s de alto nivel................................................... INTRODUCCION JDBC ........................Estableciendo el Look & Feel........................... 213 Ejemplos de Swing ...1 ¿Qué es JDBC?.. 227 1..........................1 Vista Preliminar ...................................1 Creación de objetos Statement ........... 241 3............................... 246 7 ........................ 238 2....................................................... LA CLASE Statement ..................3 JDBC frente a ODBC y otros API‟s .......... 238 2...............................................2 Establecer una conexión ................................................................................ 244 4.............................................................................1 Vista preliminar .......1 Apertura de una conexión ........................2 Uso general de URL‟s...................... ..............4 Cerrar objetos Statement... 227 1..................................................................................................1........2 Productos JDBC.................. 231 1..........8 Niveles de aislamiento de transacciones ....................1................... 240 3.......... 235 2.... 228 1......... 233 1..........................2...........2...................................1....4 El subprotocolo “odbc” ........................ 219 1....................1.........................1........................................ 234 2.....................1..................... ..........................3 Realización de Statement .......................... ......... CONEXIÓN .....

8 .

De esta forma. De momento. 1. Gracias a una acertada decisión de distribuir libremente el lenguaje por la Red de Redes y al auge y la facilidad de acceso a Internet.3 ¿Qué lo hace distinto de los demás lenguajes? Una de las características más importantes es que los programas “ejecutables”. Puede conseguirse un JDK (Java Developer's Kit) o Kit de desarrollo de aplicaciones Java gratis. además. concediéndose licencias a cualquiera sin ningún problema. El nombre del lenguaje tuvo que ser cambiado ya que existía otro llamado Oak. 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.1 Historia del lenguaje. El lenguaje Java fue creado por Sun Microsystems Inc. No es un acrónimo. son independientes de la arquitectura. Permite escribir Applets (pequeños programas que se insertan en una página HTML) y se ejecutan en el ordenador local. el precursor de Java. fomentando su uso entre la comunidad informática y extendiendo las especificaciones y funcionalidad del lenguaje. El nombre “Java” surgió en una de las sesiones de “brainstorming” celebradas por el equipo de desarrollo del lenguaje. añadiéndosele numerosas clases y funcionalidad para TCP/IP. con un Sistema Operativo eficiente (SunOS) y un lenguaje de desarrollo denominado Oak (roble). Se pretendía crear un hardware polivalente. Sun ha sabido manejar inteligentemente el éxito obtenido por su lenguaje. únicamente quedaba del proyecto el lenguaje Oak. año en el que Sun creó un grupo de trabajo. con relación a alternativas similares. permitiera la conexión a redes de ordenadores. 9 . Java fue elegido de entre muchísimas propuestas.2 ¿Qué es Java? Java es un lenguaje de desarrollo de propósito general. Se ejecutan indistintamente en una gran variedad de equipos con diferentes microprocesadores y sistemas operativos. Entonces. 1. propiciado por la WWW. es público. en un proceso por etapas que arranca en 1990. A partir de este momento. para desarrollar un sistema para controlar electrodomésticos e incluso PDAs o Asistentes Personales (pequeños ordenadores) que. 1. liderado por James Gosling.El lenguaje de Programación Java 1. ¿es simplemente otro lenguaje más? Definitivamente no. No se sabe si en un futuro seguirá siéndolo. El proyecto finalizó en 1992 y resultó un completo fracaso debido al excesivo coste del producto. Después de la disolución del grupo de trabajo. Incluye una combinación de características que lo hacen único y está siendo adoptado por multitud de fabricantes como herramienta básica para el desarrollo de aplicaciones comerciales de gran repercusión. animación. creados por el compilador de Java. Por entonces aparece Mosaic y la World Wide Web. y como tal es válido para realizar todo tipo de aplicaciones profesionales. sino únicamente algo humeante. rapidez. tras lo cual el grupo se disolvió. Buscaban un nombre que evocara la esencia de la tecnología (viveza. Sun lanzó las primeras versiones de Java a principios de 1995. Desde entonces. el lenguaje se difunde a una velocidad vertiginosa. caliente y que a muchos programadores les gusta beber en grandes cantidades: una taza de café (Java en argot Inglés americano2).0 INTRODUCCIÓN. interactividad …).

un buen estilo de programación orientada a objetos. 1.3. y sin esfuerzo. además. En particular los del C++. como ocurre con otros lenguajes de programación. Applets.3 Características del lenguaje. Es fácil de aprender y está bien estructurado. También. 1. no puede ser de otra forma. A medida que se va aprendiendo. Esto es bastante conveniente. y posteriormente subsanado.3. En realidad.2 ¿Es fácil de aprender? Para el colectivo de programadores que conocen la programación orientada a objetos. el cambio a Java puede ser realmente sencillo. se va fomentando en el programador.Se pueden escribir aplicaciones para intraredes. “agujeros” en la seguridad3 de Java. Puede controlarse su seguridad frente al acceso a recursos del sistema y es capaz de gestionar permisos y criptografía. Para todo aquel que no conozca la programación orientada a objetos. no permite “abandonar” la programación orientada a objetos.1 ¿Qué se puede programar con Java? Si tenía preconcebida la idea de que con Java sólo se programan applets para páginas web. aunque los maneja interna y transparentemente. según Sun. Funciona perfectamente en red. Pequeñas aplicaciones que se ejecutan en un documento HTML. la seguridad frente a virus a través de redes locales e Internet está garantizada. A medida que se van comprobando las ventajas de la programación orientada a objetos. como ocurre con los navegadores HotJava y las últimas versiones de Netscape y el explorador de Internet de Microsoft. Las aplicaciones son fiables. Ya que Java es un lenguaje de propósito general. ya que Java impide “hacer cosas extrañas” y. para aquellos que las desconocen. puede programarse en él cualquier cosa: Aplicaciones independientes. de lo contrario. y la facilidad y naturalidad del lenguaje Java. y después de algún tiempo trabajando con Java. este lenguaje es ideal para aprender todos sus conceptos. siempre y cuando el navegador soporte Java. 10 . Aunque al igual que ha ocurrido con otras tecnologías y aplicaciones. se han descubierto.3. ya que en cada paso de su aprendizaje se va comprobando que las cosas se hacen en la forma natural de hacerlas. Es un lenguaje bien estructurado. éste va atrapando a quien se acerca a él. hay pocos programadores que no lo consideren como “su favorito”. Tiene una gran funcionalidad gracias a sus librerías (clases). está completamente equivocado. un programador que está aprendiendo puede sentir la tentación de “volver” a lo que conoce (la programación tradicional). ya que la sintaxis es prácticamente la misma que en este lenguaje. sin sorpresas ni comportamientos extraños de los programas. Para los programadores en C++ también es sencillo el cambio. aplicaciones cliente/servidor. aplicaciones distribuidas en redes locales y en Internet. sin punteros y sin necesidad de tener que controlar la asignación de memoria a estructuras de datos u objetos. Es intrínsecamente orientado a objetos. 1. Como con cualquier otro lenguaje de propósito general. NO tiene punteros manejables por el programador. Aprovecha características de la mayoría de los lenguajes modernos evitando sus inconvenientes.

de una forma bastante eficiente. es posible compilar el programa en una estación UNIX y ejecutarlo en otra con Windows95 utilizando la máquina virtual Java para Windows95. limpio y práctico. Que el programa deba ser “interpretado” no hace que sea poco eficiente en cuanto a velocidad. la JVM (Java Virtual Machine) máquina virtual Java. La sintaxis es parecida a la de este lenguaje. De esta forma. Gracias a que fue diseñado “partiendo de cero” ha conseguido convertirse en un lenguaje orientado a objetos puro. ¿El lenguaje es Compilado o Interpretado? Ni una cosa ni la otra. es 11 . ya que la interpretación se hace prácticamente al nivel de código máquina. Incorpora Multi-Threading (para permitir la ejecución de tareas concurrentes dentro de un mismo programa). es necesario un “intérprete”. es realmente sencillo aprender Java. no necesitaba ser compatible con versiones anteriores de ningún lenguaje como ocurre con C++ y C. El lenguaje Java puede considerarse como una evolución del C++. es decir. por lo que en este libro se hará referencia a dicho lenguaje frecuentemente.El manejo de la memoria no es un problema. Genera aplicaciones con pocos errores posibles. necesita de un proceso previo de compilación. una vez superado el aprendizaje de la programación orientada a objetos. Por ejemplo. No permite programar mediante otra técnica que no sea la programación orientada a objetos (POO en adelante) y. ya que Java fue diseñado “partiendo de cero”. la gestiona el propio lenguaje y no el programador. A pesar de que puede considerarse como una evolución del C++ no acarrea los inconvenientes del mismo. Esta JVM se encarga de leer los bytecodes y traducirlos a instrucciones ejecutables directamente en un determinado microprocesador. Aunque estrictamente hablando es interpretado. Una vez “compilado” el programa. se crea un fichero que almacena lo que se denomina bytecodes o j_code (pseudocódigo prácticamente al nivel de código máquina). Para ejecutarlo.

principalmente porque la gestión de memoria y punteros es realizada por el propio lenguaje y no por el programador.mucho más rápido que cualquier otro programa interpretado como por ejemplo Visual Basic. 1.4 La Máquina Virtual Java (JVM). como Netscape o Internet Explorer. Las aplicaciones creadas en este lenguaje son susceptibles de contener pocos errores. el lenguaje contiene estructuras para la detección de excepciones (errores de ejecución previstos) y permite obligar al programador a escribir código fiable mediante la declaración de excepciones posibles para una determinada clase reutilizable. Además. Esta deficiencia en cuanto a la velocidad. Suelen ser incorporados por los navegadores. El lenguaje Java es robusto. aunque es más lento que el mismo programa escrito en C++. Bien es sabido que la mayoría de los errores en las aplicaciones vienen producidos por fallos en la gestión de punteros o la asignación y liberación de memoria. puede ser aminorada por los compiladores Just-InTime (JIT). por lo que es más rápido. La máquina virtual Java es la idea revolucionaria 4 del lenguaje. Un compilador JIT transforma los bytecodes de un programa o un applet en código nativo de la plataforma donde se ejecuta. Es la entidad que proporciona la independencia de plataforma para los programas Java “compilados” en byte-code. 12 .

si un mismo programa en byte-code puede ser ejecutado en distintas plataformas es porque existe un traductor de ese byte-code a código nativo de la máquina sobre la que se ejecuta. Esta JVM se carga en memoria y va traduciendo “al vuelo”. Existe una versión distinta de esta JVM para cada plataforma. Evidentemente.1 ¿Dónde conseguirlo? El Kit de desarrollo puede obtenerse en las direcciones siguientes: http://www.exe y javaw. Esta tarea es realizada por la JVM. Su sintaxis es la de C++ “simplificada”. en un compilador y un intérprete (JVM) para la línea de comandos. los byte-codes a código máquina. 13 . que nos ayudarán a ver para que tipo de problemas está pensado Java: 1.h) para clases nativas en C: javah.exe Un desensamblador de clases: javap.c y . creando toda la estructura de directorios. La JVM no ocupa mucho espacio en memoria.exe El visualizador de applets: appletviewer. El kit contiene básicamente: El compilador: javac.exe 1. No dispone de un entorno de desarrollo integrado (IDE).exe El generador de documentación: javadoc. La herramienta básica para empezar a desarrollar aplicaciones o applets en Java es el JDK (Java Developer’s Kit) o Kit de Desarrollo Java. etc. 1. ya que se supone que el compilador de Java traduce el fichero fuente a código ejecutable por una máquina que únicamente existe en forma virtual (aunque se trabaja en la construcción de microprocesadores que ejecuten directamente el byte-code).exe El depurador: jdb. básicamente.sun. que consiste.exe El intérprete: java. piénsese que fue diseñada para poder ejecutarse sobre pequeños electrodomésticos como teléfonos.5.6 CARACTERÍSTICAS DE JAVA A continuación haremos una pequeña redacción de las características del lenguaje. pero es suficiente para aprender el lenguaje y desarrollar pequeñas aplicaciones.com http://www. televisores. Esto es lógico. 1.com El entorno para Windows está formado por un fichero ejecutable que realiza la instalación. genera el mismo fichero en byte-code. 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.javasoft.5 El entorno de desarrollo JDK.Un mismo programa fuente compilado en distintas plataformas o sistemas operativos.6.exe El generador de archivos fuentes y de cabecera (.1 Simple Es un lenguaje sencillo de aprender.

sino que además debe simular un sistema operativo y hardware virtuales (la máquina virtual).6. En éste un entero. (ej: if(a=b) then . El motivo de esto es que el que realmente ejecuta el código generado por el compilador no es el procesador del ordenador directamente. 1. Esto permite que los Applets de una web pueda ejecutarlos cualquier máquina que se conecte a ella independientemente de que sistema operativo emplee (siempre y cuando el ordenador en cuestión tenga instalada una máquina virtual de Java). 32 o más bits. Así mismo C++ bajo UNIX almacena los datos en formato little endian. volviéndolo a llamar en caso de volverlo a necesitar.6 Portable En Java no hay aspectos dependientes de la implementación. y guardan el resultado de dicha conversión. a excepción de los tipos fundamentales de variables (int. 1. UDP. si bien es cierto que los avances en las máquinas virtuales remedian cada vez más estas decisiones de diseño. Java lo hace siempre en little edian para evitar confusiones.4 Robusto El compilador Java detecta muchos errores que otros compiladores solo detectarían en tiempo de ejecución o incluso nunca. puede tener un tamaño de 16. Java garantiza que ningún Applet puede escribir o leer de nuestro disco o mandar información del usuario que accede a la página a través de la red (como. Mac o Windows. long. 1.6. 1.5 Seguro Sobre todo un tipo de desarrollo: los Applet. mientas que bajo Windows lo hace en big endian. en Java todo.) es un objeto. ya que no sólo ha de cargar en memoria los recursos necesario para la ejecución del programa.. el compilador Java no nos dejaría compilar este código. Esto no ocurre así en C++. HTTP y FTP.7 Arquitectura Neutral El código generado por el compilador Java es independiente de la arquitectura: podría ejecutarse en un entorno UNIX. por ejemplo.1. 14 . por ejemplo. char. Por otro lado la programación gráfica empleando las librerías Swing es más lenta que el uso de componentes nativos en las interfaces de usuario. la dirección de correo electrónico).2 Orientado a Objetos Posiblemente sea el lenguaje más orientado a objetos de todos los existentes. No obstante por norma general el programa Java consume bastante más memoria que el programa C++. soportando protocolos como TCP/IP..6.. Por otro lado el uso de estos protocolos es bastante sencillo comparandolo con otros lenguajes que los soportan. En general no permite realizar cualquier acción que pudiera dañar la máquina o violar la intimidad del que visita la página web.6. que no precisa de la máquina virtual para ser ejecutado. 1. por ejemplo. 1. sino que este se ejecuta mediante una máquina virtual.6. compiladores que traduce los bytecodes de Java en código para una determinada CPU. siendo lo única limitación que el entero sea mayor que un short y menor que un long int. Esto es así gracias al uso de compiladores just in time.8 Rendimiento medio Actualmente la velocidad de procesado del código Java es semejante a la de C++. En general en Java se ha sacrificado el rendimiento para facilitar la programación y sobre todo para conseguir la característica de neutralidad arquitectural. hay ciertos pruebas estándares de comparación (benchmarks) en las que Java gana a C++ y viceversa.3 Distribuido Java está muy orientado al trabajo en red. con lo que se evita la sobrecarga de trabajo asociada a la interpretación del bytecode. todas las implementaciones de Java siguen los mismos estándares en cuanto a tamaño y almacenamiento de los datos.6.6. Estos son programas diseñados para ser ejecutados en una página web..

Esto le permite además que cada Thread de una aplicación java pueda correr en una CPU distinta. de modo transparente para el programador. Los objetos son como los tipos abstractos de datos.2 Definición de objetos.9 Multithread Soporta de modo nativo los threads. clases. 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.1.0 CONCEPTOS DE P.6. colas. La potencia real de los objetos reside en las propiedades que soportan: herencia. Un objeto es una unidad que contiene datos y las funciones que operan sobre esos datos. utilizadas para operar sobre esos datos. sin necesidad del uso de de librerías específicas (como es el caso de C++). encapsulación (o encapsulamiento) y polimorfismo junto con los conceptos de objetos. Las aplicaciones de C++ no son capaces de distribuir. 2. 2.O 2. Los datos y las funciones se encapsulan en una única entidad: objeto.O? Técnica de programación que utiliza objetos como bloque esencial de construcción. si la aplicación se ejecuta en una máquina que posee varias CPU.2.2. Contienen datos internos que definen su estado actual. junto con las funciones asociadas. Pueden heredar propiedades de otros objetos.O. la carga entre varias CPU.2 ¿Qué es un objeto? Un objeto es una colección de datos. Encapsulación (encapsulamiento) de datos y ocultación de datos. 2. Pueden comunicarse con otros objetos enviando o pasando mensajes. Gráficamente: 15 . métodos y mensajes. etc. Tienen métodos que definen su comportamiento.O.1 Características de los Objetos Se agrupan en tipos denominados clases. Ejemplo: listas. 2.1 ¿Qué es p. Soportan ocultación de datos.

5 Herencia • Es la propiedad que permite a los objetos construirse a partir de otros objetos.3 CLASES • Una clase es un tipo definido por el usuario que determina las estructuras de datos y las operaciones asociadas con ese tipo. cada subclase tiene sus propias características particulares. • Cuando se construye un objeto de una clase. • Además de las características compartidas con otros miembros de la clase. 2. • Ejemplo: • El principio de este tipo de división es que cada subclase comparte características comunes con la clase de la que se deriva. • Paso de mensajes: invocación o llamada de un método de un objeto. 16 . • Instancia de clase = objeto 2.2. ejecutando uno de sus métodos. • Son como plantillas o modelos. se crea una instancia de clase.4 Mensaje • Un mensaje es simplemente la petición de un objeto a otro objeto para que ése se comporte de una determinada manera.

el cual difiere de una figura a otra. O además. • Herencia múltiple: la clase recibe propiedades de mas de una clase base.• La herencia permite definir nuevas clases a partir de clases ya existentes. • Ejemplo: Operación sumar. • En un lenguaje de programación el operador + representa la suma de dos números ( x + y ) de diferentes tipos: enteros. • La herencia impone una relación jerárquica entre clases en la cual una clase hija hereda de su clase padre. • Polimorfismo es la capacidad del código de un programa para ser utilizado con diferentes tipos de datos u objetos. se puede definir la operación de sumar dos cadenas: concatenación.6 Polimorfismo • Polimorfismo: significa la cualidad de tener más de una forma. flotantes. 17 . Cada objeto reacciona a este mensaje haciendo el cálculo correspondiente de la superficie. 2. • También 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. • Herencia simple: la clase solo puede recibir características de otra clase base. • De modo similar: suponiendo un número de figuras geométricas que responden todas al mensaje Calcular _ superficie.

y otra correspondiente al cuerpo de la misma: Declaración de clase { Cuerpo de clase } En la plantilla anterior se ha simplificado el aspecto de la Declaración de clase. pero sí que puede asegurarse que la misma contendrá. no son accesibles fuera del mismo objeto. la palabra reservada class y el nombre que recibe la clase.• El concepto de polimorfismo se puede aplicar tanto a funciones como a tipos de datos. por su parte. •Los tipos polimórficos. como mínimo. debe contener una clase que tenga un método main con la siguiente declaración: public static void main( String args [] ) 18 . En un fichero fuente puede declararse una o más clases y tendrá un aspecto similar a este: class Clase1 { … } class Clase2 { … } … class ClaseN { … } Una clase está formada por una parte correspondiente a la declaración. un programa estará formado por uno o varios ficheros fuente y en cada uno de ellos habrá definida una o varias clases. e incluso los métodos. prácticamente todo son clases (objetos). • Las funciones polimórficas son aquellas funciones que pueden evaluarse y/o ser aplicadas a diferentes tipos de datos de forma indistinta. Así nacen los conceptos de funciones polimórficas y tipos polimórficos. La forma natural de construir una clase es la de definir una serie de atributos que. 2. a otras partes del programa u otros objetos. El cuerpo de las clases comienza con una llave abierta({) y termina con una llave cerrada (}).7 Encapsulacion El encapsulamiento consiste en la propiedad que tienen los objetos de ocultar sus atributos. 3. Por esta razón. son aquellos tipos de datos que contienen al menos un elemento cuyo tipo no está especificado. En Java. sino que únicamente pueden modificarse a través de los métodos que son definidos como accesibles desde el exterior de esa clase. Dentro del cuerpo de la clase se declaran los atributos y los métodos de la clase.0 VISIÓN GENERAL Y ELEMENTOS BÁSICOS DEL LENGUAJE. en general. El lenguaje obliga a la programación orientada a objetos y no permite la posibilidad de programar mediante ninguna otra técnica que no sea ésta. Para que un programa se pueda ejecutar.

de la clase Object. 19 . distinguiendo entre mayúsculas y minúsculas). hay que teclear: javac Hola. En nuestro ejemplo. pero sí que puede afirmarse que una vez comprendida y asimilada su filosofía.println("Hola. Entre las llaves de la clase Hola. No se pretende que. También hay que comentar que en el nombre del fichero fuente también se hace distinción entre mayúsculas y minúsculas a la hora de compilarlo. Se utilizan para marcar principio y final de bloques de código y se interpretan mediante el método LIFO (Last In First Out) donde el último en entrar es el primero en salir. En el ejemplo existen. En primer lugar.java El resultado de la ejecución será la visualización en pantalla del mensaje: Hola.class u Hola.1 Mi primer programa. mundo” y terminará.java (ATENCIÓN: Es importante que tenga el mismo nombre que la clase “Hola”. class Hola { public static void main(String args[]) { System. Las llaves no sirven únicamente para marcar el inicio y el fin de una clase. contiene muchos de los conceptos de la programación orientada a objetos en Java. se aprendan y comprendan la totalidad de los aspectos de la POO y la sintaxis del Java. únicamente tiene un método: main. este es mi primer programa"). este es mi primer programa Explicación del programa Hola. aunque el sistema operativo empleado no haga esta distinción. a pesar de no ser muy extenso. Este ejemplo habrá que crearlo mediante un editor cualquiera y guardar el fichero con el nombre Hola. se estará en un punto bastante cercano a los conocimientos básicos necesarios para entenderlos. a partir del mismo. distingue entre mayúsculas y minúsculas.java Para compilar el programa. La primera línea del programa declara una clase llamada Hola. al igual que C. se declaran los atributos y los métodos de la clase. } } El lenguaje Java. vamos a ver qué estructura debería tener el típico programa con el que se empieza en cualquier lenguaje de programación y que proporciona una primera visión de la sintaxis del lenguaje y que comienza a transmitir una serie de sentimientos de aceptación o de rechazo en el programador. además de las llaves abierta y cerrada de la clase.java El compilador creará un fichero que se llama Hola.java línea a línea: Este simple programa. al no indicar nosotros que herede de otra clase. Que se interpreten mediante el método LIFO significa que la llave cerrada ( } ) del método main() parentiza el bloque abierto por la última llave ( { ) antes de ésta. otras llaves abierta y cerrada dentro del método main(). que es descendiente. por lo que es importante transcribirlo literalmente. El programa simplemente mostrará en pantalla el mensaje “hola.class que contiene el código bytecode (“pseudoejecutable”) Para ejecutarlo: java Hola Se escribe únicamente el nombre de la clase Hola y no Hola. La extensión del mismo debe ser .out.3. Todo lo que se encuentre entre la llave abierta ( { ) y la llave cerrada ( } ) pertenece a la clase Hola.

un vector de strings. como parámetro. ¿Un poco complicado para simplemente mostrar un mensaje en pantalla? Es posible que sí lo sea para aquellos que aún no conocen la programación orientada a objetos. Los comentarios en los programas fuente son muy importantes en cualquier lenguaje. encargado de mostrar un valor a través de la salida estándar (en nuestro caso. Para aquellos que conozcan C++ pensarán que es bastante parecido a lo que habrían escrito en su “lenguaje favorito”.Todo programa independiente escrito en Java empieza a ejecutarse(como en C) a partir del método main(). puede ser llamado desde otras clases.println("hola. no se utilice.out. Todo método main() debe ser público para poder ejecutarse desde el intérprete Java (JVM). por tanto. no ocuparán espacio en el fichero “ejecutable”. Sirven para aumentar la facilidad de comprensión del código y para recordar ciertas cosas sobre el mismo. hay que anteponerle /* y al final */. También indica que el método es el mismo para todas las instancias que pudieran crearse. pero el intérprete Java no podrá ejecutarlas inicialmente. static: indica que la clase no necesita ser instanciada para poder utilizar el método al que califica. Como curiosidad (de momento). Este atributo se encuentra incluido dentro de la clase System. un String) y después realizar un retorno de carro y nueva línea. que contendrá los posibles argumentos que se le pasen al programa en la línea de comandos. Declaración del método main(): public: indica que el método main() es público y. Únicamente sirven para documentar. (No se crea ninguna instancia u objeto de la clase Hola). Este método pertenece al atributo out. y. Este línea indica que se va a ejecutar el método println(). Se pueden compilar clases que no posean método main(). aunque como es nuestro caso. Ejemplo: /* Esto es un comentario que 20 . cabe mencionar que esta clase es static (no hemos declarado ningún objeto de la clase System). pero una vez conocidos y asimilados los conceptos de la POO (Programación Orientada a Objetos) el programa es bastante sencillo y lógico. Son porciones del programa fuente que no serán compiladas. Si no se han comprendido muy bien todos estos conceptos no importa. este es mi primer programa"). 3. Hay una regla sencilla que nunca falla: El método main() siempre se declara de la misma forma: public static void main(String args[]) La instrucción que realmente realiza el trabajo efectivo de nuestro programa es la siguiente: System. aunque sí pueden ejecutarse si son llamadas desde otro método en ejecución. El método main debe aceptar siempre.2 Comentarios. por tanto. Veremos en posteriores capítulos que la forma de crear los applets es distinta y no necesita declarar este método. de momento. Si un comentario debe ocupar más de una línea. Este colectivo comprobará que es mucho más fácil que C y tiene algunas ventajas. void: indica que la función main no devuelve ningún valor.

su formato. En Java tenemos los arrays. etc.i++) // comentario de bucle { System. El tipo de la variable determina los valores que la variable puede contener y las operaciones que se pueden realizar con ella.1 Tipos de Variables Todas las variables en el lenguaje Java deben tener un tipo de dato.out. ya que desde la aparición de las dos barras inclinadas // hasta el final de la línea es considerado como comentario e ignorado por el compilador. su tamaño y una breve descripción 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. Los tipos primitivos contienen un sólo valor e incluyen los tipos como los enteros.3 Variables y Tipos de Datos Las variables son las partes importantes de un lenguaje de programación: ellas son las entidades (valores. las clases y los interfaces como tipos de datos referenciados. coma flotante. basta con poner dos barras inclinadas: // Ejemplo: for (i=0.. datos) que actúan y sobre las que se actúa. Existen dos categorias de datos principales en el lenguaje Java: los tipos primitivos y los tipos referenciados. La tabla siguiente muestra todos los tipos primitivos soportados por el lenguaje Java. Una declaración de variable siempre contiene dos componentes.ocupa tres líneas */ Si el comentario que se desea escribir es de una sola línea. los caracteres. 3. 3.. el tipo de la variable y su nombre: tipoVariable nombre. 21 .3. } No puede ponerse código después de un comentario introducido por // en la misma línea. i<20.println(“Adiós”). Existe otro tipo de comentario que sirve para generar documentación automáticamente en formato HTML mediante la herramienta javadoc .

No puede ser el mismo que una palabra clave o el nombre de un valor booleano (true or false) 3.3 CONVERSIÓN ENTRE TIPOS NUMÉRICOS Las normas de conversión entre tipos numéricos son las habituales en un lenguaje de programación: si en una operación se involucran varios datos numéricos de distintos tipos todos ellos se convierten al tipo de dato que permite una mayor precisión y rango de representación numérica. short y byte.long <.int <. 22 . un short con un short o un short con un byte Java empleará para dicha operación el operador de los datos tipo int. no deben tener el mismo nombre que otras variables cuyas declaraciones aparezcan en el mismo ámbito.168 caracteres. al menos. como 'nombreDato' las palabras se ponen juntas y cada palabra después de la primera empieza con una letra mayúscula. 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. 3. Veamos una ejemplo para comprender su sintaxis: class Cast { public static void main(String[] args) { int i = 9. Debe ser un identificador legal de Java comprendido en una serie de caracteres Unicode.short <byte. Estas conversiones sólo nos preocuparán a la hora de mirar en que tipo de variable guardamos el resultado de la operación. Por convención. el Ruso o el Hebreo.3. La regla número 3 implica que podría existir el mismo nombre en otra variable que aparezca en un ámbito diferente. arriesgándonos a perder información en el cambio. Unicode es un sistema de codificación que soporta texto escrito en distintos lenguajes humanos. Si una variable está compuesta de más de una palabra. Por convención. -Si cualquier operando es long y no hay datos reales todos se convertirán en long. -Si cualquier operando es float y no hay ningún double todos se convertirán a float. Esto es importante para que los programadores pueden escribir código en su lenguaje nativo. Este tipo de operación (almacenar el contenido de una variable de jerarquía superior en una de jerarquía inferior) se denomina cast. Si es de rango superior no habrá problemas. y otro que emplea para operar datos de tipo int. La “jerarquía” en las conversiones de mayor a menor es: double <. Un nombre de variable Java: 1. Esto le permite utilizar en sus programas Java varios alfabetos como el Japonés. por lo que el resultado de dicha operación será un int siempre. el Griego. de una jerarquía mayor o igual a la jerarquía de la máxima variable involucrada en la operación. 2.2 Nombres de Variables Un programa se refiere al valor de una variable por su nombre. así. Del mismo modo estas normas se extenderán para int. los nombres de variables empiezan por un letra minúscula.Unicode perminte la codificación de 34. De este modo cuando operemos un byte con un byte. esta ha de ser. Es posible convertir un dato de jerarquía “superior” a uno con jerarquía “inferior”. por ejemplo: -Si cualquier operando es double todos se convertirán en double. en Java.3.float <.k. los nombres de las variables empiezan con una letra minúscula (los nombres de las clases empiezan con una letra mayúscula).

//float m = 2.3.incluyendo + (suma). relacionales y condicionales.4. La notación de prefijo significa que el operador aparece antes de su operando: operador operando La notación de sufijo significa que el operador aparece después de su operando: operando operador Todos los operadores binarios de Java tienen la misma notación. 3. lógicos y de desplazamiento y de asignación.println("j: " + j + " k: " +k). Por ejemplo. . } } 3. float m = 2.out. el resultado típico de las operaciones aritméticas. Es muy útil dividir los operadores Java en las siguientes categorías: aritméticos. Los operadores unarios en Java pueden utilizar la notación de prefijo o de sufijo. ++ es un operador unario que incrementa el valor su operando en uno. j = k.println("m: "+m).println("j: " + j + " k: " +k). En todos los números enteros y de coma flotante. los operadores aritméticos (realizan las operaciones de aritmética básica como la suma o la resta) devuelven números. * (multiplicación).9F. System. k = (int)j. y % (módulo). Por ejemplo.(resta).3F. puedes utilizar este código Java para sumar dos números: sumaEsto + aEsto O este código para calcular el resto de una división: divideEsto % porEsto 23 . daría error al compilar.out.//no necesita cast System.out. System.out. / (división).println("i: "+i + " j: " +j). Los operadores que requieren dos operandos se llaman operadores binarios.1 Operadores Aritméticos El lenguaje Java soporta varios operadores aritméticos . es decir aparecen entre los dos operandos: op1 operator op2 Además de realizar una operación también devuelve un valor. Los operadores que requieren un operador se llaman operadores unarios. //empleo de un cast System. Por ejemplo.4 Operadores de Java Los operadores realizan algunas funciones en uno o dos operandos. El tipo de datos devuelto por los operadores aritméticos depende del tipo de sus operandos: si sumas dos enteros. Se dice que una operación evalúa su resultado. obtendrás un entero.float j = 47. El valor y su tipo dependen del tipo del operador y del tipo de sus operandos. El operador = es un operador binario que asigna un valor del operando derecho al operando izquierdo.

primero //decrementa i y luego lo imprime por consola prt("i-. prt("i : " + i). prt("++i : " + ++i). // Post-decremento. primero imprime //i por consola y luego de decrementa. } } 24 .println(s). Los operadores + y . ++ que incrementa en uno su operando.out.Esta tabla sumariza todas las operaciones aritméticas binarias en Java: Operador Uso + op1 + op2 op1 . Invocando a prt podremos //imprimir por consola la cadena de caracteres que le pasemos static void prt(String s) { System.op2 * op1 * op2 / op1 / op2 % op1 % op2 Descripción 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 definición del operador + para incluir la concatenación de cadenas. y -.tienen versiones unarias que seleccionan el signo del operando: Además. existen dos operadores de atajos aritméticos. // Post-incremento. prt("i : " + i).: " + i--). Ejemplo: class Incremento{ public static void main(String[] args) { int i = 1. // Pre-incremento. primero //incrementa y luego imprime por consola prt("i++ : " + i++).que decrementa en uno el valor de su operando. // Pre-decremento. primero imprime //“2” por consola y luego incrementa i.//Ahora i vale 1 } //esto nos ahorrara teclear. prt("i : " + i).//i por lo tanto vale 3 prt("--i : " + --i).

3 Operadores de Desplazamiento Los operadores de desplazamiento permiten realizar una manipulación de los bits de los datos. En un caso como este.in. los operadores condicionales. para construir expresiones de decisión más complejas. Esta tabla sumariza los operadores relacionales de Java: Frecuentemente los operadores relacionales se utilizan con otro juego de operadores. el segundo operando de un operador relacional no será evaluado.3. El operador && sólo devuelve true si los dos operandos son verdaderos.esto es. La siguiente línea de código utiliza esta técnica para determinar si un índice de un array está entre dos límites. != devuelve true si los dos operandos son distintos. Esta tabla sumariza los operadores lógicos y de desplazamiento disponibles en el lenguaje Java: 25 .2 Operadores Relacionales y Condicionales Los valores relacionales comparan dos valores y determinan la relación entre ellos.4. Por ejemplo. en esta situación se puede determinar el valor de && sin evaluar el operador de la derecha. 3. Así no se llamará a System. Uno de estos operadores es && que realiza la operación Y booleana . Consideremos esta sentencia: ((count > NUM_ENTRIES) && (System. Por ejemplo puedes utilizar dos operadores relacionales diferentes junto con && para determinar si ambas relaciones son ciertas. Por eso. la parte izquierda del operando de && evalúa a false. Aquí tienes tres operadores condicionales: El operador & se puede utilizar como un sinónimo de && si ambos operadores son booleanos.4. Java no evalúa el operando de la derecha. | es un sinonimo de || si ambos operandos son booleanos . para determinar si el índice es mayor que 0 o menor que NUM_ENTRIES (que se ha definido préviamente como un valor constante): 0 < index && index < NUM_ENTRIES Observa que en algunas situaciones.read() y no se leerá un carácter de la entrada estándar.in. Similarmente.read() != -1)) Si count es menor que NUM_ENTRIES.

La función "y" activa el bit resultante si los dos operandos son 1. Un desplazamiento a la izquierda es equivalente a multiplicar por dos. Un desplazamiento a la derecha de un bit es equivalente. pero más eficiente que. O inclusiva significa que si uno de los dos operandos es 1 el resultado es 1. Si quieres evaluar los valores 12 "and" 13: 12 & 13 El resultado de esta operación es 12. La función "and" activa los bits resultantes cuando los bits de los dos operandos son 1. de otra forma el resultado es 0. 26 . la representación binaria de 12 es 1100 y la de 13 es 1101. Los otros operadores realizan las funciones lógicas para cada uno de los pares de bits de cada operando. Observe que el bit situado más a la derecha desaparece. Los desplazamientos ocurren en la dirección indicada por el propio operador. La representación binaria del número 13 es 1101.Los tres operadores de desplazamiento simplemente desplazan los bits del operando de la izquierda el número de posiciones indicadas por el operador de la derecha. El resultado de la operación de desplazamiento es 110 o el 6 decimal. Por ejemplo: 13 >> 1 Desplaza los bits del entero 13 una posición a la derecha. ¿Por qué? Bien. Los dos bits de menor peso se evalúan a 0 porque al menos uno de los dos operandos es 0: 1101 & 1100 -----1100 El operador | realiza la operación O inclusiva y el operador ^ realiza la operación O exclusiva. puedes ver que los dos bits de mayor peso (los dos bits situados más a la izquierda de cada número) son 1 así el bit resultante de cada uno es 1. Entonces si colocas en línea los dos operandos y realizas la función "and". dividir el operando de la izquierda por dos.

El trabajo de una expresión se divide en dos partes: realizar los cálculos indicados por los elementos de la expresión y devolver algún valor. 3. lógicas o de bits y una operación de asignación al mismo tiempo. Las dos líneas de código anteriores son equivalentes.4.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. Java proporciona varios operadores de asignación que permiten realizar operaciones aritméticas. como esto: i = i + 2. operadores y llamadas a métodos (construida de acuerdo a la sintaxis del lenguaje) que evalúa a un valor sencillo. las expresiones se utilizan para calcular y asignar valores a las variables y para controlar el flujo de un programa Java. para asignar un valor a otro. Esta tabla lista los operadores de asignación y sus equivalentes: 3.5 Expresiones Las expresiones realizan el trabajo de un programa Java. Además del operador de asignación básico.4 Operadores de Asignación Puedes utilizar el operador de asignación =.4. Entre otras cosas. supón que quieres añadir un número a una variable y asignar el resultado dentro de la misma variable. Puedes ordenar esta sentencia utilizando el operador += así i += 2. Específicamente. Definición: Una expresión es una serie de variables. 27 .

in. esto no es cierto para todas las expresiones.read() != -1 utiliza el operador !=. Java te permite construir expresiones compuestas y sentencias a partir de varias expresiones pequeñas siempre que los tipos de datos requeridos por una parte de la expresión correspondan con los tipos de datos de la otra.read() != -1 compara dos enteros. La tabla siguiente muestra la precedencia asignada a los operadores de Java. etc. Por ejemplo. para aclarar la sentencia anterior. También habrás podido concluir del ejemplo anterior. Por ejemplo. el compilador evaluará primero y / 100.in.in. él decide basándose en la precedencia asignada a los operadores y otros elementos que se utilizan dentro de una expresión. Una expresión de llamada a un método devuelve el valor del método. La expresión count++ devuelve un entero porque ++ devuelve un valor del mismo tipo que su operando y count es un entero. Los operadores se han listado por orden de precedencia de mayor a menor.El tipo del dato devuelto por una expresión depende de los elementos utilizados en la expresión. cadenas. en la expresión anterior x + y / 100. esta expresión obtiene un resultado diferente dependiendo de si se realiza primero la suma o la división: x + y / 100 Puedes decirle directamente al compilador de Java cómo quieres que se evalúe una expresión utilizando los paréntesis ( y ).in. se podría escribir: (x + y)/ 100.read() devuelve un entero. Por ejemplo. Así System. así el tipo de dato de una expresión de llamada a un método es el mismo tipo de dato que el valor de retorno del método. La salida es siempre la misma sin importar el orden en que se apliquen las multiplicaciones.read() se ha declarado como un entero. la expresión System. En esta sentencia los operandos son System.in. no importa el orden en que se evalúe la expresión porque el resultado de la multiplicación es independiente del orden. Así x + y / 100 es equivalente a: x + (y / 100) Para hacer que tu código sea más fácil de leer y de mantener deberías explicar e indicar con paréntesis los operadores que se deben evaluar primero. el orden en que se evalúan los componentes de una expresión compuesta. El valor devuelto por != es true o false dependiendo de la salida de la comparación. por lo tanto. Sin embargo. Si no le dices explícitamente al compilador el orden en el que quieres que se realicen las operaciones.read() y -1. toma la siguiente expresión compuesta: x * y * z En este ejemplo particular. La segunda expresión contenida en la sentencia System.in. Los operadores con mayor precedencia se evalúan antes que los operadores con un precedencia relativamente menor. Como has podido ver. System. el valor devuelto por System.in.. Los operadores con una precedencia más alta se evalúan primero. El método System. Otras expresiones devuelven valores booleanos.read() es un operando válido para != porque devuelve un entero. 28 . Lo operadores con la misma precedencia se evalúan de izquierda a derecha. por eso.. Recuerda que este operador comprueba si los dos operandos son distintos. El operador división tiene una precedencia mayor que el operador suma.read() y -1. Por ejemplo.

// saluda_pepe toma el valor holaPepe La sencillez de Java en el manejo de cadenas de caracteres llega incluso más allá: si una cadena la intentamos encadenar con otro tipo de variable automáticamente se convierte la otra variable a String. String saluda_pepe = “”. int n = 5.2 Subcadenas En la clase String hay un método que permite la extracción de una subcadena de caracteres de otra. 29 .substring((int)posición_inicial. saludo = saludo + “ “ + n.// saludo toma el valor “hola 5” 3. //no inicializado String e =””. La definición de un String es: String e . en su lugar hay una clase. de tal modo que es perfectamente correcto: String saludo = “hola”.(int)posición_final). A continuación veremos algunas operaciones básicas soportadas por la clase String: 3. Su sintaxis es: Nombre_String. es decir “sumando” cadenas de caracteres obtenemos la concatenación de estas.1 Concatenación La concatenación en Java es increíblemente sencilla: se realiza con el operador +. String. //inicialización y asignación juntas.3. Lo ilustraremos con un ejemplo: String saludo = “hola”. //cadena vacía String e = “Hola”.5. que es la que soporta las distintas operaciones con cadenas de caracteres.5 CADENAS DE CARACTERES En Java no hay un tipo predefinido para cadenas de caracteres. saluda_pepe = saludo + nombre.5. String nombre = “Pepe”.

Estos métodos son estáticos. prt("saludo == saludo2 "+ saludo. sin incluir el último prt(saludo.2)). para ello se emplea el método charAt(posición). En el siguiente código aparecen ejemplos. //Concatena saludo con un espacio en blanco y con el valor de //la variable n prt(saludo +" " + n).equals(cadena2). siendo posición la posición del carácter que se desea extraer. } static void prt(String s) { System. Devuelve true si son iguales y false si son distintos.println(s). El siguiente ejemplo permitirá ilustrar estas operaciones con Strings: class Cadena { public static void main(String[] args) { String saludo = "Hola". String saludo = “hola”. int n = 5.Donde posición_inicial y posición_final son respectivamente la posición del primer carácter que se desea extraer y del primer carácter que ya no se desea extraer. en Java se distingue entre //mayúsculas y minúsculas.3 Comparación de cadenas Se empleo otro método de String: equals. Son distintos. String subsaludo = “”.// subsaludo toma el valor “ho” Puede extraerse un char de una cadena. Si se desea realizar una operación de exponenciación se deberá invocar el método correspondiente de la clase Math de Java. Su sintaxis general es: Math. Tampoco existen los operadores Seno o Coseno.out.lang.substring(0. String saludo2 ="hola".5. Subsaludo = saludo. Su sintaxis es: cadena1. Ejemplo: 30 . Si alguna vez deseamos realizar algún otro tipo de operación y queremos ver si la soporta Java podemos hacerlo consultando la ayuda on-line de la clase Math.equals(saludo2)). 3. } } 3.4 Exponenciación En Java a diferencia que en otros lenguajes no existe el operador exponenciación. //Imprime el resultado del test de igualdad entre saludo y //saludo2.metodo(argumentos). //Imprime por consola la subcadena formada por los caracteres //comprendidos entre el caractero 0 de saludo y hasta el //carácter 2.substring(0.5. por lo que no es necesario crear un objeto de dicha clase.2).

} //esto nos ahorrara teclear static void prt(String s) { System. En ambos casos se continúa ejecutando el resto del código.j=2. El grupo de sentencias se ejecuta solo si la condición toma un valor true.cos(i)).class Exponente { public static void main(String[] args) { int i = 45. 4.1. es decir una instrucción se ejecuta detrás de otra y sólo se ejecuta una vez.out.sin(i)). prt("Sen i : " + Math. En caso contrario se sigue ejecutando ignorando el Grupo de sentencias. //Imprime por consola la cadena de caracteres “Cos i : “ //concatenado con el resultado de calcular el coseno de i prt("Cos i : " + Math. else{ Grupo_n de sentencias} 31 .i)). . 4. en caso contrario se ejecuta Grupo2 de sentencias. prt("j^i : " + Math. por lo que supondremos que todos estáis familiarizados con ellas y se explicaran con poco detenimiento.0 CONTROL DE FLUJO EN JAVA El modo de ejecución de un programa en Java en ausencia de elementos de control de flujo essecuencial. Pasemos a ver sus principales tipos. .1 SENTENCIAS CONDICIONALES Ejecutan un código u otro en función de que se cumplan o no una determinada condición.1 If then Else Su modo más simple de empleo es: If(condicion) { Grupo de sentencias} Condición es una valor tipo boolean.println(s). para evitarlo se introducen estructuras de control de flujo.pow(j.Las estructuras de control de flujo de Java son la típicas de cualquier lenguaje de programación. } } 4. If(condicion) { Grupo de sentencias} else if (condicion2){ Grupo2 de sentencias} else if (condicion3){ Grupo3 de sentencias} . If(condicion) { Grupo de sentencias} else{ Grupo2 de sentencias} Si condición toma el valor true se ejecuta Grupo de sentencias. Esto nos permite hace programas muy limitados.

return result. No podemos comparar contra reales. Expliquemos su sintaxis antes de dar los motivos de esta crítica: switch(selector) { case valor1 : Grupo de sentencias1. pero no perfecto.println(test(5.1. else result = 0. int val2) { int result = 0.2 Switch Los creadores de Java trataron de hacer de este lenguaje una versión simplificada y mejorada del lenguaje de C++. System. ya que en las condiciones sólo se admite la igualdad.. int b) y que // devolverá -1 si a < b. } public static void main(String[] args) { //Imprimimos por consola el resultado de realizar unos //cuantos test. break. y así sucesivamente hasta acabarse todas las condiciones. default: statement. Además para colmo esta comparación de igualdad sólo admite valores tipo char o cualquier tipo de valores enteros menos long. incluída la del default. static int test(int val.println(test(10. Este tipo de estructura tiene sus posibilidades muy limitadas. System. se le sacaría partido a esta sentencia si aceptase desigualdades). case valor4 : Grupo de sentencias4.. break. +1 si a > b y 0 si a == b. 5)). Si el valor coincide se ejecuta su respectivo grupo de secuencias. case valor2 : Grupo de sentencias2. if(val > val2) result = +1. 10)). Si no se cumple ninguna se ejecuta Grupo_n de sentencias. break. Ha llegado el momento de justificar la crítica hecha a los creadores de Java. Prueba de ello es esta sentencia: está tan poco flexible como en C++. break. 32 . si condicion2 toma el valor true se ejecuta Grupo2 de sentencias.... Su trabajo fue bastante bueno. case valor5 : Grupo de sentencias5.out.out. } } 4. por poner un caso. System. Ilustraremos esto con el siguiente ejemplo: class Ejemplo1 { // Método que podremos invovar como test(int a.Si condición toma el valor true se ejecuta Grupo de sentencias. strings. break.println(test(5. Si no se encuentra ninguna coincidencia se ejecutan las sentencias de default. no ingún otro tipo de condición (sería fácil pensar ejemplos dónde. En ambos casos se continúa ejecutando el resto del código. } Se compara el valor de selector con sentencias_n. Si no se pusieran los break una vez que se encontrase un valor que coincida con el selector se ejecutarían todos los grupos de sentencias. 5)). // .. Este último else es opcional. case valor3 : Grupo de sentencias3. else if(val < val2) result = -1.out..

out. „a‟ = 97.out. Su sintaxis es: while(condición){ Grupo de sentencias} Ilustramos su funcionamiento con un ejemplo: 33 . default: //Si no era ninguna de las anteriores imprimimos consonante. i < 100.random() * 26 + 'a').out.random()*26 será un número real aleatorio entre 0 y //26. break. Esto es en muchas ocasiones fuente de errores. i++) { //Math. //Se transforma el número aleatorio entre 97y 97 + 26 en el //carácter correspodiente a su parte entera.2. de hecho en el ejemplo que empleamos para ilustrar esta sentencia aprovechamos esta característica: class Ejemplo2 { public static void main(String[] args) { //Bucle for.print(c + ": "). for(int i = 0.println("consonante"). aunque también hay que reconocer que a veces se le puede sacar partido. „e‟. System. „a‟ el carácter se transforma a //un enteroy se le suma. que por la disposición de los caracteres Unicode //será un letra del abecedario. //Math. 4.random() es un métod estático que genera un número real //aleatorio entre 0 y 1. System.2 BUCLES Son instrucciones que nos permiten repetir un bloque de código mientras se cumpla una determinada condición.1 Bucle while Cuando en la ejecución de un código se llega a un bucle while se comprueba si se verifica su condición. } } } } 4.println("vocal"). si se verifica se continua ejecutando el código del bucle hasta que esta deje de verificarse.También se le podría criticar el hecho de que una vez cumplidas una condición se ejecuten todas las sentencias si no hay instrucciones break que lo impidan. char c = (char)(Math. Será un carácter //aleatorio. switch(c) { case 'a': case 'e': case 'i': case 'o': case 'u': //Si el carácter es „a‟. „o‟ o „u‟ imprimimos //vocal. Pasemos a ver sus tipos. System. Ejecutará 100 veces el código que tiene dentro. Al sumarle un carácter. „i‟.

2.99 sigue ejecutando el cuerpo del bucle. System. }while(condición). c < 128.random(). } while(r < 0. por lo que tenemos garantizado que el código se va a ejecutar al menos una vez.random(). Expresion2 es la condición que se le impone a la variable del bucle y expresion3 indica una operación que se realiza en cada iteración a partir de la primera (en la primera iteración el valor de la variable del bucle es el que se le asigna en expresion1) sobre la variable del bucle. }}} 4.3 Bucle for Su formato es el siguiente: for(expresion1. } } 4. //Idéntico al ejemplo anterior. 34 . sólo que aquí la condición va al final del código del bucle. la variable-condición del bucle. Obsérvese como el ejemplo anterior implementado mediante un bucle independientemente del valor de r ejecutará al menos una vez el código de su cuerpo: do while class Ejemplo4 { public static void main(String[] args) { double r.expresion2.println(r).expresion3){ Grupo de sentecias.out. System. solo que ahora la condición //está al final del bucle. La sintaxis de do while es: do { Grupo de sentencias.2. class Ejemplo5 { public static void main(String[] args) { for( char c = 0. No es necesaria while(r < 0.99d) { //Genera un nuevo r aleatario entre 0 y 1. Dependerá del caso concreto si es más conveniente emplear un bucle while o do while. r = Math.class Ejemplo3 { public static void main(String[] args) { double r = 0. (int)c es el entero //correspondiente al carácter c. //Lo imprime por consola.2 Bucle do while Su comportamiento es semejante al bucle while.} Expresion1 es una asignación de un valor a una variable. do { r = Math.out.println(r).99d). c++) //Imprime los caracteres correspondientes a los números //enteros comprendidos entre 0 y 128. //Mientras que r < 0. //La d significa double.

Cuando se llama a un procedimiento ( que en POO se denominó método) al encontrarse con una sentencia return se pasa el valor especificado al código que llamó a dicho método y se devuelve el control al código invocador. Podíamos haber esperado ha hablar de métodos para introducir esta sentencia. ya que es la sentencia que le permite devolver al método un valor. Su misión tiene que ver con el control de flujo: se deja de ejecutar código secuencialmente y se pasa al código que invocó al método.3 RETURN Sus funciones son las mismas que en C++. Observaremos el funcionamiento de ambos en un mismo ejemplo: class Ejemplo6 { public static void main(String[] args) { for(int i = 0. pero hemos decidido introducirla aquí por tener una función relacionada. entre otras cosas.out.out.2. } } 4.System. pero en esta ocasión no se sale del bucle. int j = i * 27. El encontrarse una sentencia break en el cuerpo de cualquier bucle detiene la ejecución del cuerpo del bucle y sale de este. // teminamos aqui el bucle //Salto a la siguiente iteración si i no es divisible entre 9 if(i % 9 != 0) continue. i < 100. // Salimos del lazo if(i%10 != 0) continue. i++) { if(i == 74) break. sino que se pasa a la siguiente iteración de este.//Salto a la siguiente iteración System. En el ejemplo 1 ya se ha ejemplificado su uso. con control de flujo. // Lazo infinito del cual se sale con break: while(true) { i++. continuándose ejecutando el código que hay tras el bucle. if(j == 1269) break. } } } 4.4 Break y continue No se tratan de un bucle. 35 .println(i). } int i = 0. //Si I es divisible entre 9 se imprime System.out. pero sí de una sentencia íntimamente relacionada con estos. Esta sentencia también se puede usar para forzar la salida del bloque de ejecución de una instrucción condicional (esto ya se vio con switch).println(i). Continue también detiene la ejecución del cuerpo del bucle.println("valor: " + (int)c +" caracter: " + c). Esta sentencia también está profundamente relacionada con los métodos.

Pero bien. que dialogan entre ellos pasándose mensajes para llegar a resolver el problema en cuestión. La única posibilidad de repartir trozos de código relativamente independientes entre programadores son los procedimientos. totalmente desconocida para los demás objetos (a veces no es así. Por ser desconocida para los demás objetos podemos en cualquier momento modificarla sin que a los demás les importe. Esta segunda parte es. Un mensaje es la invocación de un método de otro objeto.2 CLASES Y HERENCIA Una clase es la “plantilla” que usamos para crear los objetos. y que no podremos cambiar en el futuro sin modificar los demás objetos (sí es posible añadir nuevos métodos para dar nuevas funciones al objetos sin modificar los métodos ya existentes). simplificándole además la tarea al programador. aquí un programa es un montón de objetos. a excepción de los tipos básicos de variables enteras. que ha de ser conocida por los demás. independientes entre si. programación orientada o objetos (en la literatura suele aparecer como OOP.0 OBJETOS Y CLASES Como ya hemos comentado Java es un lenguaje totalmente orientado a objetos.. y en lo único que tendrán que ponerse de acuerdo entre ellos es en los mensajes que se van a pasar. y además cada programador tendrá total libertad para llevarla a cabo como él considere oportuno. Aquí un programa no es un código que llama a procedimientos. Un objeto que se crea a partir de una clase se dice que es una instancia de esa clase. pero es lo ideal en un buena OOP). Aquí simplemente daremos unas nociones muy básicas de programación orientada a objetos que nos permitan empezar a adentrarnos en el mundo de Java. Todos los objetos pertenecen a una determinada clase. y esta era la única capacidad de reuso de código posible. y al final hay que juntar todos estos con el programa central que los llama. hay libros enteros escritos sobre objetos y metodologías de programación orientada a objetos sin abordar ningún lenguaje de programación en concreto . los trozos de código que se podían emplear en varias ocasiones a lo largo del programa (reusar) se escribían en forma de procedimientos que se invocaban desde el programa. ¿qué es un objeto? y ¿qué es la programación orientada a objetos?..1 INTRODUCCIÓN En los años 60 la programación se realizaba de un modo “clásico” (no orientado a objetos). Un método es muy semejante a un procedimiento de la programación clásica: a un método se le pasan uno. mucho más que. Si hay que repartir un programa de grandes dimensiones entre varios programadores a cada uno se le asignan unos cuantos objetos. 5.. la forma en que un programador implemente sus objetos no influye en absoluto en lo que los demás programadores hagan.5. sólo le importa a que mensajes es capaz de responder. 5. Las distintas clases tienen distintas relaciones de herencia entre si: una clase puede 36 . en principio. siendo frecuente encontrar problemas al unir estos trozos de código. que variables usa. reales y char. por ejemplo. Según los códigos se fueron haciendo más grandes y complejos este estilo de programación se hacía más inviable: es difícil programar algo de grandes dimensiones con este estilo de programación. La OOP permite abordar con más posibilidades de éxito y con un menor coste temporal grandes proyectos de software. 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. A un objeto no le importa en absoluto como está implementado otro objeto. Responder a estas preguntas no es en absoluto trivial. C++. varios o ningún dato y nos devuelve un dato a cambio. En Java todo es un objeto. Esto es así gracias a que los objetos son independientes unos de otros (cuanta mayor sea la independencia entre ellos de mayor calidad serán). que código tiene o deja de tener. Un programa era un código que se ejecutaba. Object Oriented Programing). En los años 70 se empezó a imponer con fuerza otro estilo de programación: POO. 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.

out. } Los campos que van entre corchetes son optativos. En cuanto al contenido del último corchete ya se explicará más adelante su significado.out. Por motivos de tiempo no abordaremos en este curso que es un package. } public void get_edad(){ System. String nombre.1. Como nunca trabajaremos en varios directorios asumiremos que la ausencia de modificador es equivalente a que la clase sea pública. public void nace(){ System. Ninguno: La clase es “amistosa”. } } 5. En general en lo que al alumno respecta se recomienda ignorar.println("Hola mundo"). nombre_clase_padre es el nombre de la clase padre. Será accesible para las demás clases del package.println(nombre).1 Definición de una clase La forma más general de definición de una clase en Java es: [Modificador] class nombre_clase [extends nombre_clase_padre] [implements interface] { Declaración de variables.println(edad). En Java todas las clases tienen como primer padre una misma clase: la clase Object. de la cual hereda los métodos y variables. sin embargo mientras todas las clases con las que estemos trabajando estén en el mismo directorio pertenecerán al mismo package y por ello serán como si fuesen públicas. al menos durante este curso.derivarse de otra. en ese caso la clase derivada o clase hija hereda los métodos y variables de la clase de la que se deriva o clase padre. abstract: Se trata de una clase de la cual no se puede instanciar ningún objeto. Por motivos de simplicidad y dada la corta duración de este curso ignoraremos la existencia del concepto de package en la explicación de los siguientes conceptos. Sólo podemos tener una clase public por unidad de compilación. Vamos a continuación a profundizar en todos estos conceptos y a explicar su sintaxis en Java.2. observar que tiene métodos con el mismo nombre: 37 .out. Según los parámetros que se le pasen se invocará a uno u otro método: Ejemplo: Modificaremos un poco la clase animal anterior. final: Indicará que esta clase no puede “tener hijo”.2. Veamos que opciones tenemos: 5. Los modificadores indican las posibles propiedades de la clase. o haremos breves referencias a este concepto sin dar demasiadas explicaciones.2. 5.1. toda referencia al término “package”.2 Sobrecarga de métodos Java admite lo que se llama sobrecarga de métodos: puede haber varios métodos con el mismo nombre pero a los cuales se les pasan distintos parámetros. } public void get_nombre(){ System. Ejemplo: class Animal{ int edad. nombre_clase es el nombre que le queramos dar a nuestra clase.1 Modificadores de clases public: La clase es pública y por lo tanto accesible para todo el mundo. Declaración de métodos. no se puede derivar ninguna clase de ella. aunque es posible no tener ninguna.

} public void get_nombre(){ System. Ejemplo: Veamos la clase Animal y sus constructores. } public void get_nombre(){ System. String nombre.out.out.).println(nombre +" " +edad). class Animal{ int edad. Cuando creamos un objeto (ya se verá más adelante como se hace) podemos invocar al constructor que más nos convenga.2.println(edad). public void nace(){ System.. } public void get_edad(){ System..out. } public void get_nombre(int i){ System. nombre = _nombre.println("Hola mundo").class Animal{ int edad.println("Hola mundo").out..out. no siendo necesario indicar que el tipo de dato devuelto es void. un constructor admite sobrecarga. abrir un archivo o una conexión de internet.1. } public void get_edad(){ System.println(nombre +" " +edad). } public void get_nombre(int i){ System.out.println(nombre).println(nombre). String nombre.out. public Animal(){ //constructor por defecto } public Animal(int _edad. } } 5..println(edad). } public void nace(){ System. Los constructores se emplean para inicializar los valores de los objetos y realizar las operaciones que sean necesarias para la generación de este objeto (crear otros objetos que puedan estar contenidos dentro de este objeto.3 Constructores Constructores son métodos cuyo nombre coincide con el nombre de la clase y que nunca devuelven ningún tipo de dato. String _nombre){ //constructor con 2 argumentos edad = _edad.out. } } 38 . Como cualquier método.

} 39 . Para nosotros será equivalente a que fuese público. es un método al cual se puede invocar sin crear ningún objeto de dicha clase.2 Modificadores de un método public: Pública. Si una instancia lo modifica todas ven dicha modificación. Más adelante se explicará que es esto. nombre = _nombre.2.2. Math. Este es un poderoso mecanismo para la reusabilidad del código. 5.1 Modificadores de variables public: Pública. Ninguno: Es “amistosa”. puede acceder todo el mundo a esa variable.5. se trata de un método que no podrá ser cambiado por ninguna clase que herede de la clase donde se definió. protected: Protegido. 5. Podemos heredar de una clase.2. por lo tanto. sólo pueden acceder a ella las clases hijas de la clase que posee la variable y las que estén en el mismo package.2. Veamos un ejemplo: class Animal{ protected int edad.2. esta variable es la misma para todas las instancias de una clase. Desde un método estático sólo podemos invocar otros métodos que también sean estáticos. hereda todos sus métodos y variables. Para nosotros erá equivalente a que fuese pública. Math. protected: Protegida. sólo pueden acceder a ella las clases hijas de la clase que posea el método y las que estén en el mismo package.cos son dos ejemplos de métodos estáticos. String nombre.2 Modificadores de métodos y variables Antes de explicar herencia entre clases comentaremos cuales son los posibles modificadores que pueden tener métodos y variables y su comportamiento: 5. todas comparten ese dato. final: Final. public Animal(){ } public Animal(int _edad.sin. puede acceder todo el mundo a este método. private: Privada.3 Herencia Cuando en Java indicamos que una clase “extends” otra clase estamos indicando que es una clase hija de esta y que. nadie salvo la clase misma puede acceder a estos métodos. Es un método que no se puede “sobrescribir”. static: Estática. Pueden acceder a ella todas las instancias de la clase (cuando decimos clase nos estamos refiriendo a todas sus posibles instancias) static: Estática. Ninguno: Es “amistoso”. String _nombre){ edad = _edad. puede ser accedida por cualquier miembro del package. nadie salvo la clase misma puede acceder a estas variables. y luego añadir lo que necesitemos o modificar lo que no se adapte a nuestros requerimientos. puede ser accedida por cualquier miembro del package.2. private: Privada. por lo cual partimos de su estructura de variables y métodos.

inicializarlo es darle un valor a estas variables. mes = 1.out. Un objeto en el ordenador es esencialmente un bloque de memoria con espacio para guardar las variables de dicho objeto.out. } Perro(int edad.out. super(edad.public void nace(){ System. } //Método estático que recibe un objeto de tipo perro e //invoca a su método get_edad() static void get1(Perro dog){ //El método get_edad() no está definido en perro. } public void get_nombre(){ System. anio = 1900.get1(dog).out.println(edad).println(nombre +" " +edad).get_edad().nombre). } public void get_edad(){ System. } } 5. //Invocamos al método estátioco get1 pasándole el objeto // Perro. lo ha //heredado de animal.2. } } public class Perro extends Animal{ Perro(){ //constructor vacio edad = 0.mes.ano.println(nombre).4 Creación y referencia a objetos Aunque ya hemos visto como se crea un objeto vamos a formalizarlo un poco. Para crear un objeto se utiliza el comando new. dog. } public static void main (String[] args){ //Creamos un objeto de tipo perro Perro dog = new Perro(8."Bambi"). } 40 . Fecha(){ dia=1. String nombre){ //Esta sentencia invoca a un constructor de la clase padre. Crear el objeto es sinónimo de reservar espacio para sus variables.println("Hola mundo"). } public void get_nombre(int i){ System. Veámoslo sobre un ejemplo: class Fecha{ int dia. nombre ="Tobi".

5 this Es una variable especial de sólo lectura que proporciona Java.dia = 8. 41 . mes = nmes. ano = nano. numero_cuenta = a.Fecha (int _dia.2005). reservando espacio para sus variables.. para acceder a ella tendríamos que hacerlo mediante métodos que nos devolviesen su valor. this(n. } } De este modo no podríamos acceder a las variables de la clase fecha. Fecha(){ dia=1. Con el primer comando hemos creado un puntero que apunta a una variable tipo fecha.2. Con esta sentencia creamos una variable que se llama un_dia con valor 8-12-2005.ano. Una vez creado un objeto será posible acceder a todas sus variables y métodos públicos. ano = 1900. A esto se le denomina encapsulación. int _mes. nano){ dia= ndia.nuevo_numero()). así por ejemplo en el ejemplo anterior un_dia. Si la variable fuese privada solo podrían acceder a ella sus instancias: class Fecha{ private int dia.. Fecha un_dia = new Fecha(8. mes = 1. } Fecha (int ndia.. nmes.12. como está sin inicializar apuntara a null. Con el segundo inicializamos el objeto al que apunta hoy. A veces es útil que un objeto pueda referenciarse a si mismo: class Cliente{ public Cliente(String n){ //Llamamos al otro constructor. hoy = new Fecha(). . El constructor las inicializará. Cuenta. tomando el objeto hoy el valor 1-1-1900. El empleo de this ha de ser //siempre en la primera línea dentro del constructor. mes = _mes. } public Cliente (String n. ano = _anio.. Contiene una referencia al objeto en el que se usa dicha variable. int a){ nombre = n. 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 métodos. int _anio){ dia= _dia.mes. 5. } } Fecha hoy.

3 INTERFACES En Java no está soportada la herencia múltiple. Una interface es formalmente como una clase. 5. public Animal(int edad.edad =edad. y a la hora de definirla en vez de emplear la palabra clave “class” se emplea “inteface”.} .nombre=nombre. llamar al constructor de la clase padre. } Otro posible uso de this. void hablar(){ if(gente_presente) //Invoca al método sobreescrito de la clase padre super.edad = variable del objeto perro //edad = variable definida sólo dentro del constructor this. else System. no hacen nada.out.out.println("Hola"). public String nombre = "Bob". } } class GatoMagico extends Gato { boolean gente_presente. esto es. } } Uno de los principales usos de super 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 daría una mayor potencia al lenguaje de programación.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. con dos diferencias: sus métodos están vacíos. que ya se ha visto en ejemplos anteriores es diferenciar entre variables locales de un método o constructor y variables del objeto..2. Veámoslo con un ejemplo: interface Animal1{ public int edad = 10. como ya se empleó en un ejemplo..println("Miau"). } 5. sin embargo los creadores de Java decidieron no implementar la herencia múltiple por considerar que esta añade al código 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 múltiple sus creadores introdujeron un nuevo concepto: el de interface. String nombre){ //this.hablar(). this. 42 .

cuando una clase implementa una interface lo que estamos haciendo es una promesa de que esa clase va a implementar todos los métodos de la interface en cuestión. Para que un método sobrescriba a otro ha de tener el mismo nombre. public void get_nombre().println(nombre ). public String nombre = "Bob".println(nombre +" " +i). } ***************************************************** public class Perro1 implements Animal1{ Perro3(){ get_nombre(). void get_nombre(int i). public void nace(){ System. } public void get_nombre(){ System. void get_nombre(int i). public void get_nombre(). Si la clase que implementa la interface no “sobrescribiera” alguno de los métodos de la interface automáticamente esta clase se convertiría en abstracta y no podríamos crear ningún objeto de ella. 43 . Además no pueden llevar modificadores private ni protected.edad = 8. Bien. Veámoslo con un ejemplo de una clase que implementa la anterior interface: interface Animal1{ public int edad = 10.out.println("hola mundo"). } Cabe preguntarnos cual es el uso de una interface si sus métodos están vacíos. //Compruébese como esta línea da un error al compilar debido //a intentar asignar un valor a una variable final // dog. ha de devolver el mismo tipo de dato y ha de tener el mismo modificador que el método al que sobrescribe. } //Compruébese como si cambiamos el nombre del método a nac() //no compila ya que no henos sobreescrito todos los métodos //de la interfaz. } public static void main (String[] args){ Perro1 dog = new Perro1(). } public void get_nombre(int i){ System.public void nace(). } } Las variables que se definen en una interface llevan todas ellas el atributo final.out. se le han de pasar los mismos datos. Su función es la de ser una especie de constantes para todos los objetos que implementen dicha interface. get_nombre(8). public void nace(). Si no tuviera el mismo modificador el compilador nos daría un error y no nos dejaría seguir adelante.out. sólo public. y es obligatorio darles un valor dentro del cuerpo de la interface.

} public void get_nombre(int i){ System. en la que solo s defina una forma generalizada que será compartida por todas las subclases.out.out. dejando que cada subclase complete los detalles necesarios. // en las clases abstractas se permiten métodos concretos void callmetoo() { 44 . } ********************************* public class Perro2 implements Animal1. es decir. //edad = 10. } ********************************** interface Animal3{ void get_nombre(int i). // Un ejemplo sencillo de una clase abstracta. public String nombre = "Bob".println(nombre ).println(nombre +" " +i). } ********************************* interface Animal2{ public void get_nombre().Animal3{ Perro2(){ get_nombre(). no podemos cambiar este valor } public void nace(){ System. get_nombre(8). abstract class A { abstract void callme(). } public static void main (String[] args){ Perro2 dog = new Perro2().5 Clases Abstractas: Cuando se quiere definir una superclase en la que se declare la estructura de una determinada abstracción sin implementar completamente cada método.println("hola mundo"). public void nace(). } public void get_nombre(){ System.Por último decir que aunque una clase sólo puede heredar propiedades de otra clase puede implementar cuantas interfaces se desee.Animal2.out. Veamos un ejemplo de múltiples interfaces interface Animal1{ public int edad = 10. }} 5. recuperándose así en buena parte la potencia de la herencia múltiple.

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

Triangulo(double a, double b) { super(a, b); } // sobreescribiendo el area para un triangulo double area() { System.out.println("Dentro del método 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 método 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 ningún objeto. Se pueden declarar tanto

46

métodos 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 Tamaño de un arreglo
El tamaño de una matriz, el numero de elementos se encuentra en su variable de instancia length. // Demostración 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 métodos que pueden ser utilizados entre ellos están:  Se puede comprobar la igualdad de dos cadenas mediante equals().  Se puede obtener la longitud de una cadena mediante length().  Se puede obtener el carácter que ocupa una posición 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 información a un programa que se esta ejecutando. Esto se lleva a cabo pasando los argumentos de la línea de ordenes a main().

// Presentación 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 Introducción
Un applet es un programa en Java que se incrusta en una página HTML para agregarle funcionalidad. Como por seguridad los applets están limitados a no poder accesar el medio ambiente de la computadora donde corren su principal utilidad es para hacer interfaces gráficas de aplicaciones de bases de datos. Por supuesto la base de datos debe residir en la computadora de donde vino el applet. También se pueden programar juegos, programas interactivos que complementen un texto en línea 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 método 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 método init(). • Comienzo. Se hace cuando el applet comienza a correr, ya sea después del inicio o si fué detenido. Corresponde al método start(). • Detención. Un applet se detiene cuando el usuario accesa otra página en su navegador. Corresponde al método stop(). • Destrucción. Permite liberar recursos al finalizar el applet. Corresponde al método destroy(). • Dibujo. Actualiza la salida del applet en la pantalla. Corresponde al método 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 después de estar detenido. Es importante recalcar que ninguno de estos métodos es invocado en forma explícita. El navegador los invoca en forma automática 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 vacías de los 5 métodos. Es responsabilidad del programador de applets sobreponer los métodos que vaya a utilizar. No es necesario sobreponer todos, en la práctica casi siempre solo se sobreponen o el método 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

Esta etiqueta puede tener varios argumentos.. La etiqueta <PARAM> va dentro de la etiqueta <APPLET>.applet.*.awt. pero se pueden ejecutar applets remotos poniendo la dirección http completa. Font.awt. para. public void init () { 51 . la cual tiene un atributo para el nombre del argumento y otro para el valor.BOLD. • HEIGHT – Es la altura de dicha área en pixeles. import java. Este método 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. • WIDTH – Indica el ancho en pixeles del área dentro de la página HTML que el navegador apartará para uso del applet. Los argumentos se pasan cuando el applet se inicializa. import java.3 Argumentos Los applets pueden recibir argumentos desde el archivo HTML con la etiqueta <PARAM>. 6. 36). import java. Ejemplo:Un applet que lee tres argumentos y escribe un mensaje en la pantalla. import java. Dentro del método init() se puede recuperar los valores de los argumentos usando el método getParameter(). public class EjemploArgumento extends Applet { Font f = new Font ("TimesRoman".awt. los principales son: • CODE – Indica el nombre del archivo compilado. al centro (Center) o a la derecha (Right).Font.Graphics. Si el archivo de clase está en la misma carpeta que la página HTML (lo más recomendable) basta con poner el nombre. String desde.Color.Como se puede ver en el listado anterior la etiqueta <APPLET> es la que incrusta el applet en la página HTML. mssg. • ALIGN – Permite alinear el applet a la izquierda (Left).

g. g. 5.setColor (Color.drawString ("Desde " + desde + " para " + para. mssg = getParameter ("mensaje"). } public void paint (Graphics g) { g. } } Codigo html: <HTML> <HEAD> <TITLE>Ejemplo con Argumentos !!!</TITLE> </HEAD> <BODY> <P><H2>Bienvenido a Programacion III:</H2><P> <APPLET CODE="EjemploArgumento.drawString ("El mensaje es: " + mssg.green). if (desde == null) desde = "aqui".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 . if (para == null) para = "alla".red).setFont (f). 5.setColor (Color. g. 40). g. 80).desde = getParameter ("desde"). if (mssg == null) mssg = "no hay mensaje". para = getParameter ("para").

Por último largo_grados indica la longitud del arco y la dirección de dibujo.0) está en la esquina superior izquierda del área reservada para el applet en la página HTML. Si ancho es igual que alto. • drawPolyline (x[]. Un valor de 90 significa que se dibujará un cuarto de arco en contra de las manecillas del reloj. Los argumentos son los mismos que en el método anterior. ancho. y[]. La forma más fácil de entender el resultado de este método es imaginar que se dibuja un rectángulo imaginario con una anchura de ancho pixeles y una altura de alto pixeles. • drawArc (x. Esta clase tiene métodos para dibujar líneas. x.6. alto) . el 90 al norte. En los applets el método paint recibe un objeto de tipo Graphics. Los arreglos x y y guardan respectivamente las coordenadas x y y de cada una de las esquinas del polígono. La clase Graphics tiene los siguientes métodos. el punto (0. y. El origen. y) indica la coordenada donde comienza el texto. Los argumentos son los mismos que en el método anterior. • drawRect (x. Los argumentos son los mismos que en el método anterior. y) y que el óvalo se dibuja inscrito en el rectángulo. ancho. y) y la anchura y altura por ancho y alto respectivamente. • fillRect (x. el punto (x. alto) – Dibuja un rectángulo hueco. alto. Los argumentos r_hor y r_ver determinan que tan lejos de los limites del rectángulo debe comenzar el redondeo en las esquinas. • drawLine (x1. Los primeros 4 argumentos tienen el mismo sentido que en los dos métodos anteriores. • drawRoundRect (x. • drawPolygon (x[]. • fillOval (x. el 180 al oeste y el 270 al sur. y2) – Dibuja una línea recta entre los puntos (x1. La X crece hacia la derecha en forma horizontal y la Y crece hacia abajo en forma vertical. largo_grados) – Dibuja un arco hueco. círculos.Dibuja un rectángulo lleno con color actual. Los primeros 4 argumentos tienen el mismo sentido que en los dos métodos anteriores. rectángulos. n) – Dibuja un polígono hueco. y[]. y[]. y. y. y. Al dibujar sobre este objeto se dibuja dentro del applet y los resultados se desplegarán en la pantalla. r_ver) – Dibuja un rectángulo lleno con el color actual y redondeado en las esquinas. y. ancho. con la esquina superior izquierda en el punto (x. El argumento grados_ini indica desde que grado se comienza a dibujar el arco. y) – Escribe el texto almacenado en el string s. Si pensamos en los puntos cardinales el grado 0 está al este. El polígono se cierra en forma automática porque siempre se dibuja una línea entre el último punto y el primero. • fillPolygon (x[]. r_hor. x2. • drawOval (x. Hay que aclarar que todos los argumentos son de tipo int. y) está fuera del óvalo. • fillRoundRect (x. El resultado se puede pensar que es un polígono sin cerrar. La esquina superior izquierda está dada por el punto (x. ancho. r_hor. y1.4 Gráficas La librería de Java incluye la clase Graphics para hacer dibujos sencillos. El sistema de coordenadas está en pixeles. Por lo tanto. ancho. es decir. n) – Dibuja una línea quebrada. en cambio un valor de –90 también indica un cuarto de arco pero en el sentido de las manecillas del 53 . grados_ini. ancho. y. alto. ancho. r_ver) – Dibuja un rectángulo hueco con las esquinas redondeadas. polígonos y arcos. el rectángulo se convierte en cuadrado y el óvalo se convierte en círculo. • drawString (s. y2). El punto (x. alto. alto) – Dibuja un óvalo hueco. El argumento n es el número de puntos (el tamaño de los arreglos x y y). y. n) – Dibuja un polígono lleno con el color actual. y1) y (x2. alto) Dibuja un óvalo lleno con el color actual.

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

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

El (0.6. g y b es la cantidad de rojo.drawString ("Circulo lleno".drawString ("Ovalo lleno".drawOval (150. Lo primero es crear una instancia de la clase Image. Cambia el color de lo que se haya dibujado sin importar el color en el que se dibujó.red. 20). g.0. } } 6. g.black. g. g. 240.setColor (Color. 100. public class CambioColor extends Applet { public void paint (Graphics g) { Color c1 = new Color (120. 130.orange. Cambia el color del fondo al color c. Color. 60). Color.6 Color Para cambiar el color hay que crear una instancia de la clase Color: Color c = new Color (r. 5. import java. Cambia el color actual para las operaciones de dibujo a partir de este momento. 60). g. g. verde y azul respectivamente.awt.fillOval (150. Image mi_imagen = getImage (URL.yellow. donde r.Color. • setForeground (c). 120). etc. Hay 3 funciones para cambio de color: • setColor (c). Color.7 Imágenes Un applet pueden desplegar imágenes en formato GIF y JPEG. El argumento c es un objeto de tipo Color. 30.blue.0) representa el negro y el (255. 5.awt. 60). Color. 120). g.Graphics. 150.setColor (c1). 60. 220). setBackground (Color. nombre).777. g.white. 30. 150.drawString ("Ovalo hueco". 60.gray. 30.fillOval (5. En total hay 256 3 = 16.red). import java. Para la variable URL hay 3 opciones: 56 .216 colores distintos.drawString ("Circulo hueco".lightGray). Color. g. b). 100. 60). g. g. g. 130. Color c2 = new Color (40.*.drawOval (5. Color. donde URL es la dirección donde está la imagen y nombre es el nombre del archivo que contiene la imagen. • setBackground (c).applet. 20). 255. Ejemplo: import java. 255) el blanco. en el rango de 0 a 255.setColor (c2). 200). Se puede usar alguno de los colores ya definidos en la clase Color como Color.

5).5).com/images/foo.Graphics. this).awt.getHeight (this). int alto = img.gif"). Las variables ancho y alto indica el ancho y el alto en pixeles con que se va a desplegar la imagen.awt.drawImage (img. Para desplegar la imagen: drawImage (imagen. import java. "disco. ypos. import java.drawImage (img. b) Usar la dirección relativa con respecto a donde está la página HTML. donde). "images/foo. ypos. // 50 % xpos += (ancho / 4) + 10. Por último. (int) (alto * 1. ancho / 2.gif"). this). // 100 % xpos += (ancho / 2) + 10.drawImage (img. Ejemplo: Para una imagen que se llama disco. ypos.com/images".class). alto / 4. xpos. this). ancho. g. alto.gif").5 + 10). "foo. donde imagen es un objeto de tipo Image. xpos. 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. (int) (ancho * 1.*.applet. public void init () { img = getImage (getCodeBase (). } } 57 . int xpos = 10. y. ypos. xpos. g.foo. ancho / 4. this).5). x.a) Poner la dirección absoluta.foo. x y y son las coordenadas de la esquina superior izquierda de la imagen. (int) (alto * 1. Si el directorio images está abajo de donde está el código objeto. } public void paint (Graphics g) { int ancho = img.gif"). g. ancho / 2. ypos = 10. alto / 2.drawImage (img. g.getWidth (this). // 150 % xpos += ancho + 10. public class Imagenes extends Applet { Image img. "images/foo.gif import java. Si el directorio images está abajo de donde está la página. entonces poner: Image imagen = getImage (getDocumentBase (). this). entonces poner: Image imagen = getImage (getCodeBase (). // 25 % g.Image. xpos. Si la imagen esta en: http://www. xpos. // distorsionado xpos += (int) (ancho * 1. ypos.gif.drawImage (img. entonces poner: Image imagen = getImage (new URL ("http://www. c) Usar la dirección relativa a donde está el applet compilado (archivo .

lbl3. se genera un evento de acción que se envía a cualquier 58 . Label. add (lbl2). import java. Label. add (lbl3). Label. Label(String str).RIGHT). Label(String str.*. si queremos obtener el texto de una etiqueta se utiliza el metodo getText().CENTER Se puede establecer o cambiar el texto de la etiqueta con el método setText().0 Abstract Window Toolkit y Eventos 7.setText("Etiqueta tres"). Para definir un botón hay que crear un objeto de tipo Button indicando el texto que desplegará el botón: Constructores: Button(). int how) El valor de how debe ser cualquiera de estas constantes: Label. }} 7.LEFT.2 Gestion de Botones Un botón es un componente gráfico que activa algún evento cuando se le oprime usando el ratón.7. Button(String str) Una vez creado el boton se le puede asignar un Label llamando a setLabel().CENTER). Label lbl2 = new Label ("Etiqueta dos". Para definir una etiqueta hay que crear un objeto de tipo Label indicando el texto de la etiqueta y opcionalmente una alineación: Constructores: Label().1 Etiquetas Una etiqueta es un texto no editable que despliega un letrero en la pantalla. public class LabelApplet extends Applet { Label lbl = new Label ("Etiqueta uno").setAlignment(Label. Se puede establecer el alineamiento del string dentro de la etiqueta utilizando el método setAlignment(). add (lbl). public void init () { lbl3. Para obtener el alineamiento de la etiqueta se utiliza el método getAlignment() Ejemplo: import java.awt.*.RIGHT. Label lbl3 = new Label(). tambien se puede obtener su etiqueta con getLabel() Gestionar Botones: Cada vez que se pulsa un botón.applet.

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

boolean on).7.*. CheckboxGroup (String str. import java. add (cbc). Checkbox cbext = new Checkbox ("extranjero". false. public void init (){ add (lb1). hay que llamar a getState(). Checkbox cbsal = new Checkbox ("salvadoreño". } } 60 . Para establecer un determinado estado. CheckboxGroup cbg1 = new CheckboxGroup (). Los checkbox se pueden utilizar individualmente o como parte de grupo: Constructores: Checkbox(). Checkbox(String str). add (cbs). Para obtener el estado inicial de un checkbox. Checkbox(String str. CheckboxGroup cbgroup).3 Checkboxes Un checkbox es un componente que tiene dos estados: marcado o no marcado. Para marcar (o desmarcar) desde un programa un checkbox se usa el método setState() pasando un argumento booleano que indique el nuevo estado. false. // desmarca el checkbox cb1. Se puede obtener la etiqueta asociada al checkbox llamando al método getLabel().awt. boolean on. Los checkboxes se pueden agrupar de tal forma que solo un checkbox de ese grupo pueda estar activado a la vez. add (lb2). Checkbox cbv = new Checkbox ("viudo". cbg2). add (cbsal). true. Llamado al método setLabel() se establece la etiqueta. true.*. 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. cbg1). boolean on). cb1. public class Checkbox1Applet extends Applet { Label lb1 = new Label ("Estado civil"). Ejemplo: dos grupos de checkbox import java. Checkbox cbc = new Checkbox ("casado".applet. se puede llamar a setState(). Label lb2 = new Label ("Nacionalidad").setState (false). cbg1). Checkbox(String str. Checkbox cbs = new Checkbox ("soltero". cbg2). false. add (cbv). CheckboxGroup cbGroup. add (cbext). CheckboxGroup cbg2 = new CheckboxGroup (). cbg1).

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

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 opción. Para usar un choice hay que declarar un objeto de tipo Choice y luego usar el método add para agregar la lista de opciones. Choice solo define un constructor por defecto que crea una lista vacia. Para añadir 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 añade. Los elementos se añaden a la lista en el orden en que se llama a add() o a addItem(). Para saber que opción fue seleccionada se usa el método getSelectedIndex() o al método getSelectedItem(). El metodo getSelectedItem() devuelve un string que contiene el nombre del elemento, y getSelectedIndex() devuelve la posición del elemento considerando que el primer elemento esta en la posición 0. Por defecto se selecciona el primer elemento que se añade a la lista. Para obtener el numero de elementos que hay en la lista, hay que llamar al método getItemcount(). Se puede establecer el elemento que tiene que estar seleccionado utilizando el método select() tanto con un entero que indique la posición del elemento (empezando a contar desde cero) como con un string que tenga el nombre que aparece en la lista estos métodos son: int getItemCount(), void select(int index), void select (String nombre) Dada una posición se puede obtener el nombre del elemento que esta en esa posición llamando al método getItem(), que tiene el siguiente formato: String getItem(int index) Gestión de listas Choice Cada vez que se hace una selección, se genera un evento que se envía a todo auditor que se haya registrado para recibir la información de los eventos que ese componente. Cada auditor debe implementar la interfaz ItemListener, que define el método 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 ("Fútbol"); ch_deportes.addItem ("Beísbol"); 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 línea de texto. Para usar un campo de texto hay que crear un objeto de tipo TextField que es una subclase de TextComponent indicando el número 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 número de caracteres del campo de texto. Para establecer un determinado texto se llama al método 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 también puede modificar el contenido del campo de texto llamando a setEditable(). También 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); }}

Gestión 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 selección compacta, con desplazamiento, que permite realizar selecciones múltiples. 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. También se puede configurar de manera que puedan realizar selecciones múltiples. Constructors: List(), List(int num), List (int num, boolean multiple) Métodos: void add(String nombre), void add(String nombre, int index): para añadir una lista. En listas que solo permiten seleccionar un único elemento se determina si este esta seleccionado con los métodos getSelectedItem() o con getSelectedIndex(), el primero devuelve un string con el nombre del elemento y el segundo devuelve la posición del elemento. En las listas que permiten seleccionar mas de un elemento se utilizan los métodos 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 método select(), pasándole un entero que indique la posición del elemento. Gestión 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 recién seleccionado. Cada vez que se selecciona un evento con un solo click, se genera un objeto de la clase ItemEvent. Se puede utilizar su método getStateChange() para determinar si una selección 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 añaden los elementos de la lista lista.add("Rojo"); lista.add("Verde"); lista.add("Azul"); lista.select(0); //se añaden 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 añade los siguientes metodos: void append(String str), void insert(String str, int index), void replaceRange(String str, int inicio, int fin). El metodo append() añade 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 restricción que imponen los diseños de que solo cabe un componente por celda o por zona y poder programas interfaces gráficas complejas. Un panel tiene su propio diseño 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 restricción en el número de paneles ni en el nivel de anidación. 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 diseño especificado con el método setLayout. p.setLayout (diseño); Todo lo que se ha dicho sobre agregar componentes con el método add es válido para los paneles. p.add (botón); p.add (checkbox); El uso de paneles puede mejorar el diseño de una interface gráfica evitando que los componentes se vean alineados como si estuvieran en una parrilla gigante. Una regla de diseño especifica que los componentes que están relacionados entre sí se coloquen juntos en un panel. El ejemplo a continuacion presenta un applet que utiliza 3 paneles en un diseño 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 gráficas o imágenes. Debido a que no puede contener otros componentes por lo general no se usa en forma directa sino a través de programar una clase que extienda a la clase Canvas. public miCanvas extends Canvas { La clase Canvas define un método paint vacío. La clase que extienda a Canvas sobrepone el método paint para dibujar o desplegar una imagen de la misma forma en que lo hace el método 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 método 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 lámpara 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 lámpara. La clase miCanvas es la que realmente hace el dibujo en su método paint y además tiene que definir dos métodos para que el applet le avise si cambia el color. La figura muestra la salida del applet.

70

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

-174).setColor (Color. 40. int color_lampara = 0.green). el área de texto puede leer varias. 250.class miCanvas extends Canvas { int color_mesa = 0. g.fillRect (0. 63. case 2 : g. repaint ().fillOval (120. break. break.setColor (Color. 125. g. 58). 89). // dibuja la cubierta g. case 3 : g. 312). 87.setColor (Color. 180).black). g.fillArc (78. La diferencia es que. public void paint (Graphics g) { switch (color_mesa) { case 0 : g. 175.fillArc (173.setColor (Color. } g.setColor (Color. case 1 : g. repaint (). case 3 : g. break.drawLine (175. 40). mientras el campo de texto puede leer solo una línea de texto. 110. break. } public void cambiaColorMesa (int c) { color_mesa = c. 181. 50. 119. } } 7. g. 130. 250. g. break. 130. 157.drawLine (215.red). 89). 96.8. break. 290. 120.black).setColor (Color. } public void cambiaColorLampara (int c) { color_lampara = c. 160). case 2 : g.blue). case 1 : g. // dibuja las motas g.blue). Si el texto es más grande que el tamaño con el que se despliega el área de texto automáticamente se colocan barras de scroll horizontales o verticales según sea el caso. 290). // dibuja la base g. } g. break. -65. // dibuja la mesa switch (color_lampara) { case 0 : g. 177.setColor (Color. 100. break. 250. 72 . 62. 40. 177. 40. 160).drawArc (85.drawLine (125.setColor (Color.drawLine (85.drawArc (85. 40.red). 40.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.green). 50.

60). // texto inicial TextArea ta4 = new TextArea ("hola". al oprimir el botón de Cuenta. 60. Se puede asignar texto desde el programa con el método setText. Después de escribir un texto cualquiera en la primera área de texto. 10. como si fuera una sola cadena de caracteres. La figura muestra la salida del programa. 60). String s = ta1. A pesar de que el texto esté en varias líneas. // el tamaño se deja al diseño TextArea ta2 = new TextArea (10. Cuando desde un programa se altera el texto de un área de texto es responsabilidad del programador insertar saltos de línea ('\n') dentro del string para que el texto se visualice por renglones. ta2.SCROLLBARS_NONE). // 10 renglones y 60 columnas TextArea ta3 = new TextArea ("hola mundo". 73 . El siguiente ejemplo presenta un applet con dos áreas de texto. un tamaño preferido de desplegado en renglones y columnas y un indicador para que despliegue o no barras de scroll de inicio. es decir. El texto capturado se puede recuperar llamando al método getText. la segunda área de texto despliega el número de líneas y de caracteres que se escribieron.getText (). TextArea.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.setText ("Esto es una línea\nEsto es otra línea"). 10. TextArea ta1 = new TextArea (). el área de texto lo trata como un string.

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

// permite escoger una opción List lst2 = new List (5).add ("opción uno"). List lst1 = new List (). En las listas que permiten la selección de únicamente un elemento el método getSelectedIndex devuelve la posición del elemento seleccionado.8.getSelectedIndex (). lst2. Para usar una lista hay que crear un objeto de tipo List pasando como argumentos opcionales el número de opciones que estarán visibles y un indicador si la liste permite o no opciones múltiples. El listado ejemplo presenta un applet con un choice. int opciones[] = lst3.4 Listas Una lista es similar al choice que permite seleccionar de entre varias opciones de una lista con dos diferencias: su presentación es en forma de lista y permite seleccionar más de una opción. En las listas que permiten múltiples selecciones el método getSelectedIndexes devuelve un arreglo de enteros con las posiciones de los elementos seleccionados. // cinco opciones visibles a la vez List lst3 = new List (5. una lista que permite seleccionar un solo elemento y una lista que permite selección múltiple. La figura muestra la salida del programa. int opción = lst1. 75 . true). en la parte inferior se despliegan las opciones que fueron seleccionadas en el choice y en las listas.add ("opción dos"). // permite opciones múltiples Las opciones se agregan con el método add.getSelectedIndexes ().} }). Al oprimir el botón. } } 7. lst2.

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

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

indicadores para minimizar.event.event. Es necesario crear un oidor de la clase WindowAdapter y registrarlo usando el método addWindowListener para indicarle al frame que se destruya al oprimir ese indicador. La figura muestra la salida de este programa.setVisible (true). frm. frm. } }). Para usar un frame hay que crear un objeto de tipo Frame pasando como parámetro el título de la ventana. éste es invisible. import java. frm.7. que en Windows tiene una marca de cruz y está en la parte superior derecha del frame. le agrega una etiqueta. // 200 en x y 100 en y Cuando se crea un nuevo frame. // 400 pixeles de ancho y 300 pixeles de alto frm.5 Frames Un frame es una ventana que.*. dependiendo del sistema operativo.*.add (new Label ("soy una etiqueta")).dispose ().awt. frm.applet.add (new Button ("soy un botón")). Por default el indicador de cerrar ventana. frm. El método 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.setSize (400.awt. frm. esta inactivo.dispose (). Se invoca cuando el frame ya no va a ser utilizado.WindowAdapter () { public void windowClosing (WindowEvent e) { frm. El siguiente ejemplo presenta un applet que crea un frame. Frame frm = new Frame ("titulo de la ventana"). maximizar o cerrar. // hace visible el frame El método dispose elimina el frame y libera la memoria.setLayout (new GridLayout (4.addWindowListener ( new java.8. 300). Un frame es un componente contenedor y como tal puede tener su propio diseño y contener otros componentes de AWT. 1)).awt. 100). y otras características típicas de las ventanas. import java. un campo de texto y un botón y luego lo hace visible. public class frm extends Applet { 78 .setLocation (200. import java. un menú de barra.*. cuenta con un título. El frame puede tener su propio diseño y componentes. Se le puede asignar un tamaño en pixeles usando el método setSize o indicarle las coordenadas de su esquina superior izquierda con setLocation.

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

// tiene título y es modal Los métodos setLayout. true). add.8. } } 7. Las diferencias son que los diálogos necesitan tener un frame ligado a ellos.dispose (). } }). "titulo". dispose y addWindowListener 80 . no tienen indicadores de maximizar ni de minimizar y pueden operar en forma modal. // el diálogo es no modal por default Dialog dlg2 = new Dialog (frame. // el diálogo es modal Dialog dlg3 = new Dialog (frame. setVisible. setLocation. Por estos motivos y por lo general.6 Diálogos Un diálogo es semejante a un frame en el sentido que es una ventana que puede tener su propio diseño y contener a otros componentes. Dialog dlg1 = new Dialog (frame). los diálogos se utilizan en forma modal y para avisar de errores en el programa o pedir datos al usuario. Para usar un diálogo hay que crear un objeto de tipo Dialog pasando como argumento obligatorio el frame al cual están ligados y opcionalmente un argumento string con el título del diálogo y una variable booleana para indicar si el diálogo es modal o no modal. Un diálogo modal impide que sean accesadas otras ventanas hasta que el diálogo sea cerrado. setSize.public void windowClosing (WindowEvent e) { frm_1. true).

El ejemplo presenta un applet que abre un diálogo modal y por lo tanto se bloquea.*.tienen la misma funcionalidad que en los frames. GridBagConstraints gbc = new GridBagConstraints ().*. import java. Al cerrar el diálogo el programa puede continuar y abre un frame. Label lbl_hola = new Label ("Hola"). import java. usando el método getParent. La figura muestra la salida del programa antes de cerrar el diálogo. public void init () { // Dialog Object tmp = getParent ().awt. Dialog dlg_1. Label lbl_error = new Label ("El frame no se abrira hasta cerrar este dialogo"). 81 . true).getParent (). Button btn_ok = new Button ("Cerrar"). Frame frm_1 = new Frame ("Ventanita"). import java. dlg_1 = new Dialog ((Frame) tmp. "título". Una forma de conseguirlo es obtener los componentes padres del applet. "Dialogo modal". true). while (! (tmp instanceof Frame)) tmp = ((Component) tmp). public class dlgm extends Applet { GridBagLayout gbl = new GridBagLayout ().applet. Para que un applet pueda usar un diálogo necesita hacer referencia a la ventana del navegador que lo contiene. while (! (tmp instanceof Frame)) tmp = ((Component) tmp). dlg1 = new Dialog ((Frame) tmp. Object tmp = getParent ().awt.event.getParent ().*. hasta encontrar un objeto de tipo Frame y pasar como argumento ese objeto al constructor del diálogo.

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

o dicho de otro modo. un objeto del tipo deseado de gestor de diseño con el que se desea trabajar. El operador new devuelve un gestor de diseño del tipo FlowLayout. El modo de interactuar con un gestor de diseño es muy simple: basta con especificar con cual de los disponibles queremos trabajar y añadirle componentes. qué gestor de diseño se asignará al contenedor.8. quien a partir de ahora ya sabe con qué gestor deseamos trabajar. que es la que se encuentra en la raíz de la jerarquía de clases.  Desde el punto de vista operativo.0 Gestores de diseño Introducción A lo largo de este capítulo.setLayout(new FlowLayout()). en éste veremos lo común a los dos tipos de componentes.awt. se puede especificar con cual o cuales de los gestores de diseño disponibles en Java se quiere trabajar. un Gestor de diseño es una propiedad del contenedor. El Container actúa de intermediario entre la ventana y los componentes que él almacena pasándoles a estos los mensajes que a él le llegan desde la ventana. se hace necesario indicar con cual de los disponibles deseamos trabajar. siempre que se crea un Container. viendo qué es un gestor de diseño desde tres puntos de vista diferentes:   Desde el punto de vista del lenguaje. Sin embargo. aunque muchas de las clases que implementan los gestores de diseño y el tratamiento de eventos se encuentran dentro del paquete JAVA. que puede almacenar cualquier tipo de objeto que derive de la clase Container (normalmente bien controles o bien otros gestores de diseño). vamos a continuar con la creación de interfaces de usuario en Java. Los eventos y gestores de diseño específicos de los componentes Swing los veremos en los próximos capítulos en los que se abordará Swing. Puesto que existen diferentes gestores de diseño incluidos en el lenguaje Java y puesto que además podemos construir nuestros propios gestores de diseño. Veamos. Lo que vamos a ver en este capítulo es aplicable tanto a componentes AWT como a componentes Swing (que los veremos en el siguiente capítulo). 83 . Veamos esto de una forma más detenida. es decir. El gestor de diseño es parte del Container.1 Gestores de diseño Los gestores de diseño (Layout Managers) tienen una gran importancia en el leguaje Java. Comencemos por lo tanto. como ya hemos comentado.  Desde el punto de vista de la jerarquía de clases de Java. 8.events. es el mecanismo que el lenguaje incorpora para conseguir crear aplicaciones que tengan la misma apariencia a través de las diferentes plataformas en las que éstas pueden ejecutarse. por ejemplo. es a través de la clase Container como podemos establecer el o los gestores de diseño que la ventana va a utilizar para implementar los componentes que ésta manejará. y deriva directamente de la clase Object. es necesario que estos estén contenidos en un gestor de diseño. un gestor de diseño es un interfaz. un gestor de diseño es un "estilo de visualización". éste objeto es recogido por el método setLayout() de la clase Container. de otro modo no funcionarán correctamente. los gestores de diseño y los eventos.AWT y del subpaquete java. vamos a tratar dos características del interfaz de usuario en los programas Java. la siguiente línea de código: this. de hecho. Lo que se hace pasándole al método setLayout() de la clase Container. para poder incorporar controles en una ventana (ya sea de una aplicación o de un applet).

Si queremos cambiar el layout manager de un contenedor en un momento dado. Como ya hemos comentado. cuando conoces el funcionamiento de los layouts. public void setMinimumSize(Dimenstion size). Veamos un ejemplo: . button. Los layouts por defecto para los contenedores de swing son los mismos que para sus homónimos de AWT. La clase java. Estos valores serán los que se utilizarán para renderizar el componente en pantalla. Para ello algo tan simple como lo que aparece aquí: this. frame.JComponent tiene métodos diferentes: public void setPreferredSize(Dimension size). . Por su parte la clase javax.getContentPane(). JFrame frame = new JFrame(). public void setMaximumSize(Dimension size).25)).setSize(400. tan sólo tendremos que llamar al método: contenedor. a un objeto de la clase Container le podemos añadir bien controles o bien otros gestores de diseño. Hay pequeñas variaciones de estos métodos pero su función es la misma. Si en cualquier momento decidimos que estamos hartos de un layout manager y queremos encargarnos nosotros mismos de la gestión de componentes tan sólo tendremos que escribir: contenedor. ver que aunque utilizas estos métodos...swing.La clase Container dispone del método add(). una longitud y una anchura determinadas. una coordenada vertical.300). frame.. 84 . que permite añadir componentes. Los componentes como hemos dicho son objetos que forman parte de nuestro interfaz gráfico.. Es muy frustrante.setMaximumSize(new Dimension(80.add(button). frame. JButton button = new JButton().Component nos ofrece una serie de métodos para poder modificar los valores de los atributos anteriores: public void setSize(Dimension size). Cada componente tiene asignada una coordenada horizontal. el intérprete Java no les hace ningún caso y los objetos muestran un tamaño que nosotros no queríamos.setVisible(true).setLayout(null).setLayout(LayoutManager layout).add( new Button( "Uno" ) )..awt.. public void setBounds(Rectangle r).

awt. JButton b1 = new JButton ( "A" ) . todos los layout managers implementan la interfaz LayoutManager directa o indirectamente (a través de LayoutManager). Es decir. JButton b4 = new JButton ( "D" ) .add ( b2 ) .add ( b5 ) .*. además de ser el gestor de diseño por defecto de los applets. c. por lo que nuestra llamada a setMaximumSize() no hace ningún efecto. ¿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 tamaño de los componentes ya que será el layout el que siempre tenga la última palabra1.*. 8.awt.AWT. cuando acaba con una fila de controles. } 85 . inicialmente podríamos pensar que se verá un botón de 80 puntos de largo por 25 de ancho. Este atributo indica el tamaño que a un componente le gustaría tener.Aunque no lo he dicho. Sin embargo.add ( b4 ) .setLayout (new FlowLayout ( ) ) . es también el más simple de todos. class EjemploFlowLayout extends JFrame{ public EjemploFlowLayout( ) { Container c = getContentPane ( ) . Ejemplo: import java. c. A continuación vamos a comentar los distintos gestores de diseño que podemos encontrar dentro del paquete JAVA.add ( b1 ) . c. JButton b2 = new JButton ( "B" ) . Si creamos un pequeño programa que contenga las líneas de código anteriores y lo ejecutamos.1. Este gestor. import javax. El último aspecto importante a tener en cuenta es el significado del atributo preferredSize. Este tamaño suele ser dependiente de la plataforma y el layout manager intentará respetarlo siempre que sea posible y siempre que lo permitan sus políticas de layout. import java.*. JButton b3 = new JButton ( "Botón largo " ) . Los cinco gestores de diseño son los que se comentan a continuación. JButton b5 = new JButton ( "E" ) . c. c. actuando con los controles como lo hace un editor de textos con las palabras.event. Es decir. si que se establece el tamaño máximo de 80x25 para el botón. En el ejemplo anterior.add ( b3 ) . c. sigue con la siguiente.1 FlowLayout El gestor de diseño FlowLayout.swing. lo que pasa es que después de añadirlo al frame es el layout manager de éste el que toma el control del interfaz y el que pasa a decidir el nuevo tamaño del botón. tras ejecutar el programa veremos como nuestro botón es considerablemente más grande. distribuye los controles de izquierda a derecha llenando el ancho del contenedor de arriba a abajo. los controles fluyen (Flow). En los distintos ejemplos se han utilizado botones para poner de manifiesto las diferentes distribuciones que los distintos gestores de diseño hacen del espacio ya que el gestor es invisible.

JButton este= new JButton("Este"). BorderLayout.200) . JButton centro = new JButton("Centro").setTitle("Prueba de BorderLayoutLayout"). Veamos más claramente lo que quiere decir esto con un ejemplo sencillo: import javax. Container panel = frame. en este caso se utiliza el método add() de la clase Container. Estas regiones son: North. y se corresponden con su situación dentro del contenedor en el que se encuentran.EXIT_ON_CLOSE).NORTH).getContentPane().1. BorderLayout. JButton norte = new JButton("Norte").add(este.300).EAST). } } 86 . BorderLayout.WEST). f. West y Center. panel.} }). se utiliza primero el método setLayout() pasándole por parámetro una instancia del gestor de diseño que se va a utilizar en nuestra ventana. panel.add(centro. BorderLayout. BorderLayout divide el espacio de un contenedor en cinco regiones diferentes.CENTER). frame.awt. JButton oeste = new JButton("Oeste").add(norte.*. East. frame.setDefaultCloseOperation(JFrame. frame.setVisible(true). panel.2 BorderLayout BorderLayout es el layout manager por defecto para frames por lo que al igual que FlowLayout su aprendizaje es indispensable.setSize(400.setVisible(true). 8. panel. es decir pasándole por parámetro la instancia del componente AWT que queremos añadir.SOUTH). frame. panel. en la versión que ya conocemos.add(oeste.setSize (200 . South. BorderLayout. JButton sur= new JButton("Sur").swing. public class TestBorderLayout extends JFrame { public static void main(String[] args) { TestBorderLayout frame = new TestBorderLayout(). import java. f.add(sur.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.public static void main (String [ ] args ) { EjemploFlowLayout f = new EjemploFlowLayout ( ) . f.exit(0). } } Como se puede observar. En el bucle se van añadiendo botones a la ventana.*.

el que se encontraba en el contenedor desaparecerá y el nuevo ocupará su lugar. BorderLayout respeta su alto mientras que la longitud del mismo se ajusta hasta ocupar todo el ancho del contenedor.NORTH BorderLayout. Cuando añadimos un componente en las posiciones norte o sur. Aunque ya las hemos visto en el código las resumiremos de nuevo: BorderLayout. BorderLayout. por su estructura. 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.SOUTH BorderLayout. Si no especificamos ninguna región por defecto el componente se inserta en el centro del contenedor.Como podemos ver en el código f uente anterior. una barra de estado en el panel sur y quizás un árbol de navegación en el panel izquierdo o derecho. sería bastante normal colocar una barra de herramientas en el panel norte de nuestra ventana. dejando el panel central para el resto del interfaz. Si insertamos un componente en una región donde había otro componente previamente. Con los componentes de las posiciones este y oeste pasa lo contrario. 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. Para finalizar con este layout manager vamos a hablar de como trata el tamaño de los componentes.EAST BorderLayout. Por ejemplo. es muy útil para muchas aplicaciones.CENTER // Por defecto Existe una manera alternativa de especificar donde queremos colocar los componentes y es pasando como parámetros al método add en lugar de los atributos anteriores las cadenas siguientes: “North” “South” “East” “West” “Center” Este método no está recomendado porque es demasiado propenso a errores. al añadi r un elemento al BorderLayout tenemos que especificar la región en la cual lo queremos añadir. 87 .WEST BorderLayout. Por lo tanto tenemos que tener cuidado con donde insertamos los componentes.

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

Cada fila y cada columna tiene el mismo tamaño y el área del contenedor se distribuye equitativamente entre todas las celdas. El número de filas y columnas se especifica en el constructor. En lugar de eliminar el panel e insertar otro nuevo. 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. import java. public class TestGridLayout extends JFrame { public static void main(String[] args) { TestGridLayout frame = new TestGridLayout().4 GridLayout GridLayout divide el espacio de un contenedor en forma de tabla. siendo necesario crear este mecanismo programáticamente con los inconvenientes de pérdida de tiempo y esfuerzo que esto supone. El problema que este gestor de diseño tiene es que la implementación que en Java se ha hecho de él. int Y = 3. podemos utilizar un CardLayout que nos ahorra gran cantidad de trabajo. o en lugar de eliminar los componentes e insertar otros nuevos. Este layout manager es muy sencillo y muy útil especialmente cuando tenemos un panel que variará su contenido en función de alguna parte de nuestro interfaz (una combo box por ejemplo). for (int i = 0. 8. en un conjunto de filas y columnas. es decir. Con un botón podemos ir avanzando de etiqueta.awt. } } El ejemplo es muy sencillo y muestra un contenedor con varias etiquetas.Y)).*.1. y las fichas (o tarjetas si se prefiere) no tienen la correspondiente pestaña asociada. Container container = frame. Al pulsar el botón se muestra la etiqueta siguiente llamando al método next() de CardLayout. container. En el capítulo 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 través de los paneles que presenta. i++) { 89 . int X = 3. Si pasamos cero como el número de filas o columnas el layout manager irá creando las filas y columnas en función del número de componentes y del valor de la otra dimensión.setLayout(new GridLayout(X. por lo que no existe una forma preestablecida de interactuar con los controles para cambiar de unas fichas a otras. Este tercer gestor permite manejar varias "fichas intercambiables". Veamos un ejemplo simple de funcionamiento: import javax. de forma tal que sólo una esté visible a la vez y ocupando todo el área del contenedor.*.getContentPane(). es decir. no es completa. De todo esto se deduce que GridLayout no respetará el tamaño preferido de los componentes que insertemos en cada una de las celdas. Es similar a los controles de pestaña o "tabs" de Windows. i < X.swing.setVisible(true).frame.

Odiado por unos y alabado por otros.add(new JButton(i + “ x “ + j)). Con este layout manager tenemos control absoluto sobre las posiciones que ocuparán los objetos en el interfaz final.1. frame. o que una celda pudiese ocupar varias posiciones. j++) { container.EXIT_ON_CLOSE).. 90 . Ventajas y desventajas GridBagLayout es el layout manager que más pavor causa entre los programadores Java. 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.for (int j = 0..setDefaultCloseOperation(JFrame. Afortunadamente.5 GridBagLayout GridBagLayout es el layout manager más poderoso y eficaz con mucha diferencia.300). Lo realmente interesante sería que algunas celdas pudiesen tener tamaños diferentes a las demás. frame. GridBagLayout. este layout manager proporciona una serie de ventajas sobre el resto: • Permite la creación de interfaces de usuario complejos. 8. Como se puede ver todas las celdas tienen el mismo tamaño 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. etc. Con GridBagLayout podemos imitar facilmente el comportamiento del resto de layout managers a parte de poder crear con el interfaces mucho más complejas. esto es justamente lo que nos permite el próximo layout manager que veremos.setTitle("Prueba de BorderLayoutLayout"). } } frame.setVisible(true).setSize(400. } } En la figura anterior vemos el resultado de ejecutar el ejemplo. j < Y. o dejar celdas vacías. frame.

no voy a mostrar la totalidad de atributos para no complicar las cosas a los menos experimentados. Realmente no siempre es necesario establecer su valor ya que en los casos más simples nos llegaría con gridwidth y gridheight.Layout sea un juego de niños. también tiene sus inconvenientes: • Requiere un tiempo de aprendizaje bastante grande. Los paneles son objetos bastante pesados y tener una gran cantidad de los mismos puede influir perjudicialmente en el rendimiento de nuestro programa. 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 harán que utilizar GridBag. GridBagLayout a fondo GridBagLayout basa su funcionamiento en una clase auxiliar que establece restricciones a los componentes. No sólo es necesario comprender su funcionamiento sino que también es necesario haber hecho bastantes ejemplos para llegar a dominarlo. La siguiente figura muestra gráficamente 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. Con GridBagLayout se pueden crear interfaces exactamente iguales pero con un único panel con lo que nuestra interfaz será mucho más ligera. Pero como todo.• Las interfaces construidas son más ligeras. GridBagConstraints. La clase GridBagConstraints posee bastantes atributos que nos permiten configurar el layout de un contenedor a nuestro gusto. Básicamente lo que indican 91 . • El código necesario para crear un interfaz de usuario es considerablemente más grande que con el resto de layout managers y además suele ser un código bastante complicado y díficil de comprender y por lo tanto de mantener. Comprendiendo a la perfección su significado no habrá ningún interfaz que se nos resista. Estas restricciones especifican exactamente como se mostrará cada elemento dentro del contenedor. Vamos a ir viendo los atributos más importantes de esta clase y de una forma gráfica 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. sin embargo la experiencia dice que poner este atributo es recomendable ya que permite saber en que elemento nos encontramos de una manera visual. A continuación vamos a ver los atributos importantes. Cuando queremos crear un interfaz de usuario combinando el resto de layout managers vistos hasta el momento a menudo terminamos con un número grande de paneles anidados.

c. 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.gridwidth=GridBagConstraints. Técnicamente sólo sería necesario poner REMAINDER ya que es el único que necesita conocer el GridBagLayout para saber que se ha acabado la fila. es decir. indica que el componente es el último de la fila actual o columna actual.1 En la segunda fila tenemos dos componentes.gridheight=1 c.gridheight=1 c. en este caso indica exactamente el número de filas o columnas que ocupará el componente. su valor puede ser: • Un número cardinal. ocupará todo el espacio hasta el último componente. En el segundo y tercer componente en lugar de ponerle uno como valor de gridwidth hemos de poner RELATIVE y REMAINDER.gridheight=1 c.REMAINDER.REMAINDER c. En la primera fila tenemos tres componentes. • GridBagConstraints.gridwidth=1 c.RELATIVE c. • GridBagConstraints.RELATIVE.Siempre y cuando no se use gridx y gridy. Todos los componentes ocupan una celda en horizontal y en vertical luego su gridwidth y gridheight ha de ser igual a uno.RELATIVE 92 . Analicemos la figura anterior para comprender el significado de estos dos atributos.gridwidth=GridBagConstraints.gridwidth=GridBagConstraints. indica que el componente ocupará el espacio disponible desde la fila o columna actual hasta la última fila o columna disponibles.RELATIVE c. 1.gridheight=GridBagConstraints. Ambos tienen como gridheight RELATIVE ya que se encuentran en la penúltima fila. El primer componente tiene de gridwidth REMAINDER.gridwidth y gridheight es el número de celdas que ocupará un componente dentro del GridBagLayout.

SOUTH.gridheight=GridBagConstraints. Como intuiréis indican la orientación de los componentes dentro de la celda que ocupan.RELATIVE Por último en la tercera fila tan sólo tenemos un componente con gridwidth REMAINDER y gridheight REMAINDER.c.REMAINDER c. En este ejemplo he colocado cada uno de los componentes con un anchor diferente para que podáis apreciar fácilmente el efecto que tiene este atributo. WEAST.gridwidth=GridBagConstraints. Además 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.gridwidth=GridBagConstraints. anchor Este atributo es mucho más sencillo. SOUTHEAST y CENTER. gridwidth y gridheight especifican el número de celdas horizontal y vertical que abarcará un componente. EAST.REMAINDER c.gridheight=GridBagConstraints. Como veis es bastante sencillo. fill El atributo fill especifica el espacio que ocupará el componente dentro de la celda. NORTHEAST. • VERTICAL: El componente ocupará todo el espacio vertical de la celda mientras que su longitud será la que tenga como preferida. anchor especifica la posición que ocupará un componente dentro de una celda. Los valores que puede tomar también son variables estáticas de la clase GridBagConstraints: • NONE: El componente ocupará exactamente el tamaño 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.REMAINDER c. Los valores que puede tomar este atributo están definidos como variables estáticas dentro de la clase GridBagConstraints y son: NORTH. NORTHWEST. • BOTH: El componente ocupará la totalidad de la celda Veámoslo en una imágen: 93 . Veamos una figura que nos aclare las cosas: En la imágen anterior las líneas punteadas marcan el espacio que ocupa cada una de las celdas del contenedor. SOUTHWEST.

pero eso es debido a que he utilizado los atributos weightx y weighty. en este caso las celdas si que ocupan todo el contenedor. weightx y weighty Estos dos atributos son la clave de los dolores de cabeza que GridBagLayout le da a tanta gente.Como antes. 94 . 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. Sin embargo existe un pequeño problema que veremos a continuación. la respuesta como cabía esperar son los atributos weightx y weighty. Mirar lo que sucede si hago el mismo ejemplo anterior sin estos atributos: He puesto las líneas que marcan el espacio ocupado por las celdas en rojo para que se vea mejor. Como veis las celdas tienen de largo la longitud máxima de los componentes y de alto la altura máxima de los componentes. ¿Cómo hacemos entonces para que las celdas ocupen la totalidad del contenedor?. Como se puede apreciar en la figura según el valor del atributo anchor los componentes abarcarán más o menos espacio. Hay que tener mucho cuidado porque al contrario de lo que pueda parecer si no indicamos nada las celdas no ocuparán la totalidad del contenedor. Como habéis podido observar hasta ahora no ha habido nada complicado. Todos los atributos que hemos visto son bastante sencillos de comprender y prácticamente ya estaríamos en disposición de crear un interfaz complejo con GridBagLayout. A medida que vamos añadiendo componentes a un contenedor el layout manager va determinando en función del tamaño de los componentes el espacio que ocupan las celdas. las líneas marcan los bordes de las celdas. Fijaros en la figura anterior.

La forma de especificar el espacio que quiere ocupar cada componente es mediante un número entre 0.4. Como ambos componentes han pedido espacio libre. Obviamente cuando estemos diseñando el interfaz no estaremos pensando en si este componente va a tener unos puntos y otro otros.0 Veamos como se asigna el espacio horizontal. Veamoslo: Este espacio libre (flechas) se dividirá entre todas las celdas que especifiquen valores dentro de los atributos weightx y weighty. en total 200 puntos.weighty=1. Este número representa al porcentaje de espacio libre que ocupará cada celda. Sin embargo como el componente 2 tan sólo 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 más los 75 puntos que sobraron del segundo componente.weighty=1. En este caso como los dos componentes han pedido la totalidad de su parte a ambos les corresponden 75 puntos. por lo tanto a cada uno le tocan 125 puntos. En el ejemplo anterior una vez añadidos los componentes queda una determinada cantidad de espacio libre tanto horizontal como vertical. c.Los atributos weightx y weighty especifican el porcentaje de espacio libre que ocupará una celda eterminada. El espacio vertical es más sencillo. Un ejemplo: • Espacio libre 250 puntos en horizontal y 150 en vertical • Componente 1: c.0 • Componente 2: c.0 y 1. Veamos como queda el ejemplo anterior pero utilizando los atributos weightx y weighty: 95 . éste se divide entre ambos. sin embargo los atributos weightx y weighty son de una ayuda inestimable para hacer que determinadas partes de nuestra interfaz sean más grandes que las otras.0.weightx=0.weightx=1. c.0. Como vimos antes se divide el espacio libre entre los componentes que lo han pedido.

Por lo tanto. int left. En este caso. insets.Insets cuyo constructor es: Insets(int top. Vamos a ver un último atributo que nos servirá de gran ayuda. el componente se pega literalmente al borde de la celda.awt. Si os fijáis en la figura anterior. que no os extrañe que poniendo 0. cuando se insertan componentes que ocupan la totalidad de la celda ya sea en horizontal. que haya un márgen entre el borde de la celda y los componentes.Como se puede apreciar en la figura anterior cuanto mayor sea el porcentaje más espacio libre ocupará nuestra celda. Esto lo conseguimos con el atributo insets. int right) Como intuiréis los parámetros del constructor especifican el espacio que se dejará de márgen. Muy comúnmente desearemos que los componentes no estén tan pegados. insets Con todo lo que hemos visto hasta ahora tenemos más que de sobra para crear una interfaz compleja. es decir. int bottom. El atributo insets es un objeto de la clase java. en vertical o en ambas direcciones. Lo más importante es comprender que se los porcentajes se refieren al espacio libre y que el espacio libre se determina después de insertar los componentes. Veamos un ejemplo: 96 .1 como valor de weightx las celdas de la derecha ocupen tanto espacio. las celdas de la derecha ocuparán el tamaño preferido del botón más grande ( “HORIZONTAL” ) más su porcentaje del espacio libre horizontal.

public class A { public static void main(String[] args) { JFrame f = new JFrame().0. container. tal como se ve en el código.4.EXIT_ON_CLOSE). c.insets = new Insets(2. c.gridwidth=GridBagConstraints.setVisible(true). c.BOTH. c.c). c. c.gridheight=GridBagConstraints.setSize(220. f.createTitledBorder( "Entrada al sistema")).c).weightx=0. container.fill=GridBagConstraints.weighty=1. ((JPanel)container).5.add(new JTextField().5). c.2.Vamos a empezar con el más sencillo. c.5. c.gridheight=GridBagConstraints.4.RELATIVE.0.REMAINDER.RELATIVE. container.0. f.WEST.setTitle("Login").REMAINDER. f.*. c.gridheight=GridBagConstraints.2. import java. Si compilais y ejecutáis el ejemplo os deberíais encontrar con la ventana siguiente: 97 . Esta variable puede ser reutilizada. Tened mucho cuidado de no olvidaros pasar la variable de constraints porque en otro caso el interfaz no saldrá como esperábais. c.insets = new Insets(2.gridwidth=GridBagConstraints.REMAINDER.getContentPane(). para no tener que estar creándolas continuamente.insets = new Insets(2.add(new JLabel("Contraseña"). c.0).setLayout(new GridBagLayout()).RELATIVE. c.gridwidth=GridBagConstraints.weightx=0.awt. Container container = f. c.0). f.0. container.0. GridBagConstraints c = new GridBagConstraints().anchor = GridBagConstraints.c). c.2.weightx=1.setDefaultCloseOperation(JFrame. la ventana de entrada al sistema.gridheight=GridBagConstraints.c). container. c.swing.5). } } Como se puede ver cada vez que añadimos un componente al contenedor hemos de pasar una variable de tipo GridBagConstraints al método add.weightx=1. c.gridwidth=GridBagConstraints.add(new JTextField().RELATIVE.*.add(new JLabel("Usuario").REMAINDER.2. c. Veamos como sería el código para crear este interfaz: import javax.110).setBorder(BorderFactory.insets = new Insets(2. c.

algunos diseñadores pensaron que se necesitaba dejar a un lado las librerías AWT e introducir las Swing no sintieron lo mismo del sistema de gestión de eventos. Como ya os he comentado. Realmente este sistema de gestión de eventos es bastante elegante y sencillo. A menudo con utilizar una de las dos alternativas será suficiente pero habrá ocasiones en las que tendremos que utilizar ambos atributos. El objeto que escucha los eventos es el que se encargará de responder a ellos adecuadamente. Dónde se produce el evento se denomina “fuente del evento”.0 EVENTOS El sistema de gestión de eventos de Java de la AWT es similar al de SWING. por ejemplo un scroll. hacer clic con el ratón. estos atributos no son estrictamente necesarios al igual que tampoco lo son gridwidth o gridheight.0. El sistema operativo notifica a las aplicaciones que están ocurriendo estos eventos. pulsar el ratón sobre un botón o menú (Java distingue entre simplemente pulsar el ratón en un sitio cualquiera o hacerlo. en un botón). sobre todo si se compara con el sistema de gestión de eventos de Java 1. Esta separación de código entre generación del evento y actuación respecto a él facilita la labor del programador y da una mayor claridad a los códigos. mucho más engorroso de usar y menos elegante. mover el ratón. El modelo de delegación de eventos El modelo de Java se basa en la delegación de eventos: el evento se produce en un determinado componente.Un último punto que quería resaltar del código fuente anterior es que como podéis observar no he utilizado para nada los atributos gridx y gridy. ¿Qué es un evento? Todos los sistemas operativos están constantemente atendiendo a los eventos generados por los usuarios. 9. A continuación el evento se transmite a un ”manejador de eventos” (event listener) que este asignado al componente en el que se produjo el evento. consideraron que era lo suficientemente bueno. y ellas deciden si han de responder o no de algún modo a este evento. Estos eventos pueden ser pulsar una tecla. 98 . por ejemplo.

otro objeto. manejador ha de pertenecer a una clase que implemente la interface MouseListener. Cuando el usuario genere el evento deseado (en este caso pulse el botón). en este caso un botón. Para hacer que un objeto escuche los eventos de otro objeto se emplea el método add[nombre_evento]Listener. El objeto que escucha los eventos ha de implementar para ello una interface. que tiene un total de 7 métodos que ha de implementar.. Es responsabilidad del manejador.. y no de la fuente. El nombre de esta interface es siempre el nombre del evento más “Listener”: para que un objeto escuche eventos de ratón ha de implementar la interface MouseListener. el objeto fuente empaqueta información a cerca del evento generando un objeto de tipo Event (ActionEvent en este caso) e invoca el método correspondiente del manejador (actionPerformed(actionEvent)) pasándole como información el objeto de tipo Event generado.. A continuación en la siguiente tabla mostramos los 99 .addMouseListener(manejador). así si tuviésemos un Jframe llamado “frame” y quisiésemos que el objeto llamado “manejador” escuchase los eventos de ratón de “frame” lo haríamos del siguiente modo: frame. para que escuche eventos de teclado KeyListener.. responder al evento.Gestión de eventos en Java. manejador que ha de extender la clase Adapter correspondiente o implementar la interfaz Listener (interfaz ActionLitener en este caso). Lo que la fuente de eventos le pasa al objeto encargado de escuchar los eventos es. A la fuente del evento. como no. Es un objeto tipo Event. por ello se dice que la fuente delega la gestión del evento en el manejador. le indicamos quién será su manejador de eventos. En este objeto va toda la información necesaria para la correcta gestión del evento por parte del objeto que escucha los eventos.

En la columna de la derecha se presentarán diversos componentes que pueden generar dichos eventos.eventos más comunes. 100 . junto a la interface que debe implementar el objeto que escuche esos eventos y el método para asociar un objeto para escuchar dichos eventos.

Cabe preguntarse ahora por que métodos tiene cada interface. Parece un poco estúpido implementar métodos que no hagan nada sólo porque la interface de la que heredamos los tenga. 101 . incluso aunque no los usemos. sino la clase que se encargaría de escuchar los eventos sería abstracta y no podríamos crear ningún objeto de ella. ya que hemos de implementar todos ellos.

Los creadores de Java también pensaron en esto y por ello para cada interface que tiene más de un método crearon una clase llamada [nombre_evento]Adapter (MouseAdapter). pero nosotros sólo estaremos interesados en un método de dicha interfase: mouseClicked. Nosotros lo único que tendremos que hacer es que nuestra clase que escuche eventos extienda esta clase y sobrescriba los métodos que nos interesen. que lo que hace es implementar todos los métodos de la interface sin hacer nada en ellos.Así por ejemplo si estamos interesados en escuchar clics de ratón hemos de crear una clase que implemente MouseListener. A continuación en la siguiente tabla damos un listado de las principales interfaces junto a sus respectivas clases “Adapter” y los métodos que poseen: 102 .

Las clases adaptadoras permiten sobrescribir solamente los métodos del interfaz en los que se esté interesado. Interfaz Métodos 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 .event. De esta forma se consigue un código más claro y limpio.Todos estos conceptos que estamos introduciendo acerca del tratamiento de eventos en Java. y la fuente será el botón que lanzará el evento correspondiente. en lo que al tratamiento de eventos se refiere. Cada conjunto de eventos tiene asociado un interfaz.awt. Así por ejemplo. lo que hemos llamado oyentes. si tenemos el siguiente GUI: un botón y una caja de texto dentro de una ventana. el oyente será la ventana que contiene al botón y a la caja de texto. Así por ejemplo. es decir de la clase MouseAdapter. Este modelo de eventos ofrece las siguientes características:  Ofrece un estructura robusta para soportar programas Java complejos. Un oyente en lugar de implementar un interfaz. y queremos que al pulsar el botón aparezca un mensaje en la caja de texto. Esto es posible debido a que las clases adaptadoras implementan el interfaz correspondiente y simplemente tienen implementaciones vacías de todos los métodos del interfaz EventListener. y como ya hemos dicho cada uno de estos interfaces declara una serie de métodos para cada uno de los eventos lógicos asociados al tipo de evento de que se trate. si queremos atrapar un click del ratón. Estas clases se suelen utilizar cuando se quiere hacer uso de un interfaz muy complejo del que sólo interesan un par de métodos. Las clases adaptadoras las utilizaremos para simplificar nuestro código. Es  Ofrece una clara separación entre el código de la aplicación y del interfaz de usuario.  simple y fácil de aprender. ya que implementan de forma vacía todos los métodos de un interfaz de tratamiento de eventos determinado. puede utilizar una clase que herede de una clase adaptadora de eventos del paquete java. Otras clases que también hemos comentado y que se utilizan dentro del tratamiento de eventos en Java son las clases adaptadoras. en lugar de implementar el interfaz MouseListener.  Facilita la creación de un código de tratamiento de eventos robusto y menos propenso a errores. se verá mucho más claro cuando se muestren algunos ejemplos más adelante. Dentro de la jerarquía de clases de Java hay una serie de clases para representar todos los eventos y otra serie de interfaces que definen una serie de métodos que deben implementar las clases que van a tratar los eventos. heredamos de la clase adaptadora de los eventos de ratón. solamente deberemos sobrescribir el método mouseClicked(). es decir.

addActionListener(this). será necesario implementar el interfaz ActionListener.*. public void actionPerformed(ActionEvent evento){ //cuerpo del método } Una vez comentado el tratamiento de eventos en Java de forma más o menos teórica vamos a comentar una serie de ejemplos para aplicar la teoría a la práctica. una pulsación de un botón) generado por un botón. Estos ejemplos además nos van a servir para repasar distintos puntos del lenguaje Java comentados hasta ahora. siempre que utilizamos un interfaz con un único método vamos a implementarlo. si tenemos en cuenta el ejemplo anterior del botón.awt. se debería escribir lo que objetoBoton. para poder hacer referencia a un interfaz concreto vamos a suponer que queremos realizar el tratamiento de eventos que se corresponde con la pulsación de un botón.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. A continuación vamos a comentar los pasos genéricos que se deben seguir a la hora de realizar el tratamiento de eventos en Java. public class MiClase extends ClasePadre implements ActionListener{ Debemos determinar que componentes van a generar los eventos. A continuación escribiremos la declaración de la clase para que implemente el interfaz adecuado (listener interface). Aunque los pasos son genéricos. los interfaces que únicamente poseen un método no tienen clase adaptadora correspondiente. ya que no tiene ningún sentido. Por ejemplo si se está tratando de atrapar un evento ActionEvent (es decir. Una vez hecho esto debemos crear las implementaciones de todos los métodos del interfaz que la clase debe implementar. 104 . Se registra cada uno de ellos con el tipo adecuado de oyente. Lo primero es importar el paquete java.event.awt.event: import java.

} 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"). el que se encarga de las pulsaciones de botones.out. el que se encarga del ratón. y como hemos dicho que es una ventana.exit(0). No vamos a tratar todos los interfaces.println("La ventana se ha cerrado"). y el que se encarga de las ventanas.*. //finaliza la aplicación System. public class Ventana extends Frame implements WindowListener{ //constructor de nuestra clase public Ventana(String titulo){ //constructor de la clase padre super(titulo). //paquete necesario para el tratamiento de eventos import java. } public void windowClosed(WindowEvent evento){ System. //se registra nuestra clase como oyente addWindowListener(this). al ser una aplicación debe tener un método main() de arranque. es decir. El código de esta aplicación de ejemplo es: import java. como se puede ver importamos el paquete correspondiente e indicamos que nuestra clase implementa el interfaz WindowListener. y cuando pulsemos sobre el aspa de cerrar la ventana esta se cierre y finalice la ejecución de la aplicación.event. } public void windowDeiconified(WindowEvent evento){ System.150). //tamaño de la ventana setSize(150.println("No estoy minimizada"). hereda de la clase Frame. MouseListener. ActionListener. estos tres nos ofrecen ejemplos bastante prácticos. en primer lugar. Primero vamos a comenzar creando una aplicación sencilla que va a consistir simplemente en una ventana que nos va a indicar si se encuentra minimizada o no. } } Vamos a comentar algunos puntos importantes acerca del código anterior. } public void windowIconified(WindowEvent evento){ System. Como se puede observar hemos creado también un método constructor para nuestra clase Ventana.awt.out. //se muestra la ventana setVisible(trae). quien va a 105 . WindowListener. También veremos los adaptadores MouseAdapter y WindowAdapter. En lo concerniente al tratamiento de eventos.awt.Vamos a realizar una aplicación que vamos a ir modificando y complicando para mostrar las diferentes facetas del tratamiento de eventos en Java.*. En el constructor de nuestra clase indicamos quien va a ser el oyente de nuestra clase. } public void windowClosing(WindowEvent evento){ //se cierra la ventana dispose().println("Estoy minimizada"). Vamos a comentar algunas consideraciones de carácter general.out. sólo vamos a tratar tres de los más representativos.

fuente=fuente. es decir. ya que heredamos de la clase Frame y la herencia múltiple no se permite en Java. El resto del código es bastante sencillo y considero que no necesita una mayor explicación. Como ya debe saber el alumno. Para ello utilizamos la línea de código addWindowListener(this). } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos"). Por lo tanto si queremos hacer uso de la clase adaptadora WindowAdapter. setSize(150. estamos registrando nuestra clase como oyente mediante la referencia this. En nuestro caso hay tres métodos que no nos interesan. import java. Es decir. Al ser nuestra clase fuente y oyente de los 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. Los métodos windowIconified() y windowDeiconified() se ejecutarán cuando se minimice la ventana y cuando se restaure.awt. que herede de la clase WindowAdapter.*. también debe implementar el interfaz que define los métodos que se van a ejecutar atendiendo al evento que se produzca.awt. las clases adaptadoras realizan una implementación vacía de todos los métodos del interfaz con el que se corresponden. En nuestro caso no podemos heredar de una clase adaptadora. el método windowClosing() se ejecuta en el momento de cerrar la ventana y el método windowClosed() cuando ya se ha cerrado. } //contiene la implementación de los métodos que nos interesan //del interfaz WindowListener 106 . Por lo tanto deberemos crear y definir una nueva clase que herede de la clase adaptadora del interfaz WindowListener. //constructor de nuestra clase adaptadora //recibe como parámetro la clase fuente del evento public AdaptadorVentana(Ventana fuente){ this. y al heredar de ellas utilizaremos únicamente los métodos del interfaz correspondiente que nos interese. setVisible(true).tratar los eventos. //se registra como oyente la clase que hereda la clase adaptadora addWindowListener(new AdaptadorVentana(this)). respectivamente. pero como implementamos el interfaz WindowListener debemos implementarlos aunque no tengan contenido. en este momento entran en juego las clases adaptadoras.150). el código de nuestra aplicación se debe modificar como indica : import java.event. es decir. Como ya hemos comentado hay tres métodos del interfaz WindowListener que no nos interesan y que por lo tanto nos gustaría suprimir. //nuestra clase ya no implementa el interfaz WindowListener public class Ventana extends Frame{ public Ventana(String titulo){ super(titulo).*. indicamos que el oyente de los eventos de la ventana va a ser nuestra propia clase. en este caso el oyente es nuestra misma clase.

Este atributo es necesario para tener una referencia a la clase fuente del evento.println("Estoy minimizada"). en este caso si que existe una separación clara entre clase fuente y clase oyente. pero puede ser bastante práctica. Otra cosa que cambia también en el código de nuestra aplicación es la forma en la que se registra el oyente.println("La ventana se ha cerrado"). siempre que exista una clase adaptadora para el evento en cuestión. Ahora vamos a detenernos en la clase AdaptadorVentana.println("No estoy minimizada"). El oyente sería la clase AdaptadorVentana y la clase fuente sería la clase principal Ventana. } Como se puede comprobar esta nueva clase contiene todos los métodos que nos interesan del interfaz WindowListener. } } Los mayores cambios se aprecian en que existe una nueva clase que va a ser la encargada de tratar los eventos. De esta forma la clase adaptadora puede manipular y acceder al objeto de clase Ventana.out. Si queremos tratar distintos eventos mediante clases adaptadoras. Esta referencia es necesaria .public void windowClosed(WindowEvent evento){ System. Esta clase hereda de la clase WindowAdapter y posee un atributo denominado fuente que es de la clase Ventana. La clase interna va a tener acceso a todos los métodos y atributos de la clase en la que está declarada. También existe la posibilidad de utilizar la clases adaptadoras como clases internas (inner classes).out. Nuestra clase Ventana. } public void windowIconified(WindowEvent evento){ System. dentro del método windowClosing(). } public void windowDeiconified(WindowEvent evento){ System.fuente=fuente.dispose().out. public void windowClosing(WindowEvent evento){ fuente. 107 . public AdaptadorVentana(Ventana fuente){ this. } public void windowClosing(WindowEvent evento){ fuente. Una clase interna es una clase definida dentro de otra. } Para conseguir la referencia a la fuente del evento se pasa una instancia de la clase Ventana actual al constructor de la clase adaptadora. System. aunque estos sean privados.en este caso. deberemos crear una clase que herede de la clase adaptadora de cada tipo de evento.dispose(). a la hora de cerrar la ventana. El beneficio que podemos obtener de estas clases adaptadoras.exit(0). Se sigue utilizando el método addWindowListener() pero en este caso se pasa por parámetro una instancia de la clase adaptadora que hemos creado nosotros. al ser sólo fuente de eventos no va a implementar el interfaz WindowListener y por lo tanto únicamente va a tener en su cuerpo el método constructor y el método main(). es que no tenemos porque llevar la referencia de la clase fuente. Realmente una clase interna se sale fuera de los principios de la POO. addWindowListener(new AdaptadorVentana(this)).

out. //Utilizamos una clase interna anónima addWindowListener(new WindowAdapter(){ //métodos de la clase anónima public void windowClosed(WindowEvent evento){ System.*.println("No estoy minimizada").event.out.awt.awt.*. } public void windowIconified(WindowEvent evento){ System. } public void windowDeiconified(WindowEvent evento){ System. setSize(150. se puede utilizar una clase interna anónima: import java. } public void windowClosing(WindowEvent evento){ dispose(). vamos a ofrecer una posibilidad distinta a la hora de tratar los eventos en Java.awt. public class Ventana extends Frame{ public Ventana(String titulo){ super(titulo). } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos").*.out. } }).150).println("Estoy minimizada").out.exit(0).println("La ventana se ha cerrado").println("No estoy minimizada"). setSize(150. } }//se cierra la clase interna } Ahora.println("Estoy minimizada"). //ya no se indica la referencia de la clase fuente addWindowListener(new AdaptadorVentana()).awt.//se cierra la clase interna anónima } public static void main (String[] args){ 108 . System. public class Ventana extends Frame{ public Ventana(String titulo){ super(titulo).*.150) setVisible(true). System. import java. } //clase adaptadora interna class AdaptadorVentana extends WindowAdapter{ //Ya no es necesario el atributo fuente public void windowClosed(WindowEvent evento){ System. } public void windowIconified(WindowEvent evento){ System. import java.out.println("La ventana se ha cerrado"). } public void windowDeiconified(WindowEvent evento){ System.exit(0).El nuevo código tendría el aspecto siguiente: import java. setvisible(true).out.event. } public void windowClosing(WindowEvent evento){ //llamamos directamente al método dispose() dispose().

out. //oyente del botón.println("No estoy minimizada"). En este caso deberemos implementar el interfaz ActionListener.awt. WindowAdapter en nuestro caso.*. setLayout(new FlowLayout()). System.event. El mecanismo es muy sencillo. por lo demás funciona exactamente igual a una clase interna.out. en este caso la clase Ventana boton. El nuevo aspecto de nuestra aplicación de ejemplo se puede observar en la Figura. al tener un único método llamado actionPerformed(). public Ventana(String titulo){ super(titulo). } } A la vista del código se puede ver que se trata de instanciar una clase sin indicar un objeto que contenga una referencia a la misma.println("La ventana se ha cerrado"). } public void windowDeiconified(WindowEvent evento){ System.exit(0). ya que en realidad esta clase sólo se va a utilizar para el tratamiento de eventos y no se va a querer hacer una referencia a ella. } public void windowIconified(WindowEvent evento){ System. import java.out. y se implementan los métodos que se consideren necesarios. boton=new Button("Púlsame").println("Estoy minimizada").out.*.println("Buenas tardes"). ahora vamos a añadir a nuestra aplicación un botón para que al pulsarlo escriba un mensaje en la pantalla. se utiliza el nombre de la clase adaptadora correspondiente. Una vez comentados las distintas opciones que tenemos a la hora de tratar los eventos. } public void actionPerformed(ActionEvent evento){ System. addWindowListener(new AdaptadorVentana()). ya que este interfaz no posee una clase adaptadora. add(boton).awt. setSize(150. } class AdaptadorVentana extends WindowAdapter{ public void windowClosed(WindowEvent evento){ System. //implementamos el interfaz para tratar la pulsación del botón public class Ventana extends Frame implements ActionListener{ private Button boton. } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos").150). } public void windowClosing(WindowEvent evento){ 109 . setVisible(true).Ventana miVentana=new Ventana("Eventos").addActionListener(this). import java.

println("Buenas tardes"). add(area). boton.setText("El ratón ha salido"). o por el contrario ha salido. } public void mouseExited(MouseEvent evento){ area. area=new TextArea().*. y lo vamos a realizar a través de la clase adaptadora MouseAdapter. } public void actionPerformed(ActionEvent evento){ System.dispose().awt. la única línea digna de mención es la que registra el oyente del botón. } } class AdaptadorVentana extends WindowAdapter{ 110 . public void actionPerformed(ActionEvent evento){ System. setLayout(new FlowLayout()). Al pulsar el botón veremos en pantalla el saludo "Buenas tardes".addActionListener(this). //El oyente del ratón es una nueva clase interna area. boton=new Button("Púlsame"). Utilizamos la clase adaptadora. porque sólo nos van a interesar un par de métodos del interfaz MouseListener.setText("El ratón ha entrado"). import java. } } } Como se puede observar la fuente del evento en este nuevo caso va a ser por un lado. en este caso los eventos del ratón. show(). pack().out.addActionListener(this). La clase Ventana al implementar el interfaz ActionListener debe facilitar el método actionPerformed(). addWindowListener(new AdaptadorVentana()).println("Buenas tardes"). } Para finalizar el presente capítulo modificaremos de nuevo nuestra aplicación. La clase MouseAdapter es la clase adaptadora del interfaz MouseListener. boton. Aquí vamos a tratar un nuevo tipo de evento. public Ventana(String titulo){ super(titulo).awt. } public static void main (String[] args){ Ventana miVentana=new Ventana("Eventos"). add(boton).out. public class Ventana extends Frame implements ActionListener{ private Button boton. Las líneas utilizadas para añadir el botón se encuentran en el constructor.addMouseListener(new AdaptadorRaton()).event. private TextArea area. } //clase adaptadora para los eventos del ratón class AdaptadorRaton extends MouseAdapter{ public void mouseEntered(MouseEvent evento){ area. import java. que se ejecutará al pulsar el botón. el botón y su oyente la clase Ventana.*. en la que se escribirá si el puntero del ratón se encuentra en el área de texto. añadiendo un área de texto. y por otro lado la fuente de eventos va a ser la clase Ventana y el oyente la clase interna AdaptadorVentana.

El ratón está siendo arrastrado. Para registrar el oyente del área de texto se utiliza el método addMouseListener(). La tecla está oprimida o fue liberada.out. que va a ser el área de texto. respectivamente. y un nuevo oyente que es la clase adaptadora AdaptadorRaton. } public void windowClosing(WindowEvent evento){ dispose(). Cada vez que ocurre alguna acción de las que se listan a continuación se genera un evento que puede ser atrapado por un programa: • Eventos de ratón. El componente obtuvo o perdió el foco. El ratón esta sobre un componente.exit(0).out. El botón fue oprimido. • Eventos de checkbox. La clase Ventana ofrece un nuevo atributo que va a representar el área de texto. Esta clase implementa los métodos mouseEntered() y mouseExited() que se lanzarán cuando en ratón entre en el área de texto o salga del mismo.out. • Eventos de foco. Dentro del oidor se escribe el código que se desea ejecutar cuando ocurra el evento. Un evento es una acción que se genera al cambiar el estado de un componente de AWT. } public void windowIconified(WindowEvent evento){ System. System. Se dio un enter sobre el campo de texto. Alguna de las opciones del choice fue seleccionada.println("No estoy minimizada").println("Estoy minimizada"). } } } En esta última versión de la aplicación de prueba tenemos una nueva fuente de eventos. Un evento puede ser una entrada de usuario (movimientos o clicks del ratón. sin tener en cuenta el resto de los que se produzca y sin interferir entre sí. Java soporta varios tipos de eventos. El foco se obtiene cuando se da un click al ratón sobre un componente. Se está escribiendo sobre el campo de texto. • Eventos de choice. cambios en el medio ambiente del sistema (abrir. Para que un programa atienda un evento se necesita asignar al componente un oidor o escuchador (listener en la terminología de Java). pulsaciones de teclas). • Eventos de teclado. } public void windowDeiconified(WindowEvent evento){ System. El checkbox fue seleccionado. en este caso se trata de la clase AdaptadorRaton.println("La ventana se ha cerrado"). Como se puede apreciar cada oyente se encarga de los eventos para los cuales se ha registrado. El ratón se movió.public void windowClosed(WindowEvent evento){ System. 111 . El ratón salió de un componente. El botón del ratón está oprimido o fue liberado. • Eventos de botón. Los eventos es la forma que tiene un programa para enterarse de que ocurrió algo y reaccionar conforme al suceso. • Eventos de campo de texto. a este método se le pasa una instancia de la clase adaptadora que va a tratar los eventos del ratón. cerrar o mover una ventana) o cualquier otra actividad que pudiera afectar la operación del programa.

otra forma de tratar con los errores: las excepciones. el programa deberá: · volver a un estado estable y permitir otras operaciones. por ello.Hay dos estrategias para definir oidores: la primera es usar una clase que tenga un solo método por cada tipo de evento que atienda todos los eventos que se generen de ese tipo. Esto es difícil debido a que generalmente. el código crece considerablemente. En definitiva. int k=i/j. Si una operación no puede completarse debido a un error. que un método devuelva un código de error como valor de retorno. Esto presenta una serie de inconvenientes: · No siempre queda sitio para códigos de error (p. Java ofrece una solución. · Si se contemplan todos los errores. En mi opinión la ventaja de los oidores anónimos sobrepasa la desventaja y por eso en este curso se definen oidores anónimos para el manejo de eventos.: getchar). En las secciones siguientes vamos a revisar los eventos más utilizados que son los de botón.main(Excepcion. de choice y de checkbox. Respecto a la segunda estrategia. es decir. La ventaja de la primera estrategia es que el manejo de eventos se centraliza en un solo método. 10. · La lógica del programa queda oscurecida. muchas veces no se tenga en cuenta y no se traten todos los errores. La desventaja es que ese método debe ser capaz de distinguir cuál componente fue el que generó el evento. cada componente tiene su propio oidor y el código se vuelve más legible y elegante.lang. Esto impide la construcción de programas robustos. uno por cada componente que pueda generar el evento en particular. · No sirven en los constructores. hacen que el tratamiento de errores sea complejo y que. j=0.0 EXCEPCIONES 10. · No se garantiza que se vayan a consultar. la ventaja es que no es necesario distinguir nada. El que detecta el error debe informar al que pueda manejarlo. ej.java:4) 112 . La desventaja es que la cantidad de código generado se vuelve mayor.¿QUÉ SON LAS EXCEPCIONES? Definición: Una excepción es un evento excepcional que ocurre durante la ejecución del programa y que interrumpe el flujo normal de las sentencias. · intentar guardar el trabajo y finalizar.1. La segunda estrategia consiste en declarar oidores anónimos. Ejemplo: class Excepcion { public static void main(String argumentos[]) { int i=5. el código que detecta el error no es el que puede realizar dichas tareas.ArithmeticException: / by zero at Excepcion. // División por cero } } Produce la siguiente salida al ser ejecutado: java. La solución más habitual a esto son los códigos de error.

Para manejar excepciones hay que utilizar las palabras clave try y catch. ) throw new EOFException(). Es decir. lanzamos una excepción de E/S throw new IOException().2.. pero no se puede continuar hasta que no se resuelva. finaliza el método y se lanza un objeto que facilite información sobre el error ocurrido. Con la palabra throw se lanza una excepción. Se ponen entre un try y un catch los métodos que pueden producir alguna excepción. Java obliga a que un método informe de las excepciones (explícitas) que puede lanzar. Un método especifica las excepciones que se pueden producir en él mediante la palabra throws en la declaración del método.3. ) throw new FileNotFoundException(). no se sabe cómo manejarla. Es decir: try{ // Operaciones que pueden “fallar”.10.. Por ejemplo: void f() throws EOFException.actualizaDatos(). que // pueden producir alguna excepción. a. El bloque catch es el lugar al que se transfiere el control si alguna de las operaciones produce una excepción. // ya que una excepción es un objeto. se lanza una excepción (throw) para que alguien que sepa manejarla la trate en un contexto superior.. } 10. Por ejemplo: if( “error de CRC” ) // Si se produce un error de CRC.. } catch( <tipoDeExcepción> ref ){ // Tratamiento de esa excepción } Por ejemplo: try{ a.. El try delimita el grupo de operaciones que pueden producir excepciones. if( . es decir. cuando ocurre algo así. Un método no sólo tienen que decir qué devuelve si todo va bien. a. Cuando ocurre esto.. FileNotFoundException { if( .leeCabecera(). también debe indicar qué puede fallar. Normalmente se utilizará una clase diferente para cada tipo de error.MANEJO DE EXCEPCIONES Una vez que se detecta un error hace falta indicar quién se encarga de tratarlo. // Para lanzar una excepción hay que crear un objeto.DETECCIÓN DE ERRORES Una condición excepcional es aquella que impide la continuación de una operación. } 113 . En Java.abreFichero().

out. Por ejemplo. uno para cada tipo de excepción que se pueda producir. } void f2( int accion ) { try{ f1( accion ): } catch( EOFException e ){ System.out. Si no se produce ninguna excepción el bloque catch se ignora.catch( IOException ref ){ System.out.abreFichero().println( “Error de E/S” ).out.actualizaDatos(). la excepción se propaga hacia atrás en la secuencia de invocaciones hasta encontrar un catch adecuado. Un bloque try puede tener varios catch asociados. } Si alguna de las operaciones del bloque produce una excepción. else if( accion == 2 ) throw new EOFException(). Por ejemplo: void f1( int accion ) throws EOFException { try{ if( accion == 1 ) throw new FileNotFoundException().println( “Error corregido” ).out. } System. } Si se produce una excepción que no se corresponde con ningún catch indicado. } catch( FileNotFoundException e ){ System.out. } catch( IOException ref ){ System.println( “Error de E/S” ).println( “Error de apertura” ). } System.out. Al finalizar éste. a. se continúa normalmente. } catch( FileNotFoundException ref ){ System. } ¿ Qué pasa si se llama a f2 pasándole los valores 1 ó 2 para el parámetro acción? ¿En qué método se producen las excepciones y en cuál se tratan? 114 .println( “Finalización normal de f2” ). a.leeCabecera(). try{ a.println( “Error corregido” ).println( “Finalización normal de f1” ). se interrumpe el bloque try y se ejecuta el catch.

4. } Es mejor intentar tratar los errores en cuanto sea posible (alternativa 1).. } 115 . es obligatorio: · Que se maneje el error (en un catch). de la siguiente forma: try{ // Operaciones con posibles excepciones } catch( <tipoDeExcepcion> ref ){ // Tratamiento de la excepción } finally{ // Operaciones comunes } Ejemplo: class Recurso { void reserva(){ . Dichas operaciones se pueden situar dentro de un bloque finally.. o se trata el error o se avisa que se va a continuar sin haberlo corregido para que otro método lo corrija. De todos modos.Cuando se invoca a un método que lanza excepciones.. no siempre es posible tratarlos en el mismo momento en que se producen y por tanto... 10... void f2() throws IOException { f1(). en vez de dejarlos para que los gestione alguien por encima (alternativa 2). · Que se indique mediante throws su propagación. Es decir. } void libera(){ . } void f2() { try{ // Alternativa 1: tratar el error en cuanto se produce f1() } catch( IOException e ){ // Tratamiento del error } } // Alternativa 2: se continúa propagando el error para que lo gestione // otro método. a menudo hay que recurrir a la segunda alternativa.FINALLY Puede haber ocasiones en que se desea realizar alguna operación tanto si se producen excepciones como si no. Por ejemplo: void f1() throws IOException { .

} catch( ExceptionB b ){ // Tratamiento del error recurso.libera()).} class Ejemplo { void prueba() { Recurso recurso = new Recurso().libera().libera(). } catch( ExceptionA a ){ // Tratamiento del error recurso.libera(). } } } En el ejemplo anterior queda suficientemente claro que independientemente de que se produzca una excepción o no. // Operaciones con posibles excepciones } catch( ExceptionA a ){ // Tratamiento del error } catch( ExceptionB b ){ // Tratamiento del error } catch( ExceptionC c ){ // Tratamiento del error } finally{ 116 . la liberación del recurso siempre debe llevarse a cabo (instrucción recurso. } catch( ExceptionC c ){ // Tratamiento del error recurso. Solución con finally: class Ejemplo { void prueba() { Recurso recurso = new Recurso().reserva().libera().reserva(). e independientemente de que ésta sea capturada o no. Para ello existe una solución mucho más elegante. try{ recurso. try{ recurso. // Operaciones con posibles excepciones recurso.

j=2. De ella hereda una serie de métodos que dan información sobre cada excepción. } } } Si no se produce ninguna excepción.libera(). if (i/j< 1) throw new ArithmeticException(). } Ejemplo: class LanzaExcepcion { public static void main(String argumentos[]) throws ArithmeticException { int i=1.ArithmeticException at LanzaExcepcion. } } Genera el siguiente mensaje: java. · fillInStackTrace 117 . es decir. se ejecuta éste y luego el finally. se ejecuta el finally y la excepción se propaga al contexto anterior. else System. En cambio. interesa dar un tratamiento determinado a una excepción pero también interesa seguir propagándola hacia arriba para que en un contexto superior se siga teniendo constancia de ella. · Si no es atrapada.JERARQUÍA DE EXCEPCIONES Toda excepción debe ser una instancia de una clase derivada de la clase Throwable. Estos métodos son: · getMessage · toString · printStackTrace (imprime en la salida estándar un volcado de la pila de llamadas).java:5) La excepción pasaría a buscarse entre los catch de un contexto superior (los del mismo nivel se ignoran).recurso..out.println(i/j). si se produce alguna excepción: · Si es atrapada por un catch del mismo try. pero propagar throw e. 10.6. La ejecución continúa después del finally con normalidad.5. se ejecutarán el bloque try y el finally.main(LanzaExcepcion.LANZAMIENTO DE EXCEPCIONES Hay situaciones en las que interesa relanzar una excepción ya atrapada.. Esto se hace así: catch( Exception e ){ // Salvar datos o cualquier otro tratamiento. 10.lang.

Las clases derivadas de Error describen errores internos de la JVM e indican errores serios que normalmente la aplicación no debería intentar gestionar. Se utilizan las RunTimeException para indicar un error de programación. PrinterException. etc. Estas excepciones se clasifican en: · Explícitas: las que derivan directamente de Exception. a su vez. etc. · Implícitas: las que derivan de RunTimeException. tiene una subclase llamada RuntimeException que. ArrayStoreException. ThreadDeath. CannotUndoException. Ejemplos de este tipo de excepciones son OutOfMemoryError. IndexOutOfBoundsException. a su vez. IllegalArgumentException. la cual se divide en dos subclases: Error y Exception. NoSuchMethodException. Si se produce una 118 . Estas excepciones rara vez ocurren.) · RuntimeException (ArithmeticException. DataFormatException. Esta clase. NullPointerException. Los programas en Java trabajarán con las excepciones de la rama Exception. Tampoco deberían ser lanzadas por las clases del usuario. IllegalAccessException. lo único que se puede hacer es intentar cerrar el programa sin perder datos. etc. StackOverflowError. y cuando así sea. LinkageError.La jerarquía de excepciones en Java es la siguiente: · Throwable · Error (AWTError. CloneNotSupportedException. VirtualMachineError) · Exception (ClassNotFoundException.) Que representada en forma gráfica da lugar a: Figura Jerarquía de clases de las excepciones Java Todas las excepciones descienden de la clase Throwable. tiene otras clases derivadas. CannotRedoException. EmptyStackException. ClassCastException. IOException. GeneralSecurityException. NoSuchFieldException.

En la práctica. Por ejemplo: un cast incorrecto. cuando un método declara una excepción. Por ejemplo: un error de E/S. 10. se lanzará. acceso a un array fuera de rango. Por tanto. está avisando de que puede producirse dicho error (por causas externas al método) además de cualquier error implícito (consecuencia de un error en el código que debería ser subsanado).. etc. Los métodos deben declarar sólo las excepciones explícitas. normalmente derivará: · de RuntimeException si se desea notificar un error de programación. aunque pueden producirse igualmente). como se puede ver en el siguiente ejemplo: class Ejemplo { void imprimirClientes( Cliente[] clientes ) throws PrinterException { for( int i=0. i++ ) { // Imprimir cliente[i] if( “error impresión” ) throw new PrinterException(). · de Exception en cualquier otro caso. } } } Vemos ahora otro ejemplo en el que creamos y lanzamos una excepción de tipo RuntimeException: class MesInvalidoException extends RuntimeException {} class Ejemplo { 119 .7. En un método que tiene como parámetro un entero entre 1 y 12 se recibe un 14 ¿Qué tipo de excepción se crearía para notificarlo? Si un método para listar Clientes por impresora se encuentra con que deja de responder. El resto de las Exception indican que ha ocurrido algún error debido a alguna causa ajena al programa (está correcto).CREACIÓN DE EXCEPCIONES Si se necesita notificar algún error no contemplado en Java se puede crear una nueva clase de Excepción. Las implícitas no deben declararse (el compilador no lo exige.length. etc. La única condición es que la clase derive de la clase Throwable o de alguna derivada. error de conexión. ¿qué tipo de excepción debería crear? Ejemplo de creación de una excepción: class PrinterException extends Exception{} Cuando se produzca una excepción de este tipo.excepción de este tipo hay que arreglar el código. i<clientes. uso de un puntero null.

} } En este ejemplo no se avisa que se lanza una excepción porque es de tipo RuntimeException (implícita).OBTENCIÓN DE INFORMACIÓN DE UNA EXCEPCIÓN Una excepción.println( “Sólo se han imprimido “ + e.out. puede tener operaciones que permitan obtener más información sobre el error.. } } } 120 . } } void Informe() { try{ imprimirClientes( clientes ).length. pagina<clientes. 10. pagina++ ) { // Imprimir cliente[ pagina ] if( “error impresión” ) throw new PrinterException( pagina ).void setMes( int mes ) { if( mes < 1 || mes > 12 ) throw new MesInvalidoException(). } int getNumPagina() { return numPagina. } catch( PrinterException e ) { System.8.getNumPagina() ). como cualquier otra clase. } } class Ejemplo { void imprimirClientes( Cliente[] clientes ) throws PrinterException { for( int pagina = 0. PrinterException( int pag ) { numPagina = pag. class PrinterException extends Exception { private int numPagina.

Otro ejemplo con una excepción descendiente de RuntimeException: class MesInvalidoException extends RuntimeException { private int mes. que devuelve // la representación String del error. } } class Ejemplo { public static void main( String[] args ) { Fecha fecha = new Fecha(). ¿qué pasa si no se atrapa una implícita. } getMesInvalido() { return mes. } } class Fecha { void setMes( int m ) { if( m < 1 || m > 12 ) throw new MesInvalidoException( m ). se visualizará el mensaje “Mes inválido: 14” y el programador deberá darse cuenta de que existe un error en el código que llama al método setMes(). { return “Mes inválido: “ + mes. MesInvalidoException( int m ) { mes = m. Algunos de estos métodos son: · public String getMessage() Devuelve el mensaje detallado de error de este objeto (o null si el objeto no tiene mensaje). fecha. como en este caso? · El compilador no da ningún error por tratarse de una excepción implícita y el programa compila correctamente. 121 .. } public String toString() // Método heredado de la clase Throwable. y que pueden ser implementados por las nuevas excepciones que se creen. · La excepción MesInvalidoException se produce al invocar el método setMes() con el valor 14 y al no haber sido capturada por el código que realiza la invocación (el main). } } Java obliga a atrapar las excepciones explícitas. el programa termina de manera anormal. La clase Throwable tiene una serie de métodos que tienen ya implementados todas las excepciones de Java (por herencia). · Por defecto.setMes( 14 ).

9. 122 .RESUMEN EXCEPCIONES EXPLÍCITAS-IMPLÍCITAS Excepciones explícitas: · Derivan de Exception (no de RuntimeException).. · public void printStackTrace() Visualiza en el flujo de error estándar la traza de llamadas (backtrace) que se almacena automáticamente en todo error. · Se pueden atrapar.EXCEPCIONES Y HERENCIA Al redefinir un método se está dando otra implementación para un mismo mensaje. Excepciones implícitas: · Derivan de RuntimeException. un hijo puede lanzar menos excepciones que las que declara el padre. · Si se invoca un método que las lanza. básicamente). · public native Throwable fillInStackTrace() "Rellena" la pila de llamadas. no puede lanzar una excepción que no lanzaba el padre. al no heredarse. pero pensado para devolver el mensaje localizado de una forma más específica. un método puede declarar excepciones que no lance realmente.. · Si se lanzan es obligatorio declararlas. no puede lanzar nuevas excepciones. Es útil cuando un error que se ha producido en un lugar queremos que se indique en otro. es decir. Así un método base puede permitir que las redefiniciones puedan producir excepciones. · Indican un error externo a la aplicación.10. · public void printStackTrace(PrintStream s) Lo mismo. sí pueden lanzar nuevas excepciones.· public String getLocalizedMessage() Lo mismo. pero indicando el flujo al que se manda la información. 10. · Indican un error de programación. FileNotFoundException {} } Es decir. Por otro lado. El nuevo método sólo podrá lanzar excepciones declaradas en el método original. si no simplemente se comporta como getMessage() · public String toString() Devuelve la representación String del error (la clase del error. En caso contrario finaliza la aplicación. 10. · public void printStackTrace(PrintWriter s) Idem. · No se declaran: se corrigen. devuelve el error cambiando su traza de llamadas como si se hubiera producido ahora. Hay que redefinirlo. class A { void f() throws EOFException { } } class B extends A { // Incorrecto void f() throws EOFException. Los constructores. es obligatorio atraparlas o declararlas.

11. · Es más cómodo usar métodos que no produzcan errores.. } catch(ExcB b) { // Tratamiento } } try { for (int i = 0. } } catch(ExcA a) { // Tratamiento } catch(ExcB b) { // Tratamiento } Norma 4: · No ignorar una excepción try { // operaciones.mesg1(). for (int i = 0. } catch(ExcA a) { // Tratamiento } try { obj2.mesg2(). i < len.mesg2(). } catch {EmptyStackExecution e) { } // Para que calle el compilador 123 .mesg1().pop().. obj2.10. Norma 2: · No utilizar las excepciones para evitar una consulta.NORMAS EN EL USO DE EXCEPCIONES Norma 1: · Si una excepción se puede manejar no debe propagarse. No abusar de ellas.. Norma 3: · Separar el tratamiento de errores de la lógica. } catch {EmptyStackExecution e) { . i < len.pop().empty()) stack. try { stack. } while (!stack. i++) { try { obj1. i++) { obj1.

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

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

( 4 3 2.out. error en ejecución" )..println( l ).. } } Salida: La salida resultado de ejecutar este main() sería la siguiente: Error: intento de borrar primero en lista vacía." ).out.getMessage() + ".5 2 1 ) FueraDeListaException: intento de borrar primero en lista vacía at ListaEnlazada. } System.borrarPrimero().println( "Error: " + e. } finally { l..println( "Aquí no se llega.borrarPrimero(ListaEnlazada. l. Continuamos.. Error: intentando insertar elemento más allá del fin de la lista. // de este error no se recupera el programa System... Continuamos.java:66) 126 .java:42) at ListaEnlazada.insertarPrimero( new Integer(18) ).out.main(ListaEnlazada.destruir()...} catch (FueraDeListaException e) { System. Continuamos. l.

11. class UnThread { public static void main(String args[] ) { int i.1 Un ejemplo. todos los programas creados contenían un único thread. la memoria se comparte. utilizando el mismo contexto y recursos asignados al programa (también disponen de variables y atributos locales al thread). sino que se ejecuta dentro de un programa o proceso. i++) System. en los threads lanzados desde un mismo programa.out.0 THREADS.out. for (i=1. } } La salida de este programa es una serie alternativa de Síes 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 ejecución como un proceso con un único thread mediante su método main().1 ¿Qué son los threads? Todos los programadores conocen lo que es un proceso. Después se inicia su ejecución mediante la llamada al método de la clase 127 . 11. t. pero un programa (o proceso) puede iniciar la ejecución de varios de ellos concurrentemente. Hasta el momento. como ocurría en todos los programas vistos hasta ahora. NoThread t = new NoThread(). Mientras en los procesos concurrentes cada uno de ellos dispone de su propia memoria y contexto. una secuencia de instrucciones y tiene un final. Puede parecer que es lo mismo que la ejecución de varios procesos concurrentemente a modo del fork() en UNIX.print("NO ").print("SI ").1. se declara y se crea un thread (NoThread t = new NoThread().11. pero existe una diferencia. En este proceso. Un thread no puede existir independientemente de un programa. Un thread39 es un flujo simple de ejecución dentro de un programa. } } class NoThread extends Thread { public void run() { int i. ).start().i<=20. i<=20. for (i=1. mejor que mil palabras. i++) System. la mayoría diría que es un programa en ejecución: tiene un principio.

n. Una vez se inicia la ejecución del thread. SiThread s = new SiThread(). } } class NoThread extends Thread { public void run() { int i. Tenemos dos threads ejecutándose.out. } } 128 .Thread start() (t.i<=20.2 Otro ejemplo  class DosThreads { public static void main(String args[] ) { NoThread n = new NoThread().print("NO "). s. 11. i++) System.start().1.start().start() ). se intercalan instrucciones del método main() con instrucciones del método run() entre otras correspondientes a otros procesos (del sistema operativo y otros procesos de usuario que pudieran estar ejecutándose). for (i=1. con lo cuál. el tiempo de la CPU se reparte entre todos los procesos y threads del sistema. con lo cuál comienza a ejecutarse el método run() redefinido en la clase NoThread (el método start() llama al método run() ).

i<=20. i++) System. i++) System.start().print(++Contador+":"+SiNo+" ").class SiThread extends Thread { public void run() { int i.out. Una vez que finalizan su ejecución. n. el programa termina.start(). … Esto es debido a que se ha accedido concurrentemente a una misma zona de memoria sin que se produzca exclusión mutua. Cada una de ellas con sus propios atributos de objeto (String SiNo). for (i=1. public SiNoThread(String s) { super().8. 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 ejecución mediante los métodos start().print("SI "). 129 . s.4.3. pero comparten los atributos de clase40 (int Contador).6. SiNo=s.3 Y otro ejemplo más. class UnThreadDosInstancias { public static void main(String args[] ) { SiNoThread s = new SiNoThread("SI").out.5. Estos dos threads se reparten el tiempo de la CPU y se ejecutan concurrentemente. } } La salida del programa. al igual que en el ejemplo anterior.1.2. static int Contador=0. } } 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. } } class SiNoThread extends Thread { private String SiNo. for (i=1. Puede comprobarse que el contador no ha funcionado todo lo correctamente que pudiera esperarse: 1. SiNoThread n = new SiNoThread("NO"). } public void run() { int i.i<=20. 11.

La llamada al método start() asigna los recursos necesarios al objeto. El ciclo de vida de un thread puede pasar por varios estados ilustrados en la siguiente figura: Cuando se instancia un thread. Un thread en este estado únicamente acepta las llamadas a los métodos start() o stop(). momento en el cual. Está en el estado nuevo Thread. proseguirá su ejecución.2 Estado de un thread. Un thread en estado ejecutable puede pasar al estado no ejecutable por alguna de las siguientes razones: que sean invocados alguno de sus métodos sleep() o suspend(). 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 método sleep(). lo sitúa en el estado ejecutable y llama al método run() del objeto. que el thread haga uso de su método wait() o que el thread esté bloqueado esperando una operación de entrada/salida o que se le asigne algún recurso. 130 . Esto no significa que el thread esté jecutándose (existen multitud de sistemas que poseen una sola CPU que debe ser compartida por todos los threads y procesos) sino que está en disposición de ejecutarse en cuanto la CPU le conceda su tiempo.11. ejecutable o no ejecutable). se inicializa sin asignarle recursos. el thread pasará al estado ejecutable y. Un thread puede pasar al estado muerto por dos motivos: que finalice normalmente su método run() o que se llame a su método stop() desde cualquiera de sus posibles estados (nuevo thread. si se le asigna la CPU.

esperando: que después de una llamada a wait() se continúe su ejecución con notify() o notifyAll(). bloqueado: una vez finalizada una espera sobre una operación de E/S o sobre algún recurso. 11. public void start() { t = new Thread(this). } public void run() { int i. 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.start(). for (i=1.suspendido: que. después de haber sido suspendido mediante el método suspend(). La forma en que se crean los threads como subclase de Thread ya se ha visto en puntos anteriores.i++) System. más evidente y sencilla. cuando la clase declarada no tenga que ser subclase de ninguna otra superclase. Nota: Estos dos metodos.i<=50.println(i). ya casi no se usan en java2.3 Creación de threads. Se utilizará la primera forma. sea continuado mediante la llamada a su método resume().out. t. Creación de threads utilizando la interface Runnable: class PrimerThread implements Runnable { Thread t. 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 método run() y start() de la interface. } } class SegundoThread extends PrimerThread { } class ThreadRunnable { 131 .

start(). 11. Devuelve el thread que se está ejecutando acutalmente. como ocurre en el ejemplo: SegundoThread es subclase de PrimerThread. String nombre). 132 .public static void main(String args[]) { SegundoThread s = new SegundoThread(). 3) Hay que redefinir el método start(): Instanciar el atributo de la clase Thread llamando a su constructor pasándole como parámetro la propia instancia de clase ( this): t = new Thread(this).4.start(). Devuelve false si el thread está en el estado nuevo thread o muerto y true en caso contrario. Ambas formas de crear threads admiten un parámetro de tipo String que identifica al thread por su nombre: public Thread(String nombre). ya puede ser instanciada e iniciada como cualquier thread. s.start(). Una vez declarada la clase que implementa la interface Runnable. Para poder después averiguarlo mediante el método: public final String getName(). ss. Iniciar el thread: t. cualquier subclase descendiente de esta clase poseerá también las características propias de los threads. Si no se ha asignado nombre a los threads. y por lo tanto.4. Metodología para la creación 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 método start() ) de la clase Thread: Thread t. 4) Redefinir el método run() tal y como se hace en la otra alternativa de creación de threads (mediante subclases de Thread). Es más. public static Thread currentThread(). public Thread(Runnable destino. … 11.4 Operaciones sobre threads. public final boolean isAlive().1 currentThread(). 11. } } En este caso se utiliza el constructor de la clase Thread: public Thread(Runnable destino).2 isAlive(). la clase Thread se los asigna por defecto como Thread-1. SegundoThread ss = new SegundoThread(). ambas son Thread. Thread-2.

println("isAlive() después de ejecución: "+ t. int nanosegundos) throws InterruptedException. 11.println("isAlive() antes de iniciar: "+ t. el estado del mismo pasa de ejecutable a suspendido inmediatamente y sólo puede ser reactivado (pasado al estado ejecutable) llamando a su método resume().isAlive()). 133 .isAlive()). System. class MiThread extends Thread { public void run() { try { sleep(2000).sleep(3000). // de esta forma. Una vez que se ha cumplido el tiempo.3 sleep().isAlive()).4 suspend().out. En ambos casos se lanzará la excepción InterruptedException si se llama al método interrupt() del thread.4. t. } catch (InterruptedException e) { } } } class Vivo { public static void main(String args[]) { MiThread t = new MiThread(). 2) Acepta un parámetro más: un número entero de nanosegundos (0 … 999999 ) que se sumaran a los milisegundos de espera. try { // Hace "dormir" al thread actual Thread.out. 2) public static void sleep(long milisegundos. public final void suspend().4. Llamando al método suspend() de un thread. } } La salida correspondiente al programa es: Estado antes de iniciar: false Estado en ejecución: true Estado después de ejecución: false 11. 1) public static void sleep(long milisegundos) throws InterruptedException.start().currentThread().out. System. La llamada al método resume() de un thread que está dormido no tiene ningún efecto. 1) Hace que el thread actual pase del estado ejecutable a dormido y permanezca en dicho estado durante los milisegundos especificados como parámetro. le da tiempo a terminar al // thread t } catch (InterruptedException e) { } System. el thread “despierta” y pasa automáticamente al estado de ejecutable.println("isAlive() en ejecución: "+ t.

start(). static int Contador=0. Esto no significa que el otro thread continúe su ejecución hasta llegar a su propio yield(). i++) { System. Para cada salida por pantalla. public static void yield(). 3) El thread estará “esperando” hasta que sea notificado o expire el timeout (milisegundos + nanosegundos). SiNoThreadAmable n = new SiNoThreadAmable("NO").4. pero sí que está más 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 . (System. 1) public final void wait() throws InterruptedException. El método wait() es heredado de la superclase Object.6 yield(). } public void run() { int i. int nanosegundos) throws InterruptedException 1) Causa el paso al estado “esperando” del thread indefinidamente hasta que el thread sea notificado mediante notify() o notifyAll().println(++Contador+”:”SiNo+” “). for (i=1.out. ) el thread cede la CPU al otro thread mediante la llamada a yield(). 11.start(). 2) El thread estará “esperando” hasta que sea notificado o expire el timeout (milisegundos).11. yield(). } } } En este caso hay dos threads que se ejecutan concurrentemente.5 wait(). Transfiere el control al siguiente Thread en el scheduler 41 (con la misma prioridad que el actual) que se encuentre en estado ejecutable.4. 3) public final void wait(long milisegundos. s. SiNo=s. n. class UnThreadDosInstanciasAmables { public static void main(String args[] ) { SiNoThreadAmable s = new SiNoThreadAmable("SI").print(++Contador+":"+SiNo+" "). 2) public final void wait(long milisegundos) throws InterruptedException. public SiNoThreadAmable(String s) { super().i<=20.out. } } class SiNoThreadAmable extends Thread { private String SiNo. de la cuál heredan todos sus métodos todas las clases creadas en Java. ya que puede perder la CPU antes y por consiguiente no tienen porqué salir los Síes y Noes alternativamente.

out. public final synchronized void join(long miliseg. public final void join() throws InterruptedException. 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. En estos dos casos. 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().println("El thread ha terminado"). } } El thread de la clase Join1 espera indefinidamente hasta que finalice el thread t. t. System.start().4.join(). t.11.7 join(). for (i=1. } } class Join1 { public static void main (String args[]) throws InterruptedException { MiThread t = new MiThread().out. el thread de la clase Join1 no habría esperado la finalización de t y la salida por pantalla habría 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 métodos join() más:  public final synchronized void join(long miliseg) throws InterruptedException.print(i+" ").join(). i++) System. 135 .i<=100. el thread actual no espera indefinidamente sino que reinicia su ejecución en el instante en que se finalice el thread sobre el que se hace el join() o pase el tiempo especificado por los parámetros miliseg (milisegundos) y opcionalmente nanoseg (nanosegundos). int nanoseg) throws InterruptedException.  class MiThread extends Thread { public void run() { int i.

Existen. Se llama sección crítica a los segmentos de código dentro de un programa que acceden a zonas de memoria comunes desde distintos threads que se ejecutan concurrentemente 136 . Este flujo de datos puede ser almacenado en una zona de memoria compartida por ambos threads. 11. puede encontrarse con que no hay datos que consumir o que se consuman más de una vez (si no elimina los datos consumidos. En este caso. debe existir un mecanismo que “frene” al productor o al consumidor en los casos necesarios. si el consumidor es más rápido que el productor.1 El paradigma productor/consumidor. El paradigma productor/consumidor consiste en la ejecución de un thread que produce un flujo de datos que deben ser consumidos por otro thread. por otra parte.5 Threads y sincronismo. sin embargo. Hasta el momento se ha estado tratando con threads asíncronos. sino que los elimina el productor).5. situaciones en las que es imprescindible sincronizar en cierta forma la ejecución de distintos threads que buscan un objetivo común.11. ya que de lo contrario. cada uno de ellos se ejecutaba independientemente de los demás. si el productor genera los datos más deprisa de lo que el consumidor es capaz de tratarlos.5. puede producirse un desbordamiento de la memoria asignada al efecto.2 Sección crítica. En este caso. 11. cooperando entre ellos para su correcto funcionamiento. la sincronización es vital. que se ejecutaban concurrentemente.

} public long getValor() { return valor. } } class Contable extends Thread { Contador contador. aux=valor.start(). public void incrementa() { long aux. public Contable (Contador c) { contador=c.start(). c2 . long aux. class Contador { private long valor=0.En Java. las secciones críticas se marcan con la palabra reservada synchronized. valor=aux.out. } } class ThreadSinSync { public static void main(String arg[]) { Contable c1 . c1 = new Contable(c). Aunque está permitido marcar bloques de código más pequeños que un método como synchronized.i<=100000. c2. i++) { contador.getValor()). } } 137 . } public void run () { int i.incrementa(). es preferible hacerlo a nivel de método. } System. para seguir una buena metodología de programación. for (i=1.println("Contado hasta ahora: "+ contador. Contador c = new Contador(). aux++. c2 = new Contable(c). c1.

ya que desde esta instrucción se accede a un objeto (contador) que es compartido por ambos threads (c1 y c2). la sección crítica es la línea: contador. esto mismo ha ocurrido un gran número de veces durante la ejecución de los threads. Simplemente cambiando la línea de código: public void incrementa() { por public synchronized void incrementa() { se habría conseguido bloquear el método incrementa. La salida por pantalla es: Contado hasta ahora: 124739 Contado hasta ahora: 158049 cuando debería haber sido: en la primera línea un número comprendido entre 100000 y 200000. y en la segunda línea el valor 200000. a efectos didácticos. 138 .Para realizar el incremento en el método incrementa(). aunque el efecto de haber codificado valor++ habría sido similar. de forma que no pudieran ejecutarlo simultáneamente dos threads sobre el mismo objeto (contador). se ha empleado una variable auxiliar intermedia (aux).incrementa(). Lo que ha ocurrido en realidad para que se produzca este resultado equivocado es lo siguiente Evidentemente. En este caso.

public Productor(Caja nc) { c = nc. Esto no impide que el propietario del monitor ejecute otro método synchronized de ese mismo objeto. Cuando un thread está ejecutando un método synchronized de un objeto. } } class Consumidor extends Thread { Caja c.println("sacado el valor: "+valor). public Consumidor(Caja nc) { c = nc. valor=0. for (i=1. i++) c. public synchronized void meter(int nv) { valor = nv.out. class Caja { private int valor. cada objeto que posee un método synchronized posee un único monitor para ese objeto.meter(i). System.out.3 Monitores.println("metido el valor: "+valor). En Java.La salida del programa habría sido (correcta): Contado hasta ahora: 186837 Contado hasta ahora: 200000 11. } public synchronized void sacar() { System. } public void run() { int i. se convierte en el propietario del monitor. evitando que cualquier otro thread ejecute ningún otro método synchronized sobre ese mismo objeto. i<=10. } public void run() { 139 . adquiriendo de nuevo el monitor (llamada a un método synchronized desde otro método synchronized).5. } } class Productor extends Thread { Caja c.

start(). ya que el productor puede almacenar varios valores antes de que el consumidor extraiga el valor o. se declaran los métodos meter y sacar como synchronized. que el consumidor intente sacar varios valores consecutivamente. } } class ProdCons { public static void main(String argum[]) { Caja cj = new Caja(). por lo que la salida por pantalla podría 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 habría que asegurar que no se meta ningún valor si la caja está llena y que no se saque ningún valor si la caja está vacía. Tanto el thread productor como el consumidor comparten el mismo objeto de la clase Caja. Pero esto no es suficiente para que el programa funcione correctamente. p. Sobre esta caja se pueden realizar dos tipos de operaciones:  meter(int): Almacena el entero que se le pasa como parámetro y muestra el valor en pantalla. sacar(): Muestra en pantalla el valor almacenado y pone ese valor a cero. también. Para asegurar la exclusión mutua en la zona crítica (la que accede a valor). i++) c. } } En este ejemplo existen dos threads que se comportan siguiendo el paradigma productor/consumidor.sacar(). for (i=1.start(). Consumidor c = new Consumidor(cj). Productor p = new Productor(cj). 140 . i<=10. Existe un objeto de la clase Caja que es capaz de almacenar un único número entero.int i. c.

} } class Consumidor extends Thread { Caja c. disponible=true. i++) c.class Caja { private int valor. } public void run() { int i. } } public synchronized void sacar() { if (disponible) { System.start(). } } class ProdCons2 { public static void main(String argum[]) { Caja cj = new Caja(). p.println("sacado el valor: "+valor). termina su ejecución. disponible=false. valor=0. el productor introduce el siguiente valor (el 2). Consumidor c = new Consumidor(cj). i<=10. y sigue su ejecución hasta terminar el bucle. Productor p = new Productor(cj). i<=10. como el consumidor ya ha terminado su ejecución no se pueden introducir más valores y el programa termina. el consumidor ha seguido su ejecución en el bucle y como no se mete ningún valor por el consumidor.out. for (i=1. public Productor(Caja nc) { c = nc.start().meter(i). } } } class Productor extends Thread { Caja c.out. System. 141 . } } Salida por pantalla: metido el valor: 1 sacado el valor: 1 metido el valor: 2 Después de sacar el valor 1. private boolean disponible=false. public synchronized void meter(int nv) { if (!disponible) { valor = nv.sacar(). } public void run() { int i.println("metido el valor: "+valor). for (i=1. public Consumidor(Caja nc) { c = nc. c. i++) c.

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

143 . Realiza la operación correspondiente y ejecuta el método notify() que no tiene ningún efecto. se completa el método.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 método meter(i): la variable disponible contendrá el valor false y no se ejecutará wait(). se ejecuta el método wait() el método 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. se cambia el valor de disponible a true y se llama al método notify() el método notify() hace que el thread que está en estado de espera pase a ejecutable y tome el monitor del objeto. no se ejecuta el wait(). Si el primer thread en adquirir el monitor del objeto Caja es el Consumidor al ejecutar el método sacar(): La variable disponible contendrá el valor false y por lo tanto.

Hasta ahora se ha supuesto que todos los threads se repartían el tiempo de la CPU por igual. for(i=1. se le asigna directamente la CPU. Se repartirán la CPU por igual sólo si la prioridad de los distintos thread es la misma. prioridad mínima de un thread public final static int NORM_PRIORITY = 5. t1. setPriority(pri). 225 nombre=n. public final int getPriority().11. } public void run() { int i.i++) { System. prioridad normal (por defecto) de un thread public final void setPriority(int newPriority).5). Threads. ThreadConPrioridad t2 = new ThreadConPrioridad("2".out. prioridad máxima de un thread public final static int MIN_PRIORITY = 1. sin embargo esta prioridad puede ser modificada en cualquier momento llamando al método setPriority(). Para conocer la prioridad de un thread se utiliza el método getPriority(). Además este algoritmo es explusivo. El algoritmo de planificación (scheduling) es el siguiente: la CPU se asigna al thread en estado ejecutable de mayor prioridad y si hay varios. t2. se reparten el tiempo mediante round-robin. si se está ejecutando un thread y en ese momento pasa a estado ejecutable un thread de mayor prioridad. public final static int MAX_PRIORITY = 10.start(). no debe confiarse la “no inanición” a la bondad del Java. pero esto no es generalmente así. } } 144 . public ThreadConPrioridad(String n .i<100. } } } class Prioridad1 { public static void main(String args[]) { ThreadConPrioridad t1 = new ThreadConPrioridad("1". class ThreadConPrioridad extends Thread { String nombre. Este algoritmo tiene un peligro: que un thread nunca se ejecute porque siempre existan threads de mayor prioridad. De todas formas. Java evita esto asignando la CPU también a threads de menor prioridad aunque cada mucho menos tiempo que a los threads de mayor prioridad.5). es decir.6 Prioridad de un thread. yield(). En todos los ejemplos que se han visto anteriormente los threads tenían la misma prioridad porque cuando se crea un thread. int pri) { super().start(). éste hereda la prioridad del thread que lo creó.print(nombre).

7 Threads Daemon.Salida por pantalla: 221211122122212121212121112221212121212121212121212121 212121212121212121212122121212121212121121212121221212 121212121212121212112121212121212121212121221212211212 121212121211212121212121212121221111 En este caso. Para saber si un thread es un Daemon se utiliza el método: public final boolean isDaemon().5). ThreadConPrioridad t2 = new ThreadConPrioridad("2". la mayoría del tiempo de la CPU es acaparada por el thread de mayor prioridad. La llamada al método setDaemon(true) hace que el thread se considere Daemon y setDaemon(false) hace que deje de serlo. los threads t1 y t2 tienen la misma prioridad (5). Normalmente ejecuta un bucle infinito que realiza algún tipo de operación y tiene una prioridad baja (se ejecuta en background). a pesar de ejecutar un bucle infinito. Un daemon. public final void setDaemon(boolean on). también llamado demonio o servicio es un thread que proporciona servicios al resto de threads que se ejecutan en el mismo proceso que el daemon. La salida por pantalla habría sido diferente: 111111111111111111111111111111111111111111111111111111 111111111111111111121212121212121212111111111111111111 222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222 Como puede observarse.4). Si las prioridades hubieran sido otras. por lo que al llamar al método yield() se cede la ejecución al otro thread. 145 . termina su ejecución cuando sólo quedan threads que son de tipo daemon en ejecución. Por ejemplo: ThreadConPrioridad t1 = new ThreadConPrioridad("1". Cualquier thread puede ser un daemon. Unicamente se diferencian de los threads normales en que. 11.

por ejemplo: iniciar o detener varios threads mediante una sola operación. Un grupo de threads puede contener threads. 146 . Java crea un grupo de threads llamado main. Hay que señalar que los grupos de threads no añaden ningún tipo de funcionalidad.1 Creación de grupos de threads. String nombre). Este grupo pertenecerá (subgrupo) al grupo actual (al que pertenece el thread que realiza la llamada al contructor). Esa es la razón por la cual todos los threads creados hasta el momento pertenecen al grupo main. Cuando se inicia la ejecución de un programa. se añade al grupo al que pertenece el thread que lo creó.8.lang.11. Los grupos de threads sirven para manejar como una unidad a un conjunto de threads. 2) public ThreadGroup(ThreadGroup padre. pero también grupos. cuando se crea un thread. 1) Crea un grupo cuyo nombre se pasa como parámetro. y todos ellos pertenecen a un grupo raíz: main. Mediante llamadas al constructor: 1) public ThreadGroup(String nombre). 11.8 Grupos de threads. únicamente sirven para que sea más cómodo el manejo muchos threads en un programa. Todo thread creado en Java pertenece a un Grupo de threads. A no ser que se especifique lo contrario. Para ello se utiliza la clase ThreadGroup que pertenece al paquete java.

Ejemplo: ThreadGroup tg2 = new ThreadGroup( tg .8.2. Un grupo daemon es destruido automáticamente cuando no contiene ningún thread o grupo. Convierte al grupo en daemon si el parámetro es true43. Es posible obtener el grupo al que pertenece un thread mediante la llamada al método getThreadGroup() de la clase Thread:  class Grupo1 { public static void main(String args[]) { ThreadGroup tg = Thread. aunque los threads descendientes que ya pertenecían al grupo antes de llamar al método setMaxPriority() pueden conservar su prioridad mayor que el parámetro (pri). public Thread(ThreadGroup grupo.1 Asignación de un thread a un grupo. Runnable destino. Establece la máxima prioridad que puede tener un thread descendiente de este grupo. La salida por pantalla. public final void resume().1.2. Este grupo pertenecerá al grupo que se pasa como parámetro (padre).1 Operaciones sobre threads. public final boolean isDaemon().getName()).2.Ejemplo: ThreadGroup tg = new ThreadGroup(“MiGrupo”).println(tg.8. String nombreDelThread). Pasan al estado de “ejecutable” todos los threadsdescendientes de este grupo que estaban en estado “suspendido”. Como se ha visto. public final void suspend(). 11.2 Operaciones sobre un conjunto de threads.currentThread(). Esto se realiza en la llamada al constructor del thread: public Thread(ThreadGroup grupo.2 Operaciones sobre grupos. System. evidentemente es: main 11. Pasan al estado “muerto” todos los threads descendientes de este grupo. 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 explícitamente a qué grupo debe pertenecer.2. 11.8.2 Operaciones sobre threads agrupados. 11. public final void setDaemon(boolean daemon).getThreadGroup(). } } El método getName() devuelve un String que contiene el nombre del grupo. Devuelve true si el grupo es un daemon y false en caso contrario. 2) Crea un grupo cuyo nombre se pasa como parámetro. “MiGrupo2”). public final void stop().1.out. public final void setMaxPriority(int pri). 11. Pasan al estado de “suspendido” todos los threads descendientes de este grupo (todos los threads pertenecientes a este grupo) y se llama al método suspend() de todos los grupos pertenecientes a este grupo ( valga la redundancia).8. 147 .8. String nombreDelThread).

2").3 Obtención del contenido de un grupo. } public void run() { System. 2) public int enumerate(Thread lista[].println("Ejecutado "+getName()).8. Por defecto. MiThread t22 = new MiThread(tg2.println(t[i]). for (i=0.2"). MiThread t12 = new MiThread(tg. añadiendo los threads o grupos del grupo sobre el que se realiza la llamada y de los grupos descendientes del mismo. MiThread t1 = new MiThread(tg.out. boolean recurr). Si el grupo no está vacío. boolean recurr). 1) public int enumerate(Thread lista[]). Thread t[] = new Thread[4]."Thread 2. 3) public int enumerate(ThreadGroup lista[]).out. int i.public final void destroy() throws IllegalThreadStateException.  class MiThread extends Thread { public MiThread(ThreadGroup tg. Para obtener únicamente los threads pertenecientes al grupo en cuestión es necesario pasar un nuevo parámetro de tipo bolean (recurr) con el valor false.i<t. ThreadGroup tg2 = new ThreadGroup(tg. public final boolean parentOf(ThreadGroup g)."Thread 1. String nom) { super(tg.length. hay que llamar antes al método stop() del grupo44 para que todos los threads descendientes pasen al estado “muerto”. 11.1"). tg. Este grupo debe estar vacío: no debe contener threads activos."grupo dos").enumerate(t)."Thread 2.1"). de lo contrario se genera una excepción de la clase IllegalThreadStateException.i++) System."Thread 1. } } class Grupo2 { public static void main(String args[]) throws InterruptedException { ThreadGroup tg = new ThreadGroup("grupo uno"). Devuelve true si este grupo es padre (o es el mismo) que el grupo que se pasa como parámetro (g) y false en caso contrario. se obtienen listas de threads (y grupos) recursivas. } } 148 .2. Destruye el grupo (pero no los threads descendientes). El método 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 parámetro lista[]. MiThread t21 = new MiThread(tg2. 4) public int enumerate(ThreadGroup lista[].nom).

5.5.1. la salida por pantalla habría sido: Thread[Thread 1.5.1. la salida por pantalla es la siguiente: Thread[Thread 1.grupo uno] Thread[Thread 2.1.En este caso.2.5.2.5.grupo uno] null null 149 .grupo dos] Si en lugar de enumerate(t) se hubiera utilizado la fórmula enumerate(t.grupo uno] Thread[Thread 1. false).grupo uno] Thread[Thread 1.grupo dos] Thread[Thread 2.2.5.

los componentes atómicos. El primer anuncio de JFC se realizó en la conferencia de desarrolladores JavaOne y se definió mediante las características que iba a ofrecer:  Componentes Swing: comprenden todos los componentes utilizados para interfaces de usuario desde botones. y además añaden nuevos eventos que se pueden encontrar en el paquete javax.swing. como pueden ser los eventos o los gestores de diseño siguen siendo válidos para los componentes Swing. es decir. sería necesario un curso de la misma extensión de éste (o más) para tratar Swing de forma completa.0 Swing Interfaces de usuario en Java: componentes Swing / contenedores Introducción Muchos de los conceptos vistos en capítulos anteriores. de hecho se debe importar el paquete JAVA.12. barras de progreso.AWT para utilizarlos. diálogos y ventanas hasta cajas de texto. Y en el último de esta serie de capítulos trataremos otras características de Swing como son el nuevo gestor de diseño que incorpora (implementado por la clase BoxLayout) y la característica Pluggable Look & Feel. así por ejemplo. 150 .event. Swing es bastante extenso y aun utilizando tres capítulos no lo vamos a ver completamente. aspecto y comportamiento configurables.event. paneles con pestañas y listas. no todos ya que Swing ofrece un gran número de clases agrupadas en 15 paquetes distintos. los contenedores Swing. también dedicado por completo al extenso mundo de los componentes Swing) que se encuentra en el paquete javax. A lo largo de este capítulo describiremos algunos de los componentes Swing.awt. los gestores de diseño que utilizan los componentes Swing son los mismos que los vistos en los componentes AWT. pero Swing aporta un nuevo gestor de diseño (que veremos en el siguiente capítulo. En el segundo capítulo trataremos el otro gran grupo de componentes Swing. En este primer capítulo vamos a introducir una serie de conceptos de Swing y también se va a tratar un tipo de componentes Swing. barras de menú. Como hemos dicho vamos a tener tres capítulos dedicados a Swing. JFC y Swing JFC (Java Foundation Classes) es el nombre que recibe el conjunto de un gran número de características cuya función primordial es la de permitir la construcción de complejos interfaces de usuario.swing. los componentes Swin utilizan las clases de los eventos incluidas en el paquete java. Lo mismo sucede con los eventos.

1 del lenguaje (que coincidían con las versiones de la herramienta JDK). En muchos casos se utilizan indistintamente los términos JFC y Swing sin hacer ningún tipo de distinción. Los componentes AWT aparecieron en la versión 1. La denominación swing aparece también en los paquetes correspondientes.  API Java 2D: al igual que ocurría con la característica o funcionalidad anterior. Desde Sun nos recomiendan que utilicemos si es posible los componentes Swing en lugar de los componentes AWT. imágenes y texto. Permite a cualquier programa que utilice componentes Swing definir un tipo de apariencia y comportamiento (Look and Feel).1) denominada JFC 1. y como una extensión del JDK 1. Así por ejemplo una misma aplicación puede presentar una apariencia y comportamiento al estilo de Java o bien tipo Windows. Los componentes Swing se encuentran disponibles de dos formas distintas. El API de accesibilidad se encuentra formando parte de las características avanzadas del lenguaje Java y por lo tanto no lo vamos a tratar en este curso. como parte de la plataforma Java 2. en este caso. El API Java 2D se escapa también del alcance y pretensiones de este curso. aunque la plataforma Java 2 soporta perfectamente los componentes AWT como hemos podido comprobar en capítulos anteriores.2 o como en el JDK 1. Swing fue el nombre en clave que recibió el proyecto de Sun encargado de desarrollar los nuevos componentes para la construcción de interfaces de usuario.  Soporte para arrastrar y soltar (Drag and Drop): permite la posibilidad de arrastrar y soltar componentes entra aplicaciones Java y aplicaciones en otros lenguajes. y el porqué de la existencia de dos grupos distintos de componentes. En nuestro caso vamos a utilizar la versión que se encuentran integrada con la plataforma Java 2.0 y 1. tanto en la herramienta JDK 1. se trata de un conjunto de clases especializadas. soporte para una apariencia y comportamiento configurables. en el tratamiento de gráficos en dos dimensiones.1 (versión de Java 1. 151 .  API de accesibilidad: permite a tecnologías de rehabilitación tales como lectores de pantalla y displays Braille obtener información acerca del interfaz de usuario.0 del JDK y eran los únicos componentes que se encontraban disponibles para desarrollar interfaces de usuario. Soporte para Pluggable Look and Feel: es decir. 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 creación de interfaces de usuario.3. Los componentes AWT se utilizan principalmente en las versiones 1.1. En el apartado correspondiente volveremos a retomar esta característica y la trataremos en detalle.

los navegadores Web actuales. todavía no existen navegadores Web que soporten Swing. Es  Los componentes Swing no tienen porque ser rectangulares. 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. por ejemplo.  Posiblemente en un futuro se ampliarán los componentes Swing disponibles. Se debe señalar que. Los  posible situar bordes en torno a los componentes Swing.swing. Así por ejemplo. es decir. imágenes. algunas de estas ventajas de los componentes Swing frente a los AWT son las que se enumeran a continuación:  botones Swing pueden mostrar imágenes y/o texto.AWT y los Swing en el paquete javax. los botones pueden ser redondos. e incluso que ni siquiera debemos plantearnos que tipo de componentes debemos utilizar para construir interfaces de usuario. aspecto y comportamiento configurable La y seleccionable para los elementos del interfaz de usuario. Los componentes AWT se encuentran en el paquete JAVA. siempre elegiremos componentes Swing. Vamos a comentar brevemente algunas de las razones por las que nos puede interesar utilizar componentes Swing en lugar de componentes AWT.Los componentes Swing los podemos identificar porque los nombres de sus clases suelen ir precedidos por la letra J. selectores de color. ya que en la práctica no resulta tan sencillo. Incluso los componentes Swing más sencillos ofrecen una serie de posibilidades que los componentes AWT no recogen. Aunque se pueden generar parches que utilizaremos para que los navegadores soporten componentes Swing. barras de El herramientas. la clase Button del AWT.  tecnologías de rehabilitación pueden obtener información de los componentes Swing Las de forma sencilla. etc. tiene su clase correspondiente en Swing denominada JButton. La mayor diferencia entre los componentes AWT y los componentes Swing. Parece hasta ahora que con los componentes Swing todo son ventajas. Pero este razonamiento no es correcto. desgraciadamente. Por lo tanto si queremos construir applets que puedan ejecutarse en cualquier navegador deberemos construir su interfaz gráfica mediante componentes AWT. más claro todavía. 152 . sin embargo los componentes AWT tienen el aspecto y comportamiento de la plataforma nativa sobre la que se ejecutan. incluso en sus últimas versiones. elementos de menú. Swing nos permite especificar el aspecto y comportamiento (look and feel) del interfaz de usuario de nuestra aplicación (más adelante veremos como).  rico conjunto de componentes que ofrece Swing: botones con imágenes.  arquitectura Pluggable Look & Feel. es que los componentes Swing se encuentran implementados sin utilizar ningún tipo de código nativo. no implementan la máquina virtual correspondiente a la plataforma Java 2.

Cada jerarquía de contenedores posee un contenedor de alto nivel como raíz.setPreferredSize(new Dimension(200. 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. JDialog (diálogo) y JApplet.Contenedores de alto nivel Bajo esta nomenclatura se agrupan una serie de clases que se corresponden con componentes Swing que realizan la función de contenedores de otros componentes y que constituyen la raíz en la jerarquía de contenedores. Veamos un sencillo ejemplo que muestra un contenedor de alto nivel. pero fuera del panel de contenido.setBackground(Color. También dentro de los componentes Swing encontramos una cuarta clase denominada JWindow que representa una ventana sin controles ni título y que se encuentra siempre por encima de cualquier ventana. etiquetaAmarilla.  Para aparecer en pantalla todo componente de interfaz de usuario debe formar parte de una jerarquía de contenedores. La barra de menú se sitúa en el contenedor de alto nivel. A lo largo de este capítulo iremos viendo como en los componentes Swing encontramos equivalentes de los componentes AWT. //se crea la etiqueta JLabel etiquetaAmarilla = new JLabel(""). import javax.awt.  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.yellow). barraMenuCyan. 153 . 180)). barraMenuCyan.*. //se crea la barra de menú JMenuBar barraMenuCyan = new JMenuBar().*. Y el código fuente completo de este ejemplo es : import java.  Opcionalmente se puede añadir una barra de menú a un contenedor de alto nivel. En la Figura se puede ver el aspecto del contenedor.setOpaque(true). Los componentes de alto nivel se basan en los siguientes principios:  Swing ofrece tres clases que representan a componentes de alto nivel: JFrame (ventana).setBackground(Color.swing.setOpaque(true). es el equivalente a la clase Window de los componentes AWT. etiquetaAmarilla. etiquetaAmarilla. public class ContenedorAltoNivel { public static void main(String s[]) { //se crea la ventana raíz de la jerarquía de contenedores JFrame ventana = new JFrame("Contenedor de alto nivel").cyan).

Color y BorderLayout.AWT es necesario para utilizar las clases Dimension. Veamos un ejemplo más para ilustrar las jerarquías de contenedores. public class Ventana{ public static void main(String[] args) { //se establece el Look & Feel try { UIManager.setJMenuBar(barraMenuCyan).  panel (JPanel). } } A la vista de este código podemos realizar los siguientes comentarios. ventana. El paquete JAVA. ventana.swing.setDefaultCloseOperation(JFrame. y también como añadir una barra de menú.setPreferredSize(new Dimension(200. 154 .AWT y javax.*.add(etiquetaAmarilla.getContentPane(). Se han importado los paquetes JAVA.setLookAndFeel( UIManager.*. //se añaden la etiqueta y la barra de menú a la ventana ventana.awt. BorderLayout. ventana. 20)). Para ello nos debemos fijar en la Figura siguiente: En este ejemplo se han utilizado los siguientes componentes Swing:  Una ventana principal (JFrame).barraMenuCyan. El código fuente de este ejemplo es el que aparece en el Código fuente : import javax. en este mismo apartado veremos en más detalle el código que realiza estas funciones. import java. JLabel etiqueta = new JLabel(" Soy una etiqueta").setVisible(true). Un  botón (JButton).CENTER). JButton boton = new JButton("Soy un botón"). Un  Una etiqueta (JLabel).EXIT_ON_CLOSE).swing.getCrossPlatformLookAndFeelClassName()). } catch (Exception e) { } //se crean los componentes JFrame ventana = new JFrame("Ventana"). //se muestra la ventana ventana. También podemos comprobar la forma en la que se añade un componente a un panel de contenido de un contenedor de alto nivel como puede ser un objeto de la clase JFrame. Al panel se le ha añadido un borde mediante la clase BorderFactory para mostrar el agrupamiento de forma más clara entre el botón y la etiqueta. como se puede observar aunque utilicemos componentes Swing vamos a necesitar de los componentes AWT.pack().

que juegan un papel más visible e interactivo dentro del interfaz de usuario.setDefaultCloseOperation(JFrame. //se añade el panel al panel de contenido de la ventana ventana. igualmente podría tratarse de otro componente de alto nivel como podría ser un objeto de la clase JApplet o JDialog. Vamos a pasar a describir las funciones que presentan cada uno de los componentes Swing utilizados en el ejemplo. //se asigna un borde y un gestor de diseño al panel panel.add(panel.add(boton). son componentes que no contienen otros componentes Swing.setBorder(BorderFactory.createLineBorder(Color. A menudo los componentes atómicos tienen como función obtener información del usuario. En este ejemplo la ventana representada mediante la clase JFrame es el contenedor de alto nivel. su propósito es el de simplificar la situación del botón y la etiqueta. cómo asignar un aspecto y comportamiento (Look & Feel) determinado a nuestro interfaz de usuario.EXIT_ON_CLOSE). //se añaden al panel el botón y la etiqueta panel. para que sea visible se le ha asignado un borde de color rojo. 155 . ventana. BorderLayout. ventana. //se muestra la ventana ventana.getContentPane(). como ocurría con los anteriores. Y por último y el botón y la etiqueta se denominan componentes atómicos. panel. cajas de texto (JTextField) o tablas (JTable).CENTER).add(etiqueta). 1)). esto aparece en las primeras líneas del método main() y en este caso se establece el aspecto y comportamiento de Java. Swing ofrece un gran número de componentes atómicos (en este capítulo veremos algunos de ellos) como pueden ser listas desplegables (JComboBox). panel.red. } } Este código adelanta algo que veremos en detalle en el apartado correspondiente.5)).pack().JPanel panel = new JPanel(). sino que tienen entidad suficiente para presentar por sí mismos información al usuario.setLayout(new GridLayout(0. El panel es un contenedor intermedio. Otros contenedores intermedios pueden ser paneles con pestañas (JTabbedPane) y paneles de scroll (JScrollPane).setVisible(true).

Hasta ahora hemos visto un par de ejemplos que por un lado nos han mostrado como funcionan las jerarquías de contenedores. Por ejemplo un applet que muestra un diálogo tiene dos jerarquías de contenedores. como ya vimos en el tema dedicado a los componentes AWT. Para obtener una referencia al panel de contenido de un componente de alto nivel debemos utilizar el método getContentPane(). una aplicación Java con un interfaz de usuario basado en componentes Swing. sin embargo en la práctica las barras de menú se utilizan únicamente en ventanas y en raras ocasiones en applets. que devuelve un objeto de la clase Container. La excepción a esta regla son las barras de menú.BorderLayout. y por otro. en teoría. todos los componentes visibles de la ventana del interfaz de usuario. Por ejemplo. Una de las jerarquías de contenedores tiene a un objeto de la clase JFrame como raíz. nos ha mostrado como utilizar algunos de los componentes Swing. los componentes en la ventana del navegador tienen como raíz un objeto JApplet. Un applet basado en Swing tiene al menos una jerarquía de contenedores y su raíz es un objeto de la clase JApplet. aunque más adelante veremos algunos de ellos con más detalle.getContentPane(). directa o indirectamente. Para añadir una barra de menú a un contenedor de alto nivel.add(componente. como ya hemos dicho la raíz de esta jerarquía siempre es un contenedor de alto nivel. tiene al menos una jerarquía de contenedores con un objeto de la clase JFrame como raíz de la misma. una barra de menú.Como se puede observar incluso el programa Swing más sencillo presenta múltiples niveles en su jerarquía de contenedores. y cada una de las otras dos jerarquías un objeto de la clase JDialog como raíz. y por lo tanto tres contenedores de alto nivel. Como regla general.CENTER). y los que se encuentran en el diálogo tienen como raíz de su jerarquía de componentes un objeto de la clase JDialog. si una aplicación tiene una ventana principal y dos diálogos. la aplicación presentará tres jerarquías de contenedores. Una vez que tenemos una referencia al panel de contenido podremos añadir los componentes que consideremos necesarios utilizando una sentencia similar a la que muestra el siguiente fuente . 156 . ventana. como vimos en el primer ejemplo de este apartado. y normalmente el panel de contenido contiene. Para añadir un componente a un contenedor se utiliza una de las distintas formas del método add(). Cada contenedor de alto nivel indirectamente contiene un contenedor intermedio llamado panel de contenido (content pane). que se sitúan en un lugar especial fuera del panel de contenido. Todos los contenedores de alto nivel pueden tener. además. Por defecto el panel de contenido es un contenedor intermedio que hereda de la clase JComponent y que tiene como gestor de diseño un BorderLayout.

Este panel es útil para interceptar eventos o pintar sobre un área que ya contiene otros componentes. y también lo hacen a veces los applets. es decir. En la Figura 66 se puede ver un esquema que muestra la disposición de los distintos paneles que podemos encontrar en un componente de alto nivel. Veamos la utilización de la clase JFrame mediante un sencillo ejemplo. pasándole por parámetro el objeto JMenuBar correspondiente. Las aplicaciones Java que poseen interfaz de usuario al menos utilizan un JFrame. Esto puede ser útil para mostrar menús de aparición súbita (popup menus) por encima de otros componentes. y además permite ordenar los componentes que se vayan añadir con detenimiento (Z-order). poseen otro panel llamado panel raíz (root pane).setMenuBar(objMenuBar). permite organizar los componentes del interfaz de usuario en capas. El panel de cristal suele permanecer oculto y se encuentra por encima de todos los elementos del panel raíz. su función es la de gestionar el panel de contenido y la barra de menú junto con otros dos contenedores. A continuación vamos a comentar los distintos componentes de alto nivel: JFrame. Normalmente este panel intermedio no se suele utilizar. se utiliza sobre todo para interceptar eventos de entrada que suceden sobre el contenedor de alto nivel.crearemos un objeto de la clase JMenuBar. añadiremos los elementos de menú que se consideren necesarios y por último se lanzará sobre el contenedor de alto nivel el método setMenuBar(). ventana. Se trata de una ventana que contiene una etiqueta y que al pulsar el cierre de la misma se finalizará la ejecución de la aplicación. El panel de capas contiene directamente la barra de menú y el panel de contenido. 157 . 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. Todo contenedor de alto nivel además de poseer un panel de contenido. Estos dos contenedores son el panel de capas (layered pane) y el panel de cristal (glass pane). título y botones que permiten cerrar y maximizar la ventana.

100)).  HIDE_ON_CLOSE: es el valor por defecto. la ventana se oculta.setVisible(true). import java. En las siguientes líneas se crea y añade una etiqueta (JLabel) al panel de contenido de la ventana. sin la necesidad de utilizar ningún tratamiento de eventos. ventana. la ventana sigue visible. pero sigue existiendo. ventana. en este caso se implementa únicamente el método windowClosing() para finalizar la ejecución de la aplicación.swing. una alternativa al método pack() es le método setSize() en el que se puede especificar de forma explícita las dimensiones de la ventana.CENTER). El método pack() de un tamaño a la ventana de forma que todos sus contenidos tengan el tamaño especificado o superior. } } En la primera línea del método main() se utiliza el constructor de la clase JFrame que nos permite indicar el título de la ventana mediante una cadena que pasamos como parámetro.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System. 158 .setPreferredSize(new Dimension(175. ventana.*.add(etiqueta. Al método setVisible() se le pasa el parámetro true para que muestre la ventana en la pantalla. para cambiar este comportamiento se puede utilizar el método setDefaultCloseOperation() o bien implementar un tratamiento de eventos similar al del ejemplo. BorderLayout.pack().awt.getContentPane(). etiqueta. import javax.*. } }). public class Ventana{ public static void main(String s[]) { JFrame ventana = new JFrame("Ventana Sencilla"). Por último se lanzan los métodos pack() y setVisible() sobre la instancia de la clase JFrame. teniendo la posibilidad de mostrarse la ventana de nuevo si así lo indicamos en el programa. cuando se pulsa el botón de cierre la ventana se oculta. JLabel etiqueta = new JLabel("Soy una etiqueta"). Por defecto cuando se pulsa el botón de cierre de la ventana.exit(0). otra versión de este constructor es sin argumentos. El parámetro que se le pasa al método setDefaultCloseOperation() debe ser una de las siguientes constantes:  DO_NOTHING_ON_CLOSE: en este caso cuando se pulsa el botón de cierre de la ventana no ocurre nada. A continuación se añade un oyente para los eventos de la ventana.event. ventana.awt.*.import java.

BorderLayout.  ProgressMonitor: muestra un diálogo que muestra el progreso de una operación.   Container getContentPane(): devuelve el panel de contenido de la ventana.awt. JOptionPane Vamos a pasar ahora a comentar el segundo tipo de contenedores de alto nivel. Ya hemos visto las posibles acciones. como pueden ser setSize().SOUTH). setTitle().setLayout(new BorderLayout()).CENTER).  void setMenuBar(MenuBar): asigna a la ventana una barra de menú determinada representada por un objeto de la clase MenuBar.  void setGlassPane(Component): permite asignar un panel de cristal a la ventana.add(componente.add(otroComponente. que son un tipo de ventana más limitada que las ventanas representadas por la clase JFrame. que va a ser el que contenga todos los componentes visibles del interfaz de usuario de la ventana. Hay varias clases que ofrecen diálogos:  JOptionPane: permiten crear sencillos diálogos estándar.  JDialog: permite crear directamente diálogos completamente personalizados. los diálogos.Frame y por lo tanto hereda todos los métodos de la clase Frame. pack(). ventana. Como se indica al principio de esta sección nosotros únicamente nos vamos a encargar de las clase JOptionPane y JDialog. 159 .  Component getGlassPane(): devuelve el panel de cristal de la ventana. DISPOSE_ON_CLOSE: al cerrar la ventana se elimina de la memoria y de la pantalla. en el Código se puede observar como se crea un objeto JPanel que luego va a ser el panel de contenido de una ventana. panelContenido. Además de los métodos de la clase java. setVisible() y getTitle(). la clase JFrame tiene los siguientes métodos propios:  void setDefaultCloseOperation(int): asigna el tipo de operación que va a realizar la ventana cuando el usuario pulse el botón de cierre de la ventana. es decir. JPanel panelContenido=new JPanel().  getDefaultCloseOperation(): devuelve la operación de cierre por defecto asignada a la int ventana.  void setContentPane(Container): asigna a la ventana un panel de contenido. este método se suele utilizar para una vez que tenemos una referencia al panel de contenido de la ventana añadir componentes a la misma. Este método permite utilizar nuestro propio panel de contenido.  JFileChooser: ofrece un diálogo estándar para la selección de un fichero.awt. La clase JFrame hereda de la clase java.  JColorChooser: ofrece un diálogo estándar para la selección de colores. panelContenido. en este caso se liberan todos los recursos que estaban siendo utilizados por la ventana.setContentPane(panelContenido). JDialog.Frame. panelContenido.  JMenuBar getMenuBar(): devuelve la barra de menú que tiene asignada la ventana. lo normal es utilizar un objeto de la clase JPanel para crear el panel de contenido.BorderLayout.

cuando se maximiza la ventana los diálogos vuelven a aparecer. no utilizar ningún tipo de icono. o utilizar uno de los cuatro iconos estándar que ofrece la clase JOptionPane. puede ser una ventana. suele ser 160 . La clase JDialog hereda de a clase AWT java. ya que la clase JOptionPane es un contenedor que puede crear de forma automática una instancia de la clase JDialog y añadirse al panel de contenido de ese diálogo. indicar los iconos. advertencia y error. sino que se lanzaran de forma directa sobre la clase. especificar el título y texto del diálogo y los textos de los botones que aparecen. JOptionPane ofrece soporte para mostrar diálogos estándar.awt. podemos utilizar iconos personalizados. cuando un diálogo modal se encuentra visible se bloquea la entrada del usuario en todas las demás ventanas del programa. un componente dentro de una ventana o nulo y el segundo el mensaje que se muestra. A continuación vamos a comentar las distintas características de la clase JOptionPane. añade a la clase Dialog un panel raíz (root pane) y soporte para la operación por defecto de cierre. el icono y el título que muestra el diálogo. Este método se encuentra sobrecargado y ofrece tres versiones distintas:  void showMessageDialog(Component componentePadre. para crear un diálogo no modal se debe hacer uso de la clase JDialog que permitirá indicar si el diálogo que se crea va a ser modal o no. El primero de estos métodos es el método showMessageDialog(). Cuando se utiliza la clase JOptionPane en realidad también estamos haciendo uso de la clase JDialog de forma indirecta. estos iconos son los de pregunta. El aspecto de estos iconos variará según el Look & Feel (aspecto y comportamiento) que se aplique. Para mostrar diálogos modales sencillos se utilizará directamente uno de los métodos showXXXDialog() de la clase JOptionPane.Dialog. Object mensaje): el primer parámetro indica el componente padre del que depende. si la ventana se destruye también lo harán sus diálogos asociados. Este conjunto de métodos son métodos estáticos y por lo tanto se lanzarán sobre una instancia concreta de la clase. Mediante la clase JOptionPane podemos crear distintos tipos de diálogos. Los diálogos que ofrece la clase JOptionPane son modales. Veamos a continuación los principales métodos showXXXDialog() de la clase JOptionPane.Todo diálogo es dependiente de una ventana determinada. En cuanto a los iconos que se muestran en el diálogo facilitado por JOptionPane. que muestra un diálogo modal con un botón de aceptar. Si lo que necesitamos es controlar el comportamiento del diálogo cuando se cierre o si el diálogo no debe ser modal entonces instanciaremos un objeto de la clase JOptionPane y lo añadiremos a un objeto de la clase JDialog. Como se puede observar son las mismas características que ofrecía la clase JFrame sobre la clase Frame del AWT. Se puede especificar el mensaje. información. Los diálogos pueden ser modales. Cuando la ventana se transforma en icono sus diálogos desaparecen de la pantalla.

161 .JOptionPane. WARNING_MESSAGE (icono de advertencia) y QUESTION_MESSAGE (icono de pregunta). String título. Object mensaje. Este último parámetro determinará el icono que se va a mostrar en el diálogo. la instancia de la clase JFrame. JOptionPane. obtendremos el resultado que aparece en la Figura.showMessageDialog(ventana. a nuestra clase Ventana de la sección anterior. Icon icono): en esta última versión del método podemos indicar un icono personalizado para que se muestre en el diálogo. llamada ventana va a ser el componente padre de los diálogos. JOptionPane. y el tipo de mensaje a mostrar. String título. Podemos añadir las distintas sentencias de creación de diálogos que vamos a ver ahora al ejemplo de la sección anterior en la que tratábamos la clase JFrame. INFORMATION_MESSAGE (icono de información).una cadena de texto que podemos dividir en varias líneas utilizando el carácter de nueva línea (\n).WARNING_MESSAGE)."Esto es un mensaje". ImageIcon icono = new ImageIcon("icono. ERROR_MESSAGE (icono de error).showMessageDialog(ventana. En este caso se muestra el título y tipo de mensaje por defecto. En este otro caso indicamos un icono personalizado para que se muestre en el diálogo."Título"."Esto es un mensaje". Object mensaje."Esto es un mensaje"). para ello se utilizan una serie de constantes definidas en la clase JOptionPane. Otras variaciones sobre la sentencia anterior se muestran a continuación con sus correspondientes resultados. Veamos algunos ejemplos con el método showMessageDialog(). JOptionPane.  void showMessageDialog(Component componentePadre.icono). "Título".showMessageDialog(ventana. int tipoMensaje. estas constantes son: PLAIN_MESSAGE (sin icono). Si añadimos la línea que muestra el Código. que será una cadena. int tipoMensaje): en este caso se especifica también el título.  void showMessageDialog(Component componentePadre. JOptionPane.gif").INFORMATION_MESSAGE.

int tipoMensaje): en esta versión del método podemos indicar además el tipo de mensaje que se muestra en el diálogo. que pasamos a comentar a continuación. String título.  showConfirmDialog(Component componentePadre. YES_NO_OPTION. el diálogo se cerrará.showMessageDialog(ventana. es decir.  showConfirmDialog(Component componentePadre. Este método.  showConfirmDialog(Component componentePadre. Object mensaje. Object mensaje. Icon icono): en esta última versión especificamos un icono personalizado que se va a mostrar en el diálogo de conformación correspondiente. y además con el título por defecto.  showConfirmDialog(Component componentePadre."Esto es un mensaje".JOptionPane.ERROR_MESSAGE). al igual que el anterior. para que el usuario seleccione entre los botones correspondientes. String título. se encuentra sobrecargado y por lo tanto ofrece tres versiones distintas. Object mensaje): este método int muestra un diálogo modal con los botones. si. JOptionPane. este entero va a recoger la selección que ha realizado el usuario. String título. int int tipoOpción. YES_NO_CANCEL_OPTION y OK_CANCEL_OPTION. El entero que devuelve este método se corresponde con uno de los valores de las siguientes constantes de la clase JOptionPane: 162 . de la misma forma que lo hacíamos en el método showMessageDialog(). En este caso este método muestra un diálogo de confirmación. Object mensaje. int int tipoOpción): en este caso podemos especificar el título del diálogo de confirmación y el tipo de opciones que se van a mostrar. que representan las opciones disponibles. Otro método de la clase JOptionPane que muestra un diálogo es el método showConfirmaDialog(). indicará el botón que ha sido pulsado."Título". Como se puede comprobar en todos los ejemplos. Como se puede observar en todas las versiones del método showConfirmDialog() se devuelve un entero (int). para esto último utilizaremos las siguientes constantes ofrecidas por la clase JOptionPane: DEFAULT_OPTION. si pulsamos el botón etiquetado como OK. no y cancelar. int tipoMensaje. int int tipoOpción.

JOptionPane. El siguiente método que vamos a tratar de la clase JOptionPane va a ser el método showOptionDialog(). JOptionPane. NO_OPTION. Icon icono.showConfirmDialog(ventana. La sintaxis de este método es la siguiente: int showOptionDialog(Component componentePadre."¿Desea formatear el disco?"). JOptionPane.String título. Este método es mucho más potente que los vistos anteriormente. Al igual que ocurría con el método anterior. con este método podremos indicar los botones que queremos aparezcan en el diálogo."¿Desea formatear el disco?". ya que permite una mayor personalización del diálogo. OK_OPTION y CLOSED_OPTION (el usuario cierra el diálogo sin pulsar ninguna de las opciones disponibles).showConfirmDialog(ventana. int tipoOpción. El método showOptionDialog() se diferencia del método showConfirmDialog() principalmente en que permite especificar las etiquetas de los botones que aparecen y además permite especificar la opción seleccionada por defecto. Los tres primeros parámetros son los mismos que los vistos en el método showMessageDialog() y además tienen el mismo significado.WARNING_MESSAGE).YES_NO_OPTION. mensaje y títulos especificados para que el usuario seleccione entre las distintas opciones que se le ofrecen. Object[] opciones. Object mensaje.YES_OPTION."Confirme operación". vamos a ver ejemplos de utilización del método showConfirmDialog. CANCEL_ OPTION. La función de este método es la de mostrar un diálogo modal con los botones.int tipoMensaje. 163 . JOptionPane. iconos. En este caso se muestra el diálogo de confirmación por defecto. Object valorInicial) Vamos a comentar los distintos parámetros de este nuevo método.

icono. uno de aceptar y otro de cancelar. como ocurría con los métodos anteriores. va a recoger la selección que ha realizado el usuario. Object[] opciones={"Vale".showOptionDialog(ventana. Ahora se va a mostrar ejemplos de uso del método showOptionDialog(). es decir. El último método que vamos a ver de la clase JOptionPane es el método showInputDialog(). Estos ejemplos son únicamente unas cuentas sentencias que podemos incluir. y ofrece las siguientes versiones:  Object showInputDialog(Component componentePadre. si el usuario pulsa el botón de aceptar indicará que ha introducido una información que podemos recuperar. El diálogo que se muestra va a tener dos botones.gif"). Object mensaje): muestra un diálogo con el mensaje correspondiente que requiere una entrada del usuario a través de una caja de texto. El siguiente parámetro 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 diálogo. se siguen devolviendo los mismos valores de las constantes. es decir.YES_NO_OPTION. este método va a permitir obtener información de entrada del usuario a través del diálogo."Confirme operación". y por lo tanto se corresponderán con las constantes vistas en el método showConfirmDialog(). este entero tiene la misma función que el que devolvía el método showConfirDialog(). "Ni hablar"}. El último parámetro indica cual es la opción que se encuentra seleccionada por defecto.WARNING_MESSAGE. JOptionPane. así como con un icono personalizado.JOptionPane. en nuestra ya conocida clase Ventana. es decir. ya sea a través de una caja de texto o a través de un una lista de opciones. Este método se encuentra sobrecargado. podemos utilizar nuestras propias etiquetas pera mostrar en los botones. Los dos siguientes parámetros.En el tipo de opción se especifica el conjunto de opciones que se van a presentar al usuario. Como se puede observar en la sintaxis del método showOptionDialog() se devuelve un entero (int). además es el segundo botón el que se encuentra seleccionado por defecto. opciones[1]). ImageIcon icono = new ImageIcon("icono. JOptionPane. Se debe indicar que aunque utilicemos etiquetas personalizadas para nuestros botones."¿Desea formatear el disco?". opciones. tiene el mismo cometido que el método showMessageDialog(). tipo de mensaje y el icono personalizado. indicará el botón que ha sido pulsado. Se muestra un diálogo con las opciones si/no pero con unas etiquetas de botones personalizadas. así por ejemplo un diálogo del tipo YES_NO_OPTION siempre devolverá los valores: YES_OPTION. NO_OPTION o CLOSED_OPTION. 164 .

En este caso se muestra una lista con las opciones disponibles.  Object showInputDialog(Component componentePadre. Icon icono.out. En los siguientes ejemplos se muestra la utilización del método showInputDialog().QUESTION_MESSAGE. Como se puede comprobar siempre se devuelve un objeto de la clase String. ya sea a través de una caja de texto una de una lista de opciones disponibles. String título.JOptionPane."Negro"."Azul". para que el usuario seleccione la que desee.gif"). int tipoMensaje. por lo que el diálogo se situará en el centro de la pantalla. 165 ."¿Cuál es tu nombre?"). System."Selección de color". Object opciónSeleccionada): en este método se permite indicar un icono personalizado y un conjunto de opciones en forma de lista desplegable para que el usuario seleccione una de ellas además se permite seleccionar una opción por defecto. Object respuesta=JOptionPane. icono. En este caso se va a recoger el dato facilitado por el usuario y mostrarlo en la salida estándar de la aplicación.println("El color favorito del usuario es el: "+respuesta). Object showInputDialog(Component componentePadre.valores.println("El nombre del usuario es: "+respuesta). ya que en los otros casos se centra siempre con respecto al componente padre.valores[2]). que se va a corresponder con una cadena que va a representar la información indicada por el usuario.out."Amarillo"}. aunque de todas forma sigue bloqueando a la ventana que lo ha generado.  String showInputDialog(Object mensaje): en este caso no se indica nada más que el mensaje que se va a mostrar al usuario. También se ha utilizado un icono personalizado.showInputDialog(ventana. Object mensaje. Object[] valores={"Rojo". System. ImageIcon icono = new ImageIcon("icono."Verde". Object opciones. sin utilizar ningún componente padre. String título."¿Cuál es tu color favorito?". Object mensaje. String respuesta=JOptionPane.showInputDialog(ventana. int tipoMensaje): en esta caso nos permite especificar un título y un tipo de mensaje (ya conocemos las constantes correspondientes).

ventana. import java.*.true). a todos los contenedores de alto nivel. En estos casos en los que deseamos personalizar al máximo los diálogos utilizaremos la clase JDialog conjuntamente con la clase JOptionPane. también se cierran cuando el usuario pulsa el cierre del diálogo y por último son todos modales. Estos diálogos tienen en común una serie de características: al pulsar alguno de los botones que contienen se cierran. Para ello necesitamos crear una instancia de la clase JOptionPane para añadirla al objeto JDialog correspondiente. JOptionPane. como ya hemos comentado. JDialog dialogo=new JDialog(ventana.JOptionPane. import java.awt. ya que su comportamiento se puede adecuar a nuestras necesidades.event. pero en otros casos esto puede no ser así.setLocationRelativeTo(ventana).swing.*. Una vez creado el objeto JOptionPane. las opciones disponibles y la opción seleccionada por defecto. boolean modal. } } 166 . JOptionPane contenido=new JOptionPane("Esto es un mensaje".Hasta ahora hemos visto los métodos que nos ofrece la clase JOptionPane para mostrar distintos tipos de diálogo.pack().  JDialog(Frame ventanaPropietaria. únicamente nos queda asignar al diálogo (JDialog) su contenido (JOptionPane).setSize(175.  JDialog(Frame ventanaPropietaria): se indica la ventana a la que se encuentra asociado el diálogo. La clase JOptionPane presenta múltiples constructores que permiten especificar el mensaje que se va a mostrar. ventana."Esto es un diálogo". o también nos puede interesar utilizar un diálogo que no sea modal.YES_NO_CANCEL_OPTION).setContentPane(contenido). el tipo de opciones.  JDialog(Frame ventanaPropietaria. deberemos crear el objeto JDialog que lo va a contener. Si ya tenemos instanciados el objeto JOptionPane y el objeto JDialog. import javax. sin título definido y sin ninguna ventana propietaria.awt. String título): se especifica además el título que va a tener el diálogo. dialogo. y que es común.INFORMATION_MESSAGE. En algunos casos estos diálogos nos servirán. para ello utilizamos el método setContentPane() de la clase JDialog.*. dialogo.setVisible(true). public class Dialogo{ public static void main(String s[]) { JFrame ventana = new JFrame("Ventana Sencilla"). el tipo de mensaje. boolean modal): se indica si el diálogo va a ser modal o no. A continuación se muestra este proceso con un ejemplo que consiste en una sencilla aplicación Java que muestra una ventana y un diálogo asociada a la misma. En algunos casos nos puede interesar validar la información ofrecida por el usuario en un diálogo. el icono personalizado. dialogo.setVisible(true). algunos de los constructores ofrecidos por la clase JDialog son:  JDialog(): crea un diálogo no modal. dialogo.100).

etc. JApplet Este es el tercero de los contenedores de alto nivel. suele contener componentes grandes o que pueden crecer. Contenedores intermedios Los contenedores intermedios son contenedores Swing. si queremos que realicen alguna operación cuando se pulsen. Los paneles de contenido de los contenedores de alto nivel suelen ser de la clase JPanel. Se utilizan debido a las limitaciones del tamaño de la pantalla. es decir. añadir componentes al panel de contenido. posibilidad de tener una barra de menú.applet.  void setDefaultCloseOperation(int operacion): asigna una operación de cierre por defecto al diálogo. En el código se puede observar que se utiliza el método setLocationRealtiveTo(). este método centra el diálogo de forma relativa al componente que le pasamos por parámetro.El resultado es : Como se puede comprobar la pulsación de los botones del diálogo no tiene el efecto que tenían anteriormente.  getDefaultCloseOperation(): devuelve la operación de cierre por defecto que tiene int asignada el diálogo. que aunque no son contenedores de alto nivel. es decir. Otros métodos de la clase JDialog son los siguientes:  Container getContentPane(): devuelve el panel de contenido del diálogo. como ocurría con la clase JFrame. ocultarse en el cierre. ya que la operación tiene asignada por defecto para el cierre es HIDE_ON_CLOSE.Applet. su labor principal es la de contener otros componentes. es por lo tanto la versión Swing de la clase Applet.  JScrollPane: ofrece una vista de scroll de un componente. Esta clase hereda de la clase java. deberemos hacerlo a través de una gestión de eventos a través del código de nuestra aplicación. La clase JApplet aporta dos características esenciales a los applets. 167 . se le puede asignar gestores de diseño y bordes. Swing ofrece una serie de contenedores de propósito general:  JPanel: es le más flexible y utilizado de todos ellos. con todo lo que ello supone. Se utiliza normalmente para agrupar componentes. Aunque si pulsamos el cierre del diálogo éste se sigue cerrando. ofrece soporte para tecnologías de rehabilitación y ofrece un panel raíz. Estos contenedores se siguen basando en la jerarquía de contenedores de Swing que ya hemos visto anteriormente.

La clase JPanel permite asignar un gestor de diseño al panel para indicar la forma en la que se van añadiendo los distintos componentes al mismo. La clase JPanel es la versión Swing de la clase Panel de AWT. es decir. el explorador de Windows. de hecho se debe importar el paquete JAVA. no vamos a tratar todos ya nos excederíamos en la extensión del presente curso.awt. JPanel Esta clase permite construir paneles de propósito general para contener componentes Swing. Parece una ventana y ofrece toda su funcionalidad pero debe parecer siempre dentro de otra ventana (contenedor de alto nivel) que la contiene. El aspecto es similar al que ofrece. también por defecto los paneles son opacos. que veremos en el apartado correspondiente.  JTabbedPane: contiene muchos componentes pero sólo puede mostrar un conjunto de ellos a la vez. pero se añade un nuevo gestor mediante la clase BoxLayout. como ya vimos en el apartado anterior el panel raíz esta formado por las siguientes partes: panel de capas. la vista del árbol de directorios se encuentra separada de la vista de contenidos de un directorio. Un ejemplo podrían ser las distintas pestañas de una hoja de propiedades. por defecto el gestor de diseño de un objeto JPanel es un FlowLayout. En el Código siguiente se muestra la forma de utilizar esta clase. aunque se pueden hacer transparentes mediante el método setOpaque() pasándole el valor false por parámetro.AWT para poder utilizarlos. Para añadir componentes lo haremos de la misma forma que veíamos en los paneles AWT. panel de contenido. Al añadir un componente a un panel de este tipo se especifica su profundidad mediante un entero. Un ejemplo pueden ser las barras de herramientas de un procesador de textos como MS Word.  JRootPane: esta clase representa el panel raíz de un contenedor de alto nivel. no se debe olvidar que Swing tienen un gran número de componentes. Además también existen los siguientes contenedores intermedios que se encuentran más especializados:  JInternalFrame: permite mostrar una ventana dentro de otra. la profundidad.  JLayeredPane: este panel ofrece una tercera dimensión. cuanto mayor sea el entero especificado mayor será la profundidad en la que se sitúa el componente. Por defecto un panel no muestra nada en pantalla a excepción de su fondo. 168 . Esta tercera dimensión se denomina también Zorder. JSplitPane: este panel agrupa dos componentes. el usuario puede ir cambiando entre los distintos conjuntos de manera sencilla. por ejemplo. con las distintas versiones de los métodos add().*. El usuario puede ajustar la línea divisora que separa ambos componentes arrastrándola. A continuación vamos a comentar algunos de estos paneles intermedios. para poder posicionar componentes de esta forma. import java. uno al lado del otro.  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. Swing utiliza los mismos gestores de diseño que vimos para los componentes AWT. panel de cristal y barra de menú.

public class Panel{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana Sencilla").*. import import import public public java. el resultado se puede apreciar en la Figura. los bordes. con títulos. class Panel{ static void main(String args[]) { 169 .setPreferredSize(new Dimension(175.setVisible(true). elevados. import javax. sino que se utilizan para delimitar visualmente una serie de componentes de otros. La clase JPanel hereda de la clase JComponent. 100)). panel.*.JLabel.awt. etc.awt.*.getContentPane().awt.*.*.pack().JLabel. java. ventana. y debido a ello permite utilizar una nueva funcionalidad ofrecida por Swing. ventana.event.CENTER). Veamos un ejemplo que asigna distintos bordes a varios paneles dentro de una ventana. BorderLayout.CENTER). ventana. Esta clase ofrece un gran número de métodos que permiten crear distintos tipos de bordes: de líneas.NORTH). una combinación de dos bordes.setLayout(new BorderLayout()). Para asignar un borde a un componente.swing. Cada objeto de la clase JComponent puede tener uno o más bordes.swing. se utiliza el método setBorder().setPreferredSize(new Dimension(175. .event. BorderLayout. JLabel etiqueta1 = new JLabel("Soy una etiqueta".add(etiqueta2. JLabel etiqueta2 = new JLabel("Soy otra etiqueta". etiqueta2.SOUTH).add(panel. 100)).CENTER). hundidos. panel. Los bordes no son realmente componentes. } } Se trata simplemente de añadir dos etiquetas a un panel (JPanel) y añadir este panel al panel de contenido de una ventana (JFrame). etiqueta1. BorderLayout.import java.add(etiqueta1. marcados. Para crear los distintos bordes que ofrece Swing se suele utilizar la clase BorderFactory. javax. en este caso un objeto de la clase JPanel. JPanel panel=new JPanel(). panel.

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

como se puede observar al recuperar el panel de contenido para asignarle un borde vacío se ha tenido que hacer un "cast" con la clase JPanel. El usuario puede visualizar los componentes que desea ver seleccionando la pestaña correspondiente. normalmente otros paneles. en este caso una instancia de la clase JFrame. Algunos de los métodos de la clase BorderFactory se encuentran sobrecargados. para permitir una mayor personalización de los distintos tipos de bordes que queremos asignar. 171 . este borde vacío se ha utilizado para crear un margen entre el panel de contenido y el contenedor. luego se van creando los distintos componentes y se van añadiendo al contenedor mediante el método addTab(). A cada panel se le ha asignado un borde distinto y se le ha añadido una etiqueta (JLabel) con la descripción del tipo de borde correspondiente.Comentarios del código: El primero de ellos es referente al tipo de borde utilizado en el panel de contenido. Al panel de contenido de la ventana se le ha asignado un gestor de diseño GridLayout con siete filas y una única columna. compartiendo un mismo espacio. JTabbedPane Gracias a este contenedor intermedio podemos tener distintos componentes. Para crear un contenedor de este tipo primero debemos instanciar el objeto correspondiente de la clase JTabbedPane.

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

Component componente): el primer argumento indica el texto que va a parecer en la pestaña y el componente que va a contener. LEFT y RIGHT. Estas constantes se encuentran definidas en la clase JTabbedPane y son las siguientes TOP. Icon icono. este método presenta las siguientes versiones:  addTab(String texto. especificar la localización de las pestañas. 173 . Así si modificamos el ejemplo anterior cambiando el constructor utilizado para el panel JTabbedPane mediante la siguiente línea de código:e JTabbedPane panelTab = new JTabbedPane(JTabbedPane. Component componente.BOTTOM). Component componente): en este caso además se indica el icono que se va a mostrar en la pestaña. pero existe otro constructor que permite. En el ejemplo se ha utilizado un constructor de la clase JTabbedPane que no utiliza ningún parámetro.Si se prueba el ejemplo anterior se puede comprobar que no se ha escrito ningún código para realizar el tratamiento de eventos. la clase JTabbedPane realiza este tratamiento (mostrar los componentes de la pestaña seleccionada) de forma automática.  addTab(String texto.  addTab(String texto. mediante una constante que recibe como parámetro. Icon icono. Obtenemos el resultado siguiente: Para añadir una nueva pestaña ya sabemos que debemos utilizar el método addTab(). BOTTOM. String ayuda): en la última versión del método addTab() se permite especificar el texto que aparecerá a modo de ayuda (tooltip) cuando situemos el puntero del ratón sobre la pestaña.

Así si una vez creado el panel de nuestro ejemplo añadimos las líneas de código que se muestran en el Código fuente siguientey obtenemos el resultado que aparece en la Figura.Color.panelNuevo). Para manipular las pestañas la clase JTabbedPane ofrece los siguientes métodos:  insertTab(String texto. //icono que se muestra cuando la pestaña está desactivada panelTab.add(new JTextArea(5.red). //color del texto panelTab.setComponentAt(3.  remove(Component componente): elimina la pestaña que contenga el componente especificado como argumento.otroIcono).  indexOfTab(String texto): devuelve el índice de la pestaña que posea el texto indicado int por parámetro.  removeTabAt(int índice): elimina la pestaña cuya posición coincida con la indicada. int También es posible modificar la apariencia de las pestañas del panel JTabbedPane.  getSelectedIndex(): devuelve el índice de la pestaña seleccionada actualmente. int índice): inserta una nueva pestaña en la posición indicada por el parámetro índice. Las pestañas comienzan a numerarse en cero. String ayuda.setDisabledIconAt(0. Icon icono.green).setBackgroundAt(0.setForegroundAt(0.En el ejemplo se ha utilizado el método setSelectedIndex() para indicar la pestaña que por defecto se encuentra seleccionada. //color de fondo cuando no está seleccionada la pestaña panelTab."Otro texto"). //se modifica el título panelTab.que tiene como parámetro el índice de la pestaña. Si utilizamos cualquiera de los métodos anteriores .Color. 174 .gif"). ImageIcon otroIcono = new ImageIcon("icono2. que será la que mostrará sus componentes. Podemos indicar el icono que va a mostrar la pestaña según se encuentre habilitada o deshabilitada.30)). también el color de fondo y el del texto de la pestaña.setTitleAt(2. panelNuevo. Component componente. Curioso resultado ya que no se muestra el icono que se utiliza para indicar que la pestaña está deshabilitada. y la pestaña no existe se producirá una excepción.  removeAll(): elimina todas las pestañas. //se cambia el componente que posee la pestaña panelTab. JPanel panelNuevo=new JPanel().

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

ventana. normalmente no se suele utilizar el JLayeredPane del JRootPane. cuanto mayor sea este entero mayor será la profundidad del componente correspondiente.awt.*. 100)).getContentPane(). que se sitúa un objeto de la clase JLayeredPane dentro del panel de contenido de una ventana (JFrame).setVisible(true).pack(). //se añade el panel de scroll al panel de contenido ventana. Vimos que los contenedores de alto nivel de Swing contienen un panel de raíz que a su vez contiene un panel de capas (layered pane). Si queremos que la barra de herramientas permanezca fija se puede lanzar sobre el objeto de la clase JToolBar el método setFloatable() pasando como argumento el valor false. 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. //se asigna un tamaño preferido a la ventana ((JPanel)ventana. para añadir un separador a la barra de herramientas se utiliza el método addSeparator(). ventana. Para añadir un componente a un JLayeredPane se utiliza el método add(). Veamos el Código:. JLayeredPane Este contenedor ofrece una tercera dimensión que permite posicionar los componentes que contiene especificando una profundidad.*. En el siguiente ejemplo se van añadiendo a una instancia de la clase JLayeredPane etiquetas con color de fondo a distintas profundidades. La profundidad de un determinado componente se especifica mediante un entero. Para posicionar los distintos elementos que contiene.NORTH).getContentPane().add(barra. //se crea un área de texto JTextArea areaTexto = new JTextArea(5. import import import public java.BorderLayout.BorderLayout. es decir. la clase JToolBar utiliza el gestor de diseño BoxLayout. javax.event.CENTER).getContentPane()).awt. la capa en la que se encuentra.swing.*.30). class PanelCapas{ 176 . java. que veremos más adelante en el apartado dedicado al mismo. //se añade a un panel de scroll JScrollPane scrollPane = new JScrollPane(areaTexto). } } Como se puede comprobar en el código anterior.setPreferredSize(new Dimension(400. Esto mismo ocurre en nuestro ejemplo. en este método se debe indicar la profundidad del componente. sino que se crea un objeto JLayeredPane distinto para utilizarlo dentro de otro panel.ventana.add(scrollPane.

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

dónde n es el número de componentes dentro de la capa.getIconWidth(). cuanto menor es el número mayor es la profundidad del componente dentro de la capa. a una profundidad determinada. ImageIcon icono = new ImageIcon("imagen. nuevaEtiqueta.0). Lo que se hace es añadir una nueva etiqueta con un icono en la capa 2 de modo que quede por encima de la etiqueta de color que ya existía en esa misma capa.add(nuevaEtiqueta. El valor de esta posición va desde -1 hasta n-1. Se ha utilizado un bucle for para ir recorriendo estos arrays y crear las etiquetas mediante el método crearEtiqueta(). Es posible por lo tanto definir la posición de un componente respecto al resto de componentes dentro de la misma capa. El nuevo aspecto del panel de capas es el que aparece en la Figura. 178 .gif"). indica la posición más en el fondo. se puede especificar la posición de un componente. es decir. para ello existe una versión del método add() que posee un tercer parámetro para indicar esta posición dentro de la capa.Como se puede comprobar a la vista del código se han creado dos arrays que contienen por un lado los textos de las etiquetas y por otro los colores de las etiquetas. Como se puede ver el método y los atributos utilizado son estáticos.setBounds(180. Dentro de una capa.new Integer(2). Si utilizamos 0 el componente se encontrará por encima del resto. una vez creadas las etiquetas en las distintas capas. Utilizar -1 es equivalente a utilizar n-1.75. JLabel nuevaEtiqueta=new JLabel(icono). panelCapas. Al contrario que las capas.getIconHeight()). ya que los utilizamos directamente en el método main() si lanzarlos sobre una instancia de una clase. Si al ejemplo anterior le añadimos las líneas que muestra el Código siguiente.icono.icono.

panelCapas.4. Para mover un componente dentro de una capa a la primera posición se utiliza el método moveToFront(). Si utilizamos el método moveToBack() en nuestro código tendrá un nuevo aspecto del ejemplo :. y para enviarlo al fondo el método moveToBack(). 179 .moveToBack(nuevaEtiqueta). para ello se utiliza el método setLayer().También es posible mover un componente de una capa a otra. ambos métodos tiene como parámetro el componente al que se quiere cambiar de posición dentro de una capa.0).setLayer(nuevaEtiqueta. Así si queremos mover la etiqueta con el icono a la capa 4 escribiremos lo que indica la línea de codigo siguiente: panelCapas. El último argumento del método setLayer() es la posición del componente dentro de la nueva capa.

 JRadioButton: un botón de opción que suelen utilizarse en grupos.  JToggleButton: representa un botón con dos estados (pulsado/no pulsado). aunque veremos en los distintos ejemplos que nunca vamos a utilizar directamente esta clase. una caja de texto. Componentes que muestran una información estructurada o que permiten editarla:  JColorChooser: permite realizar la selección de un color determinado.  JRadioButtonMenuItem: un elemento de menú que contiene un botón de opción. una casilla de verificación. Los componentes atómicos se subclasifican atendiendo a la labor que realizan. Todos los componentes atómicos heredan de la clase JComponent.  JComboBox: una lista desplegable.  JFileChooser: permite seleccionar ficheros y directorios.Interfaces de usuario en Java: componentes atómicos de Swing Componentes atómicos Este grupo de componentes se corresponde con aquellos componentes cuya función es presentar o recibir información. etc. Un ejemplo de componente atómico podría ser un botón.  JMenuItem: un elemento de un menú. Componentes que existen únicamente para mostrar información:  JLabel: etiqueta que puede mostrar texto.  JCheckBoxMenuItem: un elemento de menú que contiene una casilla de verificación.  JSlider: permite seleccionar al usuario un valor dentro de un rango determinado. debido a esto todos ellos soportan características estándar de los componentes Swing.  JMenuBar: una barra de menú.  JTextComponent: de esta clase heredan distintas clases especializadas en el tratamiento de textos.  JCheckBox: una casilla de verificación.  JToolTip: muestra una breve descripción de un componente.  JTree: muestra datos organizados de forma jerárquica.  JMenu: una opción de menú. Componentes que cuya misión principal es la de obtener información relativa a la entrada del usuario.  JTextField: una caja de texto en la que el usuario puede escribir. aunque es posible encontrar componentes atómicos que son la combinación de distintos componentes.  JTable: muestra la información en formato de tabla. un icono o ambos. como pueden ser bordes y tooltips.  JButton: un botón común. 180 .  JList: una lista con elementos. en lugar de contener otros componentes.  JProgressBar: barra que muestra el progreso de un proceso. A lo largo de los siguientes apartados se van a mostrar algunos de estos componentes con sus respectivos ejemplos.

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

setActionCommand("activa"). botonDer. ventana.setEnabled(false). } public void actionPerformed(ActionEvent evento){ if (evento. } public void añadeBotones(){ //creamos un panel para añadir los botones JPanel panelContenido=new JPanel(). //se registran los oyentes. botonCentro=new JButton("Botón central".setEnabled(true).setVisible(true).equals("desactiva")){ //se desactiva el botón central y se actualiza //el estado de los otros botones botonCentro. botonDer.setEnabled(true).RIGHT).add(botonDer). que son la misma clase.creaBotones(). } } Se puede comprobar si pulsamos las teclas Alt+D o Alt+A tienen el mismo efecto que pulsar el botón correspondiente. botonDer. //establecemos este panel como panel de contenido de la ventana setContentPane(panelContenido). botonCentro. ventana. ventana.setToolTipText("Activo el botón central"). getRootPane().setEnabled(false). panelContenido.iconoCentro). botonDer. panelContenido.setEnabled(true). botonCentro.getActionCommand(). botonDer.VK_A).setMnemonic(KeyEvent.add(botonCentro). botonIzq.setToolTipText("No hago nada"). } } public static void main(String args[]) { Botones ventana = new Botones().pack(). botonDer.botonDer. }else{ //se activa el botón central botonCentro.setHorizontalTextPosition(AbstractButton.addActionListener(this).setDefaultButton(botonDer). Podemos modificar el ejemplo anterior para que el botón activo por defecto sea el botón de la derecha.setEnabled(false). botonIzq.add(botonIzq). además esta combinación de teclas aparece descrita en el tooltip. Podemos indicar el botón activo por defecto de un contenedor de alto nivel mediante el método setDefaultButton() de la clase JRootPane.addActionListener(this). botonIzq. 182 .añadeBotones(). panelContenido.setEnabled(false). ventana. El botón activo por defecto aparece destacado del resto y si el usuario pulsa la tecla Enter es equivalente a pulsar este botón.

. import java. private JCheckBox chkToolTip. Las casillas de verificación se suelen agrupar y es posible seleccionar. Estas características son la posibilidad de utilizar imágenes.. también se pueden utilizar estas casillas de verificación dentro de elementos de menú. botonCentro=new JButton("<html><small><i>Botón central</i></small>". //panel del botón private JPanel panelBoton.awt.iconoIzq).*. Se dispone de tres casillas de verificación. que ya hemos visto en la sección anterior con la clase JButton. Como se puede apreciar aunque los botones se encuentran desactivas...gif").... import java.iconoCentro). escribiendo: botonIzq=new JButton("<html><b><i><u>D</u>esactiva botón central</i><b>".iconoDer).event. JCheckbox Esta clase representa a las casillas de verificación. mediante la clase JCheckBoxMenuItem. En el siguiente ejemplo se muestra la utilización de la clase JCheckBox para configurar el aspecto de un botón. para ello se debe poner la etiqueta <html> al inicio del texto.. Además hemos tenido que utilizar la etiqueta de subrayado de HTML (<u></u>) para indicar la tecla que se corresponde con cada botón. 183 . //el botón del que indicamos el aspecto private JButton boton. presenta una serie de características comunes a todos los tipos de botones. una para indicar que el botón va a tener un icono. import javax. . algunas o ninguna de ellas.swing.*. //panel de las casillas private JPanel panelCasillas.Otra característica que nos ofrece la clase JButton es la posibilidad de utilizar etiquetas HTML dentro del texto del botón... Así por ejemplo. public class Casillas extends JFrame implements ItemListener{ //cada una de las casillas private JCheckBox chkIcono. y a continuación se pueden utilizar las etiquetas HTML que se consideren necesarias para dar el formato conveniente al texto.awt. ... etc. una... private ImageIcon icono=new ImageIcon("icono2. tooltips.. teclas alternativas. ahora el texto no aparece en color gris atenuado.*. Debido a que la clase JCheckBox hereda de la clase AbstractButton. otra para indicar que tiene un tooltip y otra para indicar que tiene texto.. private JCheckBox chkTexto.. botonDer=new JButton("<html><font color='red' size='4'>"+ "<u>A</u>ctiva botón central</font>"... si retomamos nuestro código anterior y modificamos las líneas en las que se crean los botones.

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

la casilla que ha visto modificado su estado. public Opciones (){ super("Ventana con opciones"). //imagen private JLabel imagen. es decir.awt.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System. únicamente uno de los botones puede encontrarse seleccionado a un mismo tiempo. Para agrupar los botones se utiliza la clase ButtonGroup. JRadioButton Los botones de opción se suelen encontrar en grupos en los que. private JRadioButton opConejo. ventana. import java. //panel de las Opciones private JPanel panelOpciones.*. private JRadioButton opCerdo. por convención. Nuestro ejemplo va a contener tres botones de opción agrupados para mostrar tres dibujos distintos. por lo tanto presentará el comportamiento común a todas las clases que heredan de AbstractButton.ventana.event. La clase JRadioButton también tiene como superclase o clase padre a la clase AbstractButton. } } Como se puede observar la clase JButton lanza un evento del tipo ItemEvent cuando se modifica el estado de un objeto.*.*. this. import javax.swing. También podemos utilizar botones de opciones en elementos de menú mediante la clase JRadioButtonMenuItem.setVisible(true). Para esta clase vamos a mostrar un ejemplo que ofrecerá un dibujo de un animal distinto según el botón de opción que se encuentre seleccionado en cada momento. public class Opciones extends JFrame implements ActionListener{ //cada una de las Opciones private JRadioButton opGato. import java. //panel de la imagen private JPanel panelImagen. En el método itemStateChanged() debemos averiguar primero la fuente del evento.awt. y a continuación si la casilla en cuestión ha sido seleccionada o no. } 185 .pack().exit(0). por ejemplo podemos indicar que un objeto JRadioButton muestre una imagen o un tooltip.

opGato. panelImagen. panelOpciones.setActionCommand("gato.add(opGato). opGato.setLayout(new GridLayout(1.VK_G). panelOpciones.setMnemonic(KeyEvent. el botón que se pulse (seleccione) va a lanzar un evento de la clase 186 . opCerdo. //se agrupan las opciones ButtonGroup grupo=new ButtonGroup().}).add(imagen. opCerdo.gif"). opConejo. } public static void main(String args[]) { Opciones ventana = new Opciones(). } public void creaImagen(){ panelImagen=new JPanel().setLayout(new BorderLayout()).pack(). getContentPane(). ventana.BorderLayout. imagen. opGato.addActionListener(this).gif").1)). ventana.creaOpciones().VK_J).setActionCommand("cerdo. panelOpciones.addActionListener(this). en lo que a tratamiento de eventos se refiere.add(opConejo). imagen = new JLabel(new ImageIcon("cerdo.setPreferredSize(new Dimension(177.CENTER). getContentPane().add(opCerdo). grupo. 122)). panelImagen. opCerdo.setMnemonic(KeyEvent.add(panelImagen). ventana.setVisible(true). imagen. opCerdo. } } La clase JRadioButton.VK_C). } public void creaOpciones(){ panelOpciones=new JPanel().setSelected(true). grupo. opCerdo=new JRadioButton("Cerdo").add(panelOpciones).add(opGato).getActionCommand(). opConejo. grupo.gif").setMnemonic(KeyEvent.gif")). panelOpciones.creaImagen(). ventana.setLayout(new GridLayout(3.setActionCommand("conejo.setIcon(new ImageIcon(comando)). funciona igual que la clase JButton. opConejo=new JRadioButton("Conejo").add(opConejo). getContentPane(). opGato=new JRadioButton("Gato").2)).addActionListener(this). } public void actionPerformed(ActionEvent evento){ String comando=evento.add(opCerdo). opConejo.

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

que contiene varios elementos de menú.  JMenu: es una opción de menú determinada. Para indicar la opción seleccionada por defecto utilizamos el método setSelectedIndex() sobre el objeto de la clase JComboBox. es decir. que 188 . el mismo evento que se lanza cuando se pulsa un botón. El constructor utilizado para instanciar un objeto de la clase JComboBox recibe como argumento un array de cadenas (String). A continuación se comentan cada una de estas clases. que representa las opciones que muestra la lista desplegable. y para obtener la opción seleccionada y así mostrar la imagen correspondiente se utiliza el método getSelectedItem() de la clase JComboBox. Los menús parecen normalmente dentro de barras de menú o como menús contextuales (menú popup). la jerarquía que presentan los distintos componentes Swing relacionados con la creación de menús. Un elemento de menú hereda también de la clase AbstractButton. que serán objetos de la clase JMenu. como muchas de las clases que hemos visto hasta ahora.Al seleccionar un elemento del objeto JComBox se lanza un evento ActionEvent.  JMenuBar: representa la barra de menú que va a contener los distintos elementos de menú. JMenu Un menú permite elegir al usuario entre múltiples opciones disponibles. veamos en la Figura 92.

setMnemonic(KeyEvent. //se crea el primer menú menu = new JMenu("Un Menú").setMnemonic(KeyEvent. barraMenu. } }).setAccelerator(KeyStroke. JCheckBoxMenuItem cbElementoMenu.ALT_MASK)). elementoMenu=new JMenuItem("Texto e icono". menu. elementoMenu. JRadioButtonMenuItem rbElementoMenu. además 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.VK_E).add(elementoMenu). //se asigna tecla de acceso rápido elementoMenu.exit(0).getKeyStroke( KeyEvent.KeyEvent. } public void creaMenu(){ //Se crea la barra de menú barraMenu = new JMenuBar(). y también puede contener submenús.addActionListener(this).new ImageIcon("icono2.  JPopupMenu: representa un menú contextual o de aparición súbita.  JRadioButtonMenuItem: elemento de menú que posee un botón de opción. this. Como ocurre en estos casos primero se muestra el código fuente del ejemplo.VK_T). elementoMenu.setMnemonic(KeyEvent. elementoMenu. menu.event.awt.*. public class Menus extends JFrame implements ActionListener.add(elementoMenu).serán objetos de la clase JMenuItem.addActionListener(this).  JCheckBoxMenuItem: elemento de menú que posee una casilla de verificación.addActionListener(this). submenu. setJMenuBar(barraMenu). 189 . public Menus(){ super("Ventana con múltiples menús").VK_D).addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.awt.gif")). JMenuItem elementoMenu.VK_1. menu.addSeparator(). menu.VK_E). import javax. elementoMenu. ItemListener{ JMenuBar barraMenu. JScrollPane panelScroll.swing. JMenu menu. En el Código siguiente se muestra la utilización de todas estas clases. import java. objetos de la clase JMenu.add(menu).gif")).  JMenuItem: es un elemento de menú. es decir. //unos cuantos elementos de menú elementoMenu=new JMenuItem("Elemento de menú de texto".*.*. elementoMenu=new JMenuItem(new ImageIcon("icono2. //se añade un separador menu. import java. elementoMenu. contiene elementos de menú (JMenuItem).  JSeparator: elemento de menú especial que ofrece una separación entre elementos de menú de una misma opción de menú.add(elementoMenu). ActionEvent. JTextArea texto.

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

elementoMenu=new JMenuItem("Segundo del popup". El área de texto (JTextArea) encargada de ir mostrando los eventos que se producen se añade a un panel de scroll (ScrollPane). //se crean y añadden los distinos elementos de menú de la misma manera elementoMenu=new JMenuItem("Primer elemento del popup". Un contextual o de aparición súbita (popup) se encuentra representado por la clase JPopupMenu y debe registrarse un oyente de ratón en cada componente que tenga asociado el menú popup. ventana.pack(). ventana.new ImageIcon("icono2. Se va añadir un nuevo atributo a nuestra clase. } } Los elementos de menú lanzan eventos ActionEvent. ventana. elementoMenu. También se va a añadir un nuevo método llamado creaPopup(). ventana. Como se puede ver se han añadido dos teclas rápidas mediante el método setAccelerator(). llamado menuPopup que pertenece a la clase JPopupMenu. 191 .append(mensaje). el oyente debe detectar que el usuario a pulsado el botón derecho del ratón para mostrar el menú contextual correspondiente.texto. y los elementos de menú del tipo JCheckBoxMenuItem lanzan eventos de la clase ItemEvent. public void creaPopup(){ menuPopup=new JPopupMenu().KeyEvent.add(elementoMenu). Vamos a modificar el ejemplo anterior en el que utilizábamos la barra de menú y vamos a añadir 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ú.addActionListener(this). El oyente que se ocupa de mostrar el menú contextual lanza el método show() sobre la instancia correspondiente de la clase JPopupMenu.creaMenu(). } public static void main(String args[]) { Menus ventana = new Menus(). menuPopup. El método show() recibe como parámetros el componente al que se asocia el menú y las coordenadas de la pantalla en la que se quiere mostrar el menú contextual.VK_P).gif")).setVisible(true).creaTexto().

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 método se puede lanzar una vez utilizado el método 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 código 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 utilización 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á también cuando se pulse el botón izquierdo del ratón, y no sólo cuando se pulse el botón derecho, que sería lo deseable. La clase MouseEvent ofrece el método isPopupTrigger() para averiguar si debemos mostrar el menú popup o no. El método isPopupTrigger() devolverá verdadero si la pulsación del ratón se corresponde con un evento que debe mostrar un menú contextual, en el caso de Windows se corresponde con el botón derecho del ratón. Así nuestro código fuente del adaptador del ratón quedaría como se muestra en el Código :
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 botón derecho ni con el botón derecho del ratón, la verdad no he conseguido discernir porque no aparece.

JSlider
Este componente permite al usuario seleccionar un valor numérico 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 según se indique en el selector (JSlider) se dará un tamaño determinado a un botón (JButton), el botón se redimensionará según 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 botón"); 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 código 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 orientación del selector correspondiente, se corresponde con las constantes HORIZONTAL y VERTICAL de la clase JSlider. Los siguientes argumentos, especifican respectivamente, el valor mínimo, el valor máximo y el valor seleccionado inicialmente del objeto JSlider. Si no indicamos estos valores, el valor mínimo del selector será 0, el máximo 100 y el seleccionado inicialmente 50. Para configurar inicialmente el objeto de la clase JSlider, hemos utilizado una serie de métodos:  setMajorTickSpacing(int espaciado): este método nos permite indicar las separaciones mayores entre el máximo y el mínimo del selector.  setMinorTickSpacing(int espaciado); este método indica las separaciones menores entre el rango indicado para el componente Swing JSlider.  setPaintTicks(boolean): mediante este método 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 método 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 método que debe implementar un oyente de eventos ChangeEvent es el método stateChanged(). En el ejemplo anterior hemos visto que se ha utilizado el método getValueIsAdjusting() de la clase JSlider, este método devuelve verdadero mientras se esté
194

seleccionando el valor del objeto JSlider, de esta forma no se redimensionará el botón hasta que el usuario no haya finalizado de arrastrar el tirador del selector, es decir, cuando el usuario ha finalizado con el proceso de selección. Las etiquetas que aparecen en el selector las podemos personalizar con la ayuda del método 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 método put() vamos indicando la posición que queremos que ocupe la etiqueta y el objeto JLabel que va a mostrar el texto correspondiente. Veamos, en el Código 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("Minúsculo")); etiquetas.put(new Integer(100),new JLabel("Pequeño")); 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 función es la de recoger la entrada del usuario, a continuación vamos a comentar algunos componentes atómicos cuya función es la de simplemente mostrar información al usuario, algunos de los representantes de este grupo que vamos a tratar a continuación ya los conoceremos de ejemplos anteriores, como ocurre con el componente JLabel.
195

Componentes para mostrar información
Se ofrecen distintos ejemplos de componentes Swing encargados de mostrar información.

JLabel
Ya hemos utilizado numerosas veces este útil y sencillo componente de Swing, como ya sabemos su labor es la de mostrar una información al usuario, ya sea textual o con imágenes o con ambos elementos. En el siguiente ejemplo se muestra la utilización de la clase JLabel creando cuatro objetos distintos de esta clase, se ha añadido 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("Sólo 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 añadeEtiquetas(){ 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.añadeEtiquetas(); ventana.pack(); ventana.setVisible(true); }}
196

También se puede indicar la alineación de los componentes dentro de la etiqueta con los métodos setHorizontalAlignment() y setVerticalAlignment(). RIGHT.setBorder(BorderFactory. Al igual que sucedía con la clase JButton.event.setBorder(BorderFactory. this.setBorder(BorderFactory. JLabel.createLineBorder(Color. y mediante las constantes LEFT. getContentPane(). } }). getContentPane().add(etiqueta4).blue)).*.createLineBorder(Color. private JLabel etiqueta3. getContentPane().15.swing.Como se puede observar en el ejemplo anterior es posible especificar la alineación del texto respecto a la imagen que contiene la etiqueta utilizando el método setVerticalTextPosition() y setHorizontalTextPostion(). } public static void main(String args[]) { 197 .BOTTOM).CENTER). etiqueta1.createLineBorder(Color.createLineBorder(Color. TOP y BOTTOM.setVerticalTextPosition(JLabel. etiqueta2=new JLabel("<html><ul><li>Elemento 1<li>Elemento2</ul>"). etiqueta3.setHorizontalTextPosition(JLabel.setVerticalTextPosition(JLabel.JLabel.CENTER).green)). etiqueta4=new JLabel("<html><h2><i>Imagen y texto</i></h2><hr>".addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System. etiqueta1. con los componentes JLabel podemos utilizar código HTML para indicar el formato del texto que se va a mostrar.15)). 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>". etiqueta1. import javax. El valor por defecto es centrado (CENTER).CENTER). etiqueta4.red)).gif"). icono.exit(0). getContentPane(). etiqueta2.*.add(etiqueta3).awt. CENTER.add(etiqueta1). private JLabel etiqueta2. import java. private JLabel etiqueta4. public class Etiquetas extends JFrame{ private JLabel etiqueta1. } public void añadeEtiquetas(){ getContentPane(). public Etiquetas(){ super("Ventana con etiquetas"). etiqueta4.icono.1.JLabel.setHorizontalTextPosition(JLabel. private ImageIcon icono.awt.CENTER).CENTER).setBorder(BorderFactory.TOP). } public void creaEtiquetas(){ icono=new ImageIcon("icono2.*.add(etiqueta2).setLayout(new GridLayout(4.pink)). etiqueta1=new JLabel("<html><font size='4' color='red'><b><i>"+ "Imagen y texto</b></i></font>". Veamos un ejemplo:l import java. etiqueta4.

para ello se utilizan las constantes HORIZONTAL y 198 . también podemos encontrar la clase JToolTip. sino que utilizaremos los métodos setToolTipText() y getTootTipText() de la clase JComponent.450). nos va informando de la evolución de un proceso. } } JToolTip Dentro del grupo de componentes atómicos de Swing encargados exclusivamente de mostrar información. la barra de progreso va mostrando el porcentaje de tarea que se ha realizado.creaEtiquetas(). esta clase representa las pequeñas ayudas que se muestran en forma de pequeño texto explicativo (tooltip) cuando nos situamos sobre un componente determinado. ventana.setVisible(true). ventana. ventana. JProgressBar Este componente Swing muestra gráficamente el progreso de una operación determinada. ventana.Etiquetas ventana = new Etiquetas(). Aunque realmente no vamos a utilizar nunca directamente la clase JToolTip. ya hemos visto la utilización de estos métodos en ejemplos anteriores. Al crear una instancia de la clase JProgressBar podemos indicar la orientación que va a tener la barra de progreso.setSize(300. con el primero asignamos un tooltip a un componente Swing y con el segundo obtenemos el tooltip que tiene asignando un componente Swing determinado.añadeEtiquetas().

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

nos permite seleccionar un elemento determinado de esa información. ventana. JColorChooser Este componente Swing además de ofrecer una información estructurada. ventana. } } Como se puede comprobar en el ejemplo. Componentes que muestran información 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. este tanto por ciento lo calcula teniendo en cuenta el valor actual y sus valores máximo y mínimo. como pueden ser los distintos colores.setVisible(true). los siguientes componentes pertenecen al grupo de los componentes atómicos que muestran también una información pero de una forma más compleja y estructurada. La clase JColorChooser representa un componente Swing atómico. 200 . respectivamente. la barra de progreso muestra el tanto por ciento (%) de la operación realizada.200). ventana.creaBarra(). Para obtener el tanto por ciento de la tarea realizada (completada) representada por la barra de progreso disponemos del método getPercentComplete(). Veremos dos ejemplos de las dos formas que podemos utilizar el componente JColorChooser: como si se tratara de otro componente más o como un diálogo. Además este componente permite mostrar de forma sencilla un diálogo que permite seleccionar también un color. Para que se muestre el etiqueta del tanto por ciento se ha utilizado el método setStringPainted() pasándole como argumento el valor true.setSize(200. ventana. Este es el último de los componentes Swing pertenecientes al grupo de componentes encargados de mostrar información.BarraProgreso ventana = new BarraProgreso().creaBoton(). Podemos mostrar en la barra de progreso cualquier texto mediante el método setString() de la clase JProgressBar y así variar el comportamiento por defecto de la barra de progreso. además en algunos casos permiten editar esta información. pero bastante complejo y completo que permite seleccionar colores de distintas formas.

Font. El panel de muestra se encuentra en la parte inferior y va mostrando el color seleccionado en cada momento del panel de selección. cada una de ellas permite una forma distinta de seleccionar los colores. etiqueta.BOLD. this. Para poder tratar la selección actual el componente JColorChooser hace uso de una clase llamada ColorSelectionModel que se encuentra en el subpaquete de Swing denominado javax. 24)). Pasemos ahora a ver un código de ejemplo que utiliza un objeto de la clase JColorChooser para configurar el color del texto de una etiqueta.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.green).JLabel. lanza un evento de la clase ChangeEvent (este evento vimos que también era lanzado por el componente JSlider) cada vez que se cambia la selección del color en el selector de color (JColorChooser). Para obtener la instancia de la clase ColorSelectionModel correspondiente a un objeto de la clase JColorChooser. pero no se debe hacer directamente sobre el componente JColorChooser.CENTER). El modelo de selección de color (instancia de la clase ColorSelectionModel).swing. private JColorChooser selectColor. } }).white).colorchooser.event.*. sino sobre su ColorSelectionModel. import java.swing.setOpaque(true). Si queremos detectar los cambios de selección de color del componente JColorChooser debemos registrar el oyente correspondiente al evento ChageEvent. public SelectorColor() { super("Selector de color"). public class SelectorColor extends JFrame implements ChangeListener{ private JLabel etiqueta. 201 .swing.setForeground(Color. import javax.colorchooser.awt.*.*. import javax. debemos lanzar sobre el objeto JColorChooser el método getSelectionModel().awt. El panel de selección con pestañas encuentra en la parte superior y presenta tres pestañas. etiqueta. etiqueta.*.setBackground(Color.setFont(new Font("SansSerif".event.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.swing. un panel con pestañas (JTabbedPane) y un panel con la muestra de la selección (preview). etiqueta. import java. } public void creaEtiqueta(){ //Configuramos la etiqueta sobre la que se aplica la selección de color etiqueta= new JLabel("La Plataforma Java 2". import javax.*.exit(0).

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

y se lo aplica al texto de la etiqueta mediante el método setForeground(). en la Figura 102 se puede ver otra selección distinta. El método stateChanged(). que se lanzará cada vez que se cambie la selección de color del selector de color. utilizando el formato de color RGB.showDialog(Component padre. añadiéndolo al panel de contenido de la ventana. La sintaxis de este método es: JColorChooser. recupera el color actual mediante el método getColor() de la clase JColorChooser.Como se puede comprobar en el ejemplo el constructor utilizado de la clase JColorChooser recibe como parámetro un 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. En la ejecución del ejemplo se puede observar las tres formas distintas de seleccionar el color que permite el componente JColorChooser. String título. pero también podemos mostrarlo como un diálogo. En este caso el componente JColorChooser lo hemos tratado como otro componente atómico más. Color color) 203 . este es el color seleccionado inicialmente dentro del selector de color. para ello podemos utilizar el método estático showDialog() de la clase JColorChooser.

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

El componente JFileChooser es similar al visto anteriormente.ventana. 205 . } } Si el usuario cancela la selección de color. ya sea cerrando el diálogo o pulsando el botón de cancelación. no se devolverá ningún color.setVisible(true). sino que se devolverá el valor null (nulo). Al pulsar el botón selección de color aparece: JFileChooser Este es el último componente atómico de Swing que vamos a ver. podemos crearlo como un componente más y añadirlo a un panel o bien mostrarlo como un diálogo modal.

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

creaBotones().5)).add(panelResultado. } public void actionPerformed(ActionEvent evento) { if (evento.showOpenDialog(this). JScrollPane panelResultado = new JScrollPane(resultado).5.20). es: 207 . ventana.append("Operación de grabado cancelada"+ "por el usuario\n"). }else{ int valorDevuelto= selectorFichero.APPROVE_OPTION) { File fichero = selectorFichero. resultado.append("Guardado: " + fichero.BorderLayout.getName()+"\n"). resultado.CENTER).5. getContentPane().append("Abierto: " + fichero.getSelectedFile().creaResultado().getSource()==botonAbrir){ int valorDevuelto= selectorFichero.pack().showSaveDialog(this). resultado. ventana. } } Y la del diálogo de selección de ficheros para su apertura.setMargin(new Insets(5. if (valorDevuelto== JFileChooser. } else resultado. } } public static void main(String[] args) { DialogoSelectorFichero ventana = new DialogoSelectorFichero(). ventana.setEditable(false). resultado.append("Apertura cancelada por el usuario\n").getSelectedFile().getName()+"\n"). } else resultado. ventana. if (valorDevuelto== JFileChooser.setVisible(true).resultado = new JTextArea(5.APPROVE_OPTION) { File fichero = selectorFichero.

Cada vez que se muestra el diálogo para la selección de ficheros guarda el directorio actual de la selección anterior. Además permite la creación de nuevos directorios. en el siguiente capítulo trataremos el nuevo gestor de diseño de Swing y la característica Pluggable Look & Feel. es decir. A través de este objeto podemos manipular ficheros. Interfaces de usuario en Java: otras características de Swing Introducción Este capítulo cierra el ciclo de capítulos dedicado a Swing. que devuelve un objeto de la clase File. en una aplicación real se utilizaría para guardar o abrir ficheros. 208 . este capítulo no va a ser tan extenso como los anteriores ya que trataremos dos aspectos muy determinados. Este ha sido el componente Swing atómico que cierra este capítulo. El constructor de la clase JFileChooser puede recibir como argumento un objeto File o String para indicar el directorio inicial. para obtener el fichero que se ha seleccionado se utiliza el método getSelectedFile().Como se puede comprobar en el ejemplo. representado por la clase BoxLayout y la característica especial que ofrece Swing denominada Pluggable Look & Feel. el nuevo gestor de diseño de Swing. El directorio inicial también se puede especificar mediante el método setCurrentDirectory() al que se le pasa como argumento un objeto de la clase File. va recordando las rutas de las selecciones anteriores.

add(new JButton("Soy un botón")).pack(). import java. ventana. representa a un nuevo gestor de diseño ofrecido por Swing. import javax. Antes de seguir comentando este gestor de diseño ofrecido por Swing.setVisible(true).add(new JButton("Soy el último")). JPanel panelContenido=new JPanel().El gestor de diseño BoxLayout La clase BoxLayout.  Y_AXIS: los componentes se distribuyen de arriba a abajo. Este gestor de diseño organiza los componentes en una pila.exit(0).X_AXIS. ventana. Se puede decir que es una versión ampliada del gestor de diseño FlowLayout que ofrece AWT.swing. panelContenido.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.add(new JButton("Soy un botón más")). uno encima del otro y también de izquierda a derecha uno al lado del otro. } } En el ejemplo anterior podemos comprobar la forma de crear un gestor de diseño para distribuir los componentes de arriba a abajo.setContentPane(panelContenido). panelContenido.Y_AXIS por BoxLayout.add(new JButton("Otro botón")). ventana.event. Si retomamos el ejemplo anterior y cambiamos la constante BoxLayout.Y_AXIS)).*. panelContenido. se obtiene el resultado que muestra continuación: 209 . Este último parámetro 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. panelContenido.setLayout(new BoxLayout(panelContenido. vamos a ver con un sencillo ejemplo la forma en la que distribuye los componentes en el panel al que se aplica.swing.*. panelContenido. ventana. que podemos encontrar en el paquete javax. El constructor de la clase BoxLayout posee dos argumentos. el panel (JPanel) sobre el que se va a aplicar el gestor de diseño y la orientación en la que se van a distribuir los componentes dentro de dicho panel. } }). public class BoxLayoutSencillo{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout").awt.BoxLayout.

panelInferior. import javax. ventana. panelSuperior.awt.event.BorderLayout.createLineBorder(Color. panelInferior. panelSuperior.setBorder(BorderFactory.add(new JTextArea(5.pack().createRigidArea(new Dimension(0.red)). JPanel panelInferior=new JPanel(). panelSuperior. import java.swing.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.add(Box.add(Box.Y_AXIS)). panelInferior.5))). JPanel panelSuperior=new JPanel(). } }).*.BoxLayout. ventana.add(new JButton("Botón 1")).add(panelInferior.add(new JLabel("Esto es una etiqueta")).getContentPane().add(new JButton("Botón 2")). } } El interfaz que genera este código es: A la vista del código se puede decir que este interfaz está compuesto por dos paneles los que se han aplicado un gestor de diseño BoxLayout distinto.setLayout(new BoxLayout(panelSuperior.BoxLayout.createRigidArea(new Dimension(10. ventana.30)).add(Box.getContentPane().add(panelSuperior.exit(0). ventana.green)). public class DosBoxLayout{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout").0))).CENTER).setBorder(BorderFactory.createLineBorder(Color. panelInferior.X_AXIS)). panelSuperior. panelInferior.NORTH).*. se suele utilizar la clase Box.BorderLayout.setLayout(new BoxLayout(panelInferior.setVisible(true). panelSuperior. esta clase ofrece una serie de métodos para controlar de una forma más precisa la forma en la que se distribuyen los componentes dentro de un panel al que se le ha aplicado este gestor de diseño BoxLayout. import java. panelInferior. ventana.awt.createHorizontalGlue()).*. 210 . uno de arriba a abajo y otro de izquierda a derecha.Conjuntamente con la clase BoxLayout.

addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System. JPanel panelContenido=new JPanel(). ventana.RIGHT_ALIGNMENT).*. public class BoxLayoutSencillo{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout"). JButton boton2=new JButton("Soy un botón más"). boton1. JButton boton3=new JButton("Otro botón"). JButton boton4=new JButton("Soy el último").setAlignmentX(Component. el método createHorizontalGlue(). En este caso se ha indicado una separación vertical de cinco pixels. JButton boton1=new JButton("Soy un botón"). este método rellena el espacio sobrante y desplaza a los componentes hacia la derecha. es decir.awt.exit(0). 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. El gestor de diseño BoxLayout respeta el tamaño máximo de los componentes que contiene y también la alineación establecida por los mismos. aunque para que no existan problemas de alineación todos los componentes deben tener la misma. boton3. Si modificamos el primer ejemplo visto en este apartado tenemos: import java. enviando al segundo botón a la derecha del todo. En este panel inferior también se utiliza el método createRigidArea() para separar los dos botones diez pixels.event. } }).*. La clase Box ofrece también el método createVerticalGlue(). Si la llamada al método createHorizontalGlue() la hubiéramos realizado en el lugar en la que se encuentra la llamada al método createRigidArea(). En el segundo panel se utiliza otro método de la clase Box. en este caso se ha utilizado un área sólo con componente horizontal. En el primer panel se ha utilizado el método createRigidArea() de la clase Box para crear una separación fija (área rígida) invisible entre la etiqueta (JLabel) y el área de texto (JTextArea).awt. import java.CENTER_ALIGNMENT). en este caso el área invisible se extiende de forma vertical enviando los componentes dentro del panel hacia la zona inferior.setAlignmentX(Component.Se ha aplicado un borde a cada uno de estos dos paneles para distinguirlos más claramente. este método provoca que los dos botones que se añadan a continuación se desplacen hacia la derecha. El método createHorizontalGlue() crea un área invisible que crece horizontalmente para rellenar todo el espacio libre disponible dentro del contenedor. 211 .

} }). ventana. ventana.CENTER_ALIGNMENT).setAlignmentX(Component. import java.CENTER_ALIGNMENT). JButton boton4=new JButton("Soy el último"). panelContenido. panelContenido.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.*.setLayout(new BoxLayout(panelContenido.Y_AXIS)). ventana. panelContenido.event.setAlignmentX(Component. JButton boton1=new JButton("Soy un botón").add(boton2). ventana.awt. boton3.CENTER_ALIGNMENT). } } 212 .pack().BoxLayout. public class BoxLayoutSencillo{ public static void main(String args[]) { JFrame ventana = new JFrame("Ventana con BoxLayout"). panelContenido. boton2.add(boton2). ventana.setAlignmentX(Component. JButton boton3=new JButton("Otro botón").add(boton1).CENTER_ALIGNMENT). import java.add(boton3). ventana.BoxLayout.pack(). JPanel panelContenido=new JPanel().setVisible(true).exit(0).swing.awt.boton4.CENTER_ALIGNMENT). panelContenido.setContentPane(panelContenido). panelContenido.setAlignmentX(Component.*. JButton boton2=new JButton("Soy un botón más").add(boton4).*. panelContenido.add(boton4). boton1.add(boton1).setVisible(true). } } La alineación se respetará en este otro ejemplo.setLayout(new BoxLayout(panelContenido.Y_AXIS)).add(boton3). ventana. import javax. panelContenido.setAlignmentX(Component.setContentPane(panelContenido). panelContenido. panelContenido. boton4.

Incluso. 213 .metal. tienen la característica común que todas ellas heredan de la clase abstracta javax. Se pueden distinguir cuatro Look & Feel estándar 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.mac.MacLookAndFeel. Se corresponde con la implementación de la clase javax.plaf. Este Look & Feel lo podemos utilizar en cualquier plataforma.  Motif: representa el aspecto CDE/Motif. es decir.swing. Todas estas clases que implementan un Look & Feel determinado. esta plataforma es la plataforma Mac OS. Al igual que ocurría con el Look & Feel de Windows. como veremos un poco más adelante. que es el Look & Feel de Java. podemos establecer y cambiar en tiempo de ejecución de nuestra aplicación su Look & Feel. Cuando no se indica ningún Look & Feel determinado Swing utiliza el Look & Feel por defecto.Estableciendo el Look & Feel En el capítulo anterior comentábamos que una característica que ofrece Swing es el aspecto y comportamiento configurable de sus componentes.swing.java. y al igual que el Look & Feel de Java puede ser utilizado en cualquier plataforma.plaf.  Mac: este último Look & Feel es el correspondiente a los sistemas Macintosh. lo que se denomina Pluggable Look & Feel.motif. A diferencia del anterior Look & Feel. Se encuentra en al clase javax.MetalLookAndFeel. que recibe como parámetro una cadena que especifica la clase que se corresponde con el Look & Feel que deseamos asignar a nuestra aplicación Java. Para indicar un Look & Feel determinado en una aplicación Java utilizaremos la clase UIManager (gestor de interfaz gráfico) que se encuentra en el paquete javax. ya que en nuestros ejemplos no hemos especificado ningún Look & Feel.plaf.  Windows: es aspecto que presentan los sistemas Windows y lo encontramos implementado en la clase com. también denominado Metal (fue el código que se le dio al proyecto que lo desarrolló).windows. que es el aspecto que tienen las plataformas de Sun. este Look & Feel está restringido a una plataforma específica. éste únicamente puede ser utilizado en plataformas Windows.java.swing.swing. a nuestra aplicación Java le podemos asignar un aspecto específico.sun.LookAndFeel.sun.swing. La clase UIManager ofrece un método estático llamado setLookAndFeel().WindowsLookAndFeel.plaf. Este Look & Feel es implementado por la clase com.swing.MotifLookAndFeel.

MotifLookAndFeel"). para que se redimensionen todos los componentes según su nuevo Look & Feel. try { UIManager. conjuntamente con el método setLookAndFee() se puede ver en el Código siguiente. podemos establecer el Look & Feel de la aplicación en tiempo de ejecución.swing.out. así por ejemplo si deseamos aplicar el Look & Feel de la plataforma sobre la que se ejecuta la aplicación. que se encuentra también dentro del paquete javax. El método 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. además del método setLookAndFeel().getSystemLookAndFeelClassName()).sun. En este caso además de utilizar el método setLookAndFeel() de la clase UIManager. utilizaríamos el Código: try { UIManager. De esta forma si queremos que nuestra aplicación posea el Look & Feel de Motif deberemos incluir el siguiente código dentro del método main() de la aplicación.setLookAndFeel("com. } catch (Exception excepcion) { System. SwingUtilities.plaf. } catch (Exception e) { } new AplicacionSwing(). try { UIManager.swing. Además es recomendable lanzar el método pack() sobre cada uno de los contenedores de alto nivel del interfaz de usuario. y el segundo caso se corresponde con el nombre de la clase que ofrece el Look & Feel de Java. La utilización de este método.java.setLookAndFeel(UIManager. Ambos métodos devuelven una cadena. presente también en el paquete javax. para que de esta forma todos actualicen su Look & Feel en tiempo de ejecución. cuando el Look & Feel que queremos utilizar no es soportado por la plataforma actual. como ya hemos adelantado al comienzo de este apartado. es decir. Por lo tanto estos dos métodos se pueden utilizar como argumento para el método setLookAndFeel(). estos métodos son getSystemLookAndFeelClassName() y getCrossPlatformLooAndFeelClassName().El método setLookAndFeel() de la clase UIManager lanza una excepción de la clase UnsupportedLookAndFeelException.setLookAndFeel(lf).motif. } En este caso se supone que sólo existe un contenedor de alto nivel. deberemos atrapar por lo tanto esta excepción cuando invoquemos el método setLookAndFeel().swing. this. El método updateComponentTreeUI() de la clase SwingUtilities debe utilizarse para cada uno de los contenedores de alto nivel del interfaz de usuario de la aplicación.println("No se pudo asignar el Look & Feel"). se puede establecer una vez que la aplicación ya se ha iniciado y el interfaz de usuario es visible. } catch (Exception e) { } 214 . La clase UIManager. deberemos utilizar el método estático updateComponentTreeUI() de la clase SwingUtilities.pack(contenedor).updateComponentTreeUI(contenedor). nos ofrece otros dos métodos que pueden resultar de utilidad. Pero el Look & Feel. que es el Look & Feel que se garantiza funcionará correctamente para todas las plataformas. 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 aplicación.

debido a esto al arrancar la aplicación comienza a ejecutarse utilizando el Look & Feel de Java.BoxLayout. } En el constructor además de establecer el Look & Feel de Java se establece el gestor de diseño que va a utilizar el panel de contenido de la ventana.setLookAndFeel(UIManager. this. El código fuente perteneciente al constructor de nuestra clase se puede observar : public LF(){ super("Pluggable Look & Feel"). Al iniciarse su ejecución dentro del constructor de la aplicación. try { UIManager. contiene cinco botones de opción (JOptionButton) agrupados.getCrossPlatformLookAndFeelClassName()). } catch (Exception e) { } getContentPane(). Es decir. y se corresponde con el Look & Feel de Java. Antes de seguir comentando más detalles de esta aplicación de ejemplo. Al ejecutar la aplicación el aspecto que muestra es el de la Figura. se establece el Look & Feel mediante el nombre de la clase devuelto por el método getCrossPlatformLookAndFeelClassName() de la clase UIManager. existen cinco botones de opción. El gestor de diseño que se 215 . además de contener diversos componentes Swing. vamos a mostrar el aspecto que tendría esta aplicación.Para mostrar la característica Pluggable Look & Feel de Swing se va a utilizar un sencillo ejemplo que consiste una aplicación con una ventana (JFrame) que permite especificar el Look & Feel en tiempo de ejecución.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System. cuatro de ellos para cada uno de los distintos Look & Feel (Java. } }). LA ventana de la aplicación del ejemplo.setLayout(new BoxLayout(getContentPane().exit(0). Windows. Motif y Mac) y otro para seleccionar el Look & Feel de la plataforma sobre la que se ejecuta la aplicación. y que al seleccionar cada uno de ellos se aplicará sobre la aplicación el Look & Feel que representa cada opción.Y_AXIS)).

A continuación se muestran otras dos figuras. ya que el código fuente de la aplicación de ejemplo es bastante sencillo y se utilizan componentes Swing vistos en los capítulos anteriores. y además en mi caso particular el Look & Feel del sistema (Look & Feel de la plataforma actual) será por lo tanto el de Windows. Podemos ir asignando a la aplicación los distintos Look & Feel seleccionando la opción correspondiente. sólo vamos a mostrar y comentar aquellos fragmentos que resultan más interesantes (sobre todo desde el punto de vista de la característica Pluggable Look & Feel). descrito en el apartado anterior. 216 . por lo tanto éste Look & Feel no estará disponible. No vamos hacer como en otros ejemplos de este curso. De la figura anterior también se puede comprobar que la opción seleccionada por defecto se corresponde lógicamente con el Look & Feel que presenta la aplicación inicialmente. cada una con otro nuevo Look & Feel.utiliza es BoxLayout. La primera corresponde al Look & Feel de Windows y la segunda al Look & Feel de Motif. en su variante de distribución de componentes de arriba abajo (BoxLayout.Y_AXIS). ya que he utilizado una plataforma Windows para ejecutar la aplicación de ejemplo. en los que se ha ofrecido el código completo del ejemplo. En mi caso no he podido utilizar el Look & Feel de Mac.

add(opJava).MotifLookAndFeel".VK_S).add(opSistema).5.VK_C). grupo. grupo.plaf. opMotif.addActionListener(this).windows. panelOpciones. grupo.plaf. opJava.5. Será en este método dónde se establezca el Look & Feel seleccionado a través de los distintos botones de opción.setMnemonic(KeyEvent. opSistema.sun.5)).add(opSistema).add(opMotif). opJava=new JRadioButton("Java/Metal").addActionListener(this). panelOpciones. opWindows=new JRadioButton("Windows").metal. cuyo código veremos: public void creaOpciones(){ panelOpciones=new JPanel().add(opWindows).setBorder(BorderFactory.createLoweredBevelBorder()).plaf.swing.getSource().motif. else if(fuente==opMotif) lf="com. if (fuente==opWindows) lf="com. opWindows.addActionListener(this).addActionListener(this).sun.VK_M). opMotif=new JRadioButton("Motif"). opMotif. panelOpciones.setSelected(true).add(opMac).WindowsLookAndFeel". A continuación se ofrece el código fuente perteneciente al método actionPerformed(). //se agrupan las opciones ButtonGroup grupo=new ButtonGroup().add(opMotif).addActionListener(this). else if(fuente==opJava) lf="javax. opWindows. panelOpciones. opMac.setMnemonic(KeyEvent.swing. opSistema=new JRadioButton("Sistema").add(opJava). public void actionPerformed(ActionEvent evento){ Object fuente=evento. conjuntamente con el constructor de la clase.setMnemonic(KeyEvent. getContentPane(). panelOpciones. pero si que es interesante mostrar como se crean las opciones que permiten seleccionar el Look & Feel de la aplicación.add(opMac). opJava. opMac.add(opWindows). grupo. No vamos a detenernos en la creación de los distintos componentes del interfaz de usuario. 217 . opMac=new JRadioButton("Mac").swing. opJava. grupo. panelOpciones. } La pulsación de cada opción será tratada dentro del método actionPerformed() que realmente.setLayout(new GridLayout(1.add(panelOpciones). opSistema.public class LF extends JFrame implements ActionListener{ Como se puede ver es una sencilla ventana que va a contener una serie de componentes Swing. String lf="".setMnemonic(KeyEvent. es dónde utilizamos métodos y clases relacionados directamente con el mecanismo Pluggable Look & Feel.MetalLookAndFeel".VK_W).java. panelOpciones.setMnemonic(KeyEvent.java.VK_J). Para crear las opciones que representan los distintos LooK & Feel se ha utilizado el método creaOpciones().

mac.\n"+excepcion+"\n").getSystemLookAndFeelClassName().plaf. try { UIManager. Si ocurre una excepción se muestra en el área de texto (JTextArea) de la aplicación.MacLookAndFeel".append("Excepción de instanciación.swing. } catch (ClassNotFoundException excepcion){ texto. else if(fuente==opSistema) lf=UIManager. obtendré una excepción lanzada por el método setLooAndFeel(). } catch (UnsupportedLookAndFeelException excepcion) { texto. } catch(IllegalAccessException e){ texto. se nos obligará a atrapar tres excepciones más.\n"+excepcion+"\n"). esto se puede observar en la Figura. utilizando una plataforma Windows. selecciono la opción correspondiente al Look & Feel de Mac.\n"+e+"\n").append("Look & Feel no soportado. se decidimos atrapar la excepción UnsupportedLookAndFeelException.pack(). this. } catch (InstantiationException excepcion){ texto.append("Excepción de acceso. SwingUtilities.setLookAndFeel(lf).append("Clase no encontrada. Si en mi caso particular. } } Como se puede comprobar a la vista del código anterior. si se tiene en cuenta lo explicado hasta el momento.updateComponentTreeUI(this). Por lo demás.else if(fuente==opMac) lf="javax.\n"+excepcion+"\n"). el código de este método es bastante sencillo. 218 . lo único que se hace es establecer el Look & Feel que se corresponde con la opción pulsada.

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

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

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

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

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

if(comando. 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. boton.drawLine(0. import java.drawRect(100. g.drawLine(150. JPanel p =new JPanel(). todo lo demás es “borrado”. 90.SOUTH).blue). Si queremos que siempre que el componente se redibuje apareciesen nuestros dibujos hemos de sobrescribir el método paintComponent y escribir en el código necesario para realizar estos dibujos.event.(int)(this. public class Dibujar2 extends JFrame implements ActionListener{ public Dibujar2(){ setSize(400. 200).400).red). g.awt. g. Además el componente se dibujará cuando se crea y cuando se invoque el método repaint().equals("Salir")){ System. 150).getGraphics().addActionListener(this). } } El método paintComponent Cuando cualquier componente de las librerías Swing por cualquier razón necesita “redibujarse” en pantalla llama al método paintComponent. setTitle("Dibujo"). contentpane.*. g. import javax.getWidth().setLayout (new BorderLayout()). Cuando este método es invocado lo único que aparecerá en el componente es lo que se dibuje desde el. 150.getActionCommand().getHeight()).*. 200. g. Veamos como haríamos esto sobre el ejemplo Dibujar1: Ejemplo 2. Este es el motivo por el cual en nuestro ejemplo anterior al minimizar y luego maximizar la pantalla dejamos de ver lo que habíamos dibujado. BorderLayout.*. JButton boton = new JButton("Dibujar"). import java. p.} public void actionPerformed(ActionEvent e){ String comando = e.setColor(Color.fillRect(110. 150. contentpane.add(boton).setColor(Color.setActionCommand("Dibujar").exit(0).(int) (this. 80.add(p. JButton boton2 = new JButton("Salir"). 100.awt. En este método se halla toda la información que se necesita para dibujar el componente en pantalla. Container contentpane = this. } Graphics g = panel.swing.getSize()). 224 . public static void main(String[] args){ new Dibujar1(). } JPanel panel = new JPanel(). 100).getSize()). g. boton.getContentPane().0.

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

226 .

Y. JDBC también proporciona una base común para la construcción de herramientas y utilidades de alto nivel. fácil de usar. es necesario disponer del driver JDBC apropiado que haga de intermediario entre ésta y JDBC. seguro. 1. es un lenguaje base excelente para aplicaciones de base de datos. Dependiendo de varios factores. (Como punto de interés JDBC es una marca registrada y no un acrónimo. otro para acceder a Oracle y otro para acceder a Informix. En otras palabras. tampoco es necesario escribir diferentes aplicaciones para ejecutar en diferentes plataformas. 227 . 1. Usando JDBC es fácil enviar sentencias SQL virtualmente a cualquier sistema de base de datos. este driver puede estar escrito en Java puro. siendo robusto. o ser una mezcla de Java y métodos nativos JNI (Java Native Interface). y descargable automáticamente desde la red. INTRODUCCION JDBC Java Database Connectivity (JDBC) es una interfase de acceso a bases de datos estándar SQL que proporciona un acceso uniforme a una gran variedad de bases de datos relacionales. fácil de entender. Java. Drivers JDBC Para usar JDBC con un sistema gestor de base de datos en particular. no es necesario escribir un programa que acceda a una base de datos Sybase. Un único programa escrito usando el API JDBC y el programa será capaz de enviar sentencias SQL a la base de datos apropiada. Consiste en un conjunto de clases e interfases escritas en el lenguaje de programación Java. no obstante a menudo es conocido como “Java Database Connectivity”). con una aplicación escrita en el lenguaje de programación Java. La combinación de Java y JDBC permite al programador escribir una sola vez y ejecutarlo en cualquier entorno. JDBC suministra un API estándar para los desarrolladores y hace posible escribir aplicaciones de base de datos usando un API puro Java. con el API JDBC..1 ¿Qué es JDBC? JDBC es el API para la ejecución de sentencias SQL.

Una interfase de alto nivel es „amigable‟. es posible publicar una página web que contenga un applet que usa información obtenida de una base de datos remota. Con cada vez más y más programadores desarrollando en lenguaje Java. JDBC es una interfase de bajo nivel. con Java y JDBC API.getString("b").next()) { int x = rs. c FROM Table1").JDBC expande las posibilidades de Java.executeQuery("SELECT a. En esta función trabaja muy bien y es más fácil de usar que otros API‟s de conexión a bases de datos. while (rs. Envía sentencias SQL Procesa los resultados. El siguiente fragmento de código nos muestra un ejemplo básico de estas tres cosas: Connection con = DriverManager. Macintosh y UNIX) a una base de datos interna vía intranet. ResultSet rs = stmt. 228 . String s = rs. Por ejemplo. float f = rs. 1.getInt("a"). pero está diseñado de forma que también sea la base sobre la cual construir interfaces y herramientas de alto nivel.1. la necesidad de acceso fácil a base de datos desde Java continúa creciendo. b. usa un API mas entendible o más conveniente que luego se traduce en la interfase de bajo nivel tal como JDBC.getConnection ( "jdbc:odbc:wombat". "login". lo que quiere decir que se usa para „invocar‟ o llamar a comandos SQL directamente.2 JDBC es un API de bajo nivel y una base para API’s de alto nivel. "password").1. } 1. O una empresa puede usar JDBC para conectar a todos sus empleados (incluso si usan un conglomerado de máquinas Windows.1 ¿Qué hace JDBC? Simplemente JDBC hace posible estas tres cosas:    Establece una conexión con la base de datos.createStatement(). Statement stmt = con.getFloat("c").

portable y seguro en todas las plataformas Java.. puesto que se les da una orientación a objeto basándose en clases que se implementan sobre ODBC. ha sido diseñado para mantener las cosas sencillas mientras que permite las características avanzadas cuando éstas son necesarias. el código JDBC es automáticamente instalable. la implementación. el gestor de drivers de ODBC y los drivers deben instalarse manualmente en cada máquina cliente. Las llamadas desde Java a código nativo C tienen un número de inconvenientes en la seguridad. la robustez y en la portabilidad automática de las aplicaciones. JDBC retiene las características básicas de diseño de ODBC. JDBC por otro lado. Un API Java como JDBC es necesario en orden a permitir una solución Java “pura”. 4. es probablemente el API más extendido para el acceso a bases de datos relacionales. En resumen. Como el driver JDBC esta completamente escrito en Java. La pregunta es ahora ¿por qué necesito JDBC?.1. de hecho. el API JDBC es el interfase natural de Java para las abstracciones y conceptos básicos de SQL. Java no tiene punteros. incluyendo el notoriamente propenso a errores “void * “.ODBC no es apropiado para su uso directo con Java porque usa una interface C. ADO y OLE DB. ambos interfaces están basados en el X/Open SQL CLI (Call Level Interface). entonces. pero es preferible hacerlo con la ayuda de JDBC mediante el puente JDBC-ODBC. Mezcla características simples y avanzadas juntas. 2. 3.1. y ODBC hace un uso copioso de ellos. Cuando se usa ODBC. RDO. desde Java?. Más recientemente Microsoft ha introducido nuevas API detrás de ODBC. La respuesta es que se puede usar ODBC desde Java. Por ejemplo.3 JDBC frente a ODBC y otros API’s En este punto. Ofrece la posibilidad de conectar a la mayoría de las bases de datos en casi todas las plataformas. Estos diseños se mueven en la misma dirección que JDBC en muchas maneras. y sus opciones son complejas para „querys‟ simples.Una traducción literal del API C de ODBC en el API Java podría no ser deseable. el ODBC de Microsoft (Open Database Connectvity). 229 . Hay varias respuestas a estas preguntas: 1. ODBC es difícil de aprender.. Se puede pensar en JDBC como un ODBC traducido a una interfase orientada a objeto que es el natural para programadores Java. ODBC. ¿Por qué no usar.

En el modelo de tres-pisos. La base de datos procesa las sentencias SQL y devuelve los resultados a el „piso intermedio‟.4 Modelos en dos y tres pisos. que conecta a los empleados dentro de la corporación.1. que envía las sentencias SQL a la base de datos. Finalmente en muchos casos la arquitectura de tres niveles puede proporcionar ventajas de rendimiento. este nivel intermedio ha sido escrito en lenguajes como C ó C++. Hasta ahora. que ofrecen un rendimiento más rápido. En el modelo de dos-pisos. 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. o puede ser Internet. 230 . Otra ventaja es que el usuario puede emplear un API de alto nivel más sencillo que es traducido por el „piso intermedio‟ en las llamadas de bajo nivel apropiadas. Aplicacion Java Máquina cliente JDBC Protocolo propietario DBMS DBMS Servidor de BD. Esto requiere un driver JDBC que pueda comunicar con el gestor de base de datos particular al que se pretende acceder. De cualquier modo. Esta es una configuración Cliente/Servidor en la que la máquina del usuario es el cliente y la máquina que hospeda a la base de datos es el servidor. La red puede ser una intranet. La base de datos puede estar localizada en otra máquina a la que el usuario se conecta mediante la red. con la introducción de compiladores optimizadores que traducen el bytecode en código máquina eficiente. Las sentencias SQL de usuario se envían a la base de datos. que a su vez lo envía al usuario. se está convirtiendo en práctico desarrollar este nivel intermedio en Java. y el resultado de estas sentencias se envían al usuario. por ejemplo. un applet Java o una aplicación habla directamente con la base de datos. los comandos se envían a un „piso intermedio‟ de servicios. El API JDBC soporta los modelos en dos y tres pisos de acceso a base de datos.1.

de cualquier modo. Aplicacion Java Máquina cliente llamadas HTTP. Esto quiere decir que una aplicación es libre de usar la sentencia SQL tal como quiera. Una de las áreas de dificultad es que aunque muchas DBMS‟s (Data Base Management Systems) usan un formato estándar de SQL para la funcionalidad básica. 1. estos no conforman la sintaxis más recientemente definidas o semánticas para funcionalidades más avanzadas. y aquellas que lo hacen no son consistentes con otras. Es de esperar que la porción de SQL que es verdaderamente estándar se expandirá para incluir más y más funcionalidad. Por ejemplo. no todas las bases de datos soportan procedimientos almacenados o joins de salida. pero se corre el riesgo de recibir un error en el DBMS.Esta es una gran ventaja al hacer posible aprovechar las características de robustez. Una forma en que el API JDBC trata este problema es permitir que cualquier cadena de búsqueda se pase al driver subyacente del DBMS.1. o puede ser una derivación especializada de SQL 231 . multiproceso y seguridad de Java. De hecho una consulta de una aplicación incluso no tiene por que ser SQL. el API de JDBC debe soportar SQL tal como es. Entretanto. RML ó CORBA Aplicacion Servidora (Java) JDBC Servidor (Lógica de negocio) Protocolo propietario del DBMS DBMS Servidor de BD.5 SQL Conforme SQL es el lenguaje estándar para el acceso a las bases de datos relacionales.

Y es proveer información 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 API‟s de alto nivel . Los desarrolladores de drivers pueden cerciorarse que sus drivers cumplan estas especificaciones mediante la suite de test disponible en el API JDBC. La designación “JDBC COMPLIANT” indica que la implementación JDBC de un vendedor ha pasado los tests de conformidad suministrados por JavaSoft. Para aplicaciones complejas. Una segunda forma en que JDBC trata este problema es proveer cláusulas de escape al estilo de ODBC . pero esta definición de compliance tiene algún grado de seguridad en una implementación JDBC. ”Sintaxis de escape en Sentencias Objetos”.1. un driver debe soportar al menos el nivel de entrada ANSI SQL-2 Entry Level. En orden a usar esta designación.diseñada para especificas DBMS (para consultas a imágenes o documentos por ejemplo). Tales tests no son exhaustivos.5. Por ejemplo.sun. y JavaSoft no esta distribuyendo implementaciones de vendedores. Estas pruebas de conformidad chequean la existencia de todas las clases y métodos definidos en el API JDBC. http://java. La designación “JDBC COMPLIANT” se creó para situar un nivel estándar de funcionalidad JDBC en la que los usuarios puedan confiar. Existen una serie de productos basados en JDBC que ya han sido desarrollados. La sintaxis de escape provee una sintaxis JDBC estándar para varias de las áreas más comunes de divergencia SQL. JDBC se está convirtiendo en el estándar de acceso a bases de datos. JDBC trata la conformidad SQL de una tercera manera. Con la mayor aceptación de JDBC por parte de vendedores de bases de datos.com/products/jdbc 232 . 1. de servicios de Internet y desarrolladores. que se discutirán en el 4.2 Productos JDBC. por supuesto. ahí escapes para literales de fecha o procedimientos almacenados. y chequean tanto como es posible que la funcionalidad SQL Entry Level esté disponible. direccionará el problema de la conformidad a cualquiera de estas. Por supuesto la información de este apartado será rápidamente obsoleta.

1. Actualmente es bastante pequeña y simple. 233 .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. La suite de testeo JDBXC suministra seguridad y confianza en los drivers JDBC que se ejecutarán en el programa. Fue implementado como una forma de llegar rápidamente al fondo de JDBC y para proveer de acceso a los DBMS menos populares si no existen drivers JDBC para ellos. El puente JDBC-ODBC permite a los drivers ODBC usarse como drivers JDBC. su función principal es conectar las aplicaciones Java al driver JDBC correcto y después soltarlo.2. Solo los drivers que pasan el test pueden ser designados JDBC COMPLIANT.

3. Esto permite llamadas directas desde la máquina cliente al servidor DBMS y es la solución más práctica para accesos en intranets. debe cargarse en cada máquina cliente que use este driver.. los fabricantes de bases de datos serán los principales suministradores. Sybase. 1. 234 ..puente JDBC-ODBC más driver ODBC: El producto de JavaSoft suministra acceso vía drivers ODBC. Nótese que como el driver puente. Este middleware en el servidor de red es capaz de conectar a los clientes puros Java a muchas bases de datos diferentes.. este tipo de driver es el más apropiado en un red corporativa donde las instalaciones clientes no son un problema mayor. Nótese que el código binario ODBC. Este driver traduce llamadas JDBC al protocolo de red independiente del DBMS que después es traducido en el protocolo DBMS por el servidor. Como resultado.driver Java nativo JDBC-Net. 4.2. este estilo de driver requiere que cierto código binario sea cargado en cada máquina cliente.. En general esta es la alternativa más flexible. Informix. Este tipo de driver convierte llamadas JDBC en el protocolo de la red usado por DBMS directamente. y en muchos casos el código cliente de base de datos. Las categorías 1 y 2 son soluciones interinas cuando no están disponibles drivers directos puros Java..1.driver puro Java y nativo-protocolo. Esperamos que las alternativas 3 y 4 sean las formas preferidas de acceder a las bases de datos desde JDBC. El protocolo específico usado dependerá del vendedor. 2.2 Tipos de drivers JDBC Los drivers que son susceptibles de clasificarse en una de estas cuatro categorías. o para una aplicación en el servidor escrito en Java en una arquitectura en tres-niveles.driver Java parcialmente Nativo. Este tipo de driver convierte llamadas JDBC en llamadas del API cliente para Oracle. DB2 y otros DBMS. Dado que muchos de estos protocolos son propietarios.

getConnection. La clase DriverManager mantiene una lista de clases Driver registradas y cuando se llama al método getConnection. El método connect de Driver usa esta URL para establecer la conexión. referida como la capa de gestión JDBC. Una URL (Uniform Resource Locator) da información para localizar un recurso en Internet.2. Connection con = DriverManager.2 Uso general de URL’s Dado que URL‟s causan a menudo cierta confusión. Un usuario puede evitar la capa de gestión de JDBC y llamar a los métodos de Driver directamente. Este método toma una cadena que contiene una URL. Normalmente. Una sesión de conexión incluye las sentencias SQL que se ejecutan y los resultados que son devueltos después de la conexión. o puede tener varias conexiones con varias bases de datos diferentes. 2.1 Vista Preliminar Un objeto Connection representa una conexión con una base de datos. Una única aplicación puede tener una o más conexiones con una única base de datos. 235 .1. "12Java"). intenta localizar un driver que pueda conectar con la base de datos representada por la URL. es mucho más fácil dejar que la clase DriverManager maneje la apertura de la conexión. String url = "jdbc:odbc:wombat".1. 2. daremos primero una breve explicación de URL en general y luego entraremos en una discusión sobre URL‟s de JDBC.1 Apertura de una conexión La forma estándar de establecer una conexión a la base de datos es mediante la llamada al método DriverManager. se chequea con cada driver de la lista hasta que encuentra uno que pueda conectar con la base de datos especificada en la URL. Puede pensarse en ella como una dirección. La clase DriverManager. Esto puede ser útil en el caso raro que dos drivers puedan conectar con la base de datos y el usuario quiera seleccionar uno explícitamente. CONEXIÓN 2. de cualquier modo.getConnection(url. "oboy". El siguiente código muestra como ejemplo una conexión a la base de datos localizada en la URL “jdbc:odbc:wombat” con un user ID de “oboy” y password “12java”.

com 2. da información sobre donde se encuentra la fuente de los datos. Esto quiere decir que la URL JDBC puede referirse a una base de datos lógica o un host lógico que se traduce dinámicamente al nombre actual por el sistema de nombramiento de la red.com/products/jdk/CurrentRelease file:/home/haroldw/docs/books/tutorial/summary. el siguiente es la URL para la home page de JavaSoft.com/docs/JDK-1_apidocs. permite que las URL contengan valores de atributos (pero no los requieren).1. Esta URL identifica solo al host: http://java. Segundo. permiten a diferentes drivers usar diferentes esquemas para nombrar las bases de datos. todo después de los dos puntos. Dado que los JDBC URL se usan con varios tipos de drivers. para un applet que quiera hablar con una base de datos dada el abrir la conexión sin necesitar que el usuario realice ninguna tarea de administración de sistemas. 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. Los desarrolladores de drivers son los que determinan actualmente que JDBC URL identifica su driver particular. por ejemplo.3 JDBC y URL’s Una URL JDBC suministra una forma de identificar una base de datos para que el driver apropiado pueda reconocerla y establecer la conexión con ella. por ejemplo. EL subprotocolo odbc. Si el protocolo es file. las URL‟s JDBC permiten a los desarrolladores de drivers codificar toda la información de la conexión dentro de ella. Por ejemplo. el resto de la URL identifica el host y puede opcionalmente dar un path más específico al sitio.sun.sun.html El resto de la URL. 236 .zip http://java. ellos simplemente usan la URL suministrada con el driver que usan. Esto hace posible. Primero. El rol de JDBC es recomendar algunas convenciones a los fabricantes de drivers para su uso. Los usuarios no tienen por que preocuparse sobre como se forma una URL JDBC. Tercero. las convenciones son necesariamente muy flexibles. el resto de la URL es el path al fichero.La primera parte de una URL especifica el protocolo usado para acceder a la información y va siempre seguida por dos puntos. Algunos protocolos comunes son ftp. Para los protocolos ftp y http. las URL‟s JDBC permiten un nivel de indirección. que especifica “file transfer protocol” y http que especifica “hypertext transfer protocol”.

podría tener una URL como : jdbc:dcenaming:accounts-payable En este ejemplo. y no hay restricción acerca de cual usar. la dirección de red debería incluirse en la URL JDBC como parte del 237 . Un ejemplo sobresaliente de un subprotocolo es “odbc”.una forma de identificar la base de datos. el subprotocolo es “odbc” y el subnombre “fred” es el nombre de la fuente de datos ODBC local. la URL a usar podría ser algo así como lo siguiente: jdbc:odbc:fred En este ejemplo. que puede estar soportado por uno o más drivers. Si la base de datos va a ser accesible a través de Internet. La sintaxis para las URL‟s JDBC que se muestra a continuación tiene tres partes separadas por dos puntos: jdbc:<subprotocol>:<subname> Las tres partes se descomponen como sigue: 1 2 jdbc – el protocolo. Una base de datos en un servidor remoto requiere más información. Por ejemplo para acceder a la base de datos a través del puente JDBC-ODBC. El punto de un subnombre es para dar información suficiente para localizar la base de datos. 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.Esto permite a los administradores de sistemas evitar dar especificaciones de sus hosts como parte del nombre JDBC. por ejemplo. 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 actúal). 3 <subname> . NIS y DCE). Por tanto. para el ejemplo. Hay una variedad de servicios de nomenclatura de red diferentes (tales como DNS. el servicio de nombre puede ser el subprotocolo.<subprotocol> . que ha sido reservado para URL‟s que especifican nombres de fuentes de datos estilo ODBC. El subnombre puede variar dependiendo del subprotocolo. En el ejemplo anterior “fred” es suficiente porque ODBC suministra la información restante.el nombre del driver o el nombre del mecanismo de conectividad con la base de datos. y puede tener un subnombre con cualquier sintaxis interna que el fabricante del driver haya escogido. El protocolo en una URL JDBC es siempre jdbc .

Si fuera.subnombre y debería seguir a la convención estándar de nomenclatura de URL. 2.5 Registro de subprotocolos Un desarrollador de drivers puede reservar un nombre para usar como el subprotocolo en una URL JDBC. Ha sido reservado para URL‟s que especifican el estilo ODBC de nombres de fuentes de datos y que tiene la característica de permitir especificar cualquier número de valores de atributos después del subnombre (el nombre de la fuente de datos) La sintaxis completa para el protocolo “odbc” es: jdbc:odbc:<data-source-name>[.4 El subprotocolo “odbc” El subprotocolo “odbc” es un caso especial.<attribute-name>=<attribute-value>]* Todos los siguientes son nombres válidos jdbc:odbc jdbc:odbc:qeor7 jdbc:odbc:wombat jdbc:odbc:wombat.1. Por ejemplo. por poner otro ejemplo. //hostname:port/subsubname Suponiendo que “dbnet” es un protocolo para conectar a un host.1. 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. la URL JDBC debería parecerse a algo como: jdbc:dbnet://wombat:356/fred 2. se usa para pasar sentencias SQL a la base de datos subyacente. Por ejemplo odbc está reservado para el puente JDBC-ODBC.UID=kgh.ExtensionCase=LOWER jdbc:odbc:qeora.CacheSize=20. Cuando la clase DriverManager presenta este nombre a su lista de drivers registrados. permitiendo el uso de sentencias específicas de la base de datos o incluso sentencias no SQL. que el usuario sea responsable de asegurarse que la base de datos subyacente sea capaz de procesar las sentencias SQL que le están siendo enviadas y soportar las consecuencias si no es así.6 Envío de Sentencias SQL Una vez que la conexión se haya establecido. Se requiere de cualquier modo. el driver para el que este nombre está reservado debería reconocerlo y establecer una conexión a la base de datos que lo identifica. JDBC no pone ninguna restricción sobre los tipos de sentencias que pueden enviarse: esto da un alto grado de flexibilidad.PWD=fooey 2. JDBC requiere que un driver cumpla al menos ANSI SQL-2 Entry 238 .1. una aplicación que intenta enviar una llamada a un procedimiento almacenado a una DBMS que no soporta procedimientos almacenados no tendrá éxito y generará una excepción.

El método prepareStatement se usa para:   Sentencias SQL con uno ó más parámetros IN Sentencias SQL simples que se ejecutan frecuentemente El método prepareCall se usa para:  Llamar a procedimientos almacenados.1. Cuando se llama al método commit o rollback .Statement – creada por el método createStatement. Estas clases y métodos son los siguientes: 1. JDBC suministra tres clases para el envío de sentencias SQL y tres métodos en la interfaz Connection para crear instancias de estas tres clases. Esto significa que los usuarios pueden contar al menos con este nivel de funcionalidad.7 Transacciones Una transacción consiste en una o más sentencias que han sido ejecutadas. Un objeto PreparedStatement es potencialmente más eficiente que un objeto Statement porque este ha sido precompilado y almacenado para su uso futuro. 239 .PreparedStatement – creada por el método prepareStatement. Instancias de PreparedStatement extienden Statement y por tanto heredan los métodos de Statement.. o bien se ha hecho commit o bien roll-back. y añade métodos para el manejo de los parámetros OUT e INOUT. Un objeto CallableStatement hereda métodos para el manejo de los parámetros IN de PreparedStatement.. algo parecido a una función . los cuales son enviados a la base de datos cuando se procesa la sentencia SQL.CallableStatement – creado por el método prepareCall. Los objetos CallableStatement se usan para ejecutar procedimientos almacenados SQL – un grupo de sentencias SQL que son llamados mediante un nombre.. Un objeto Statement se usa para enviar sentencias SQL simples 2.. PreparedStatement tiene un grupo de métodos que fijan los valores de los parámetros IN. 2.Level para ser designado JDBC COMPLIANT. la transacción actúal finaliza y comienza otra. El método createStatement se usa para:  Sentencias SQL simples (sin parámetros).Un objeto PreparedStatement se usa para sentencias SQL que toman uno o más parámetros como argumentos de entrada (parámetros IN). completas y. La lista siguiente da una forma rápida de determinar que método Connection es el apropiado para crear los diferentes tipos de sentencias SQL. 3.

A veces un usuario no quiere que tenga efecto un determinado cambio a menos que se efectué otro. Si el modo auto-commit es desactivado. Por ejemplo. el nivel de transacción más alto es el más lento en la ejecución de la aplicación.8 Niveles de aislamiento de transacciones Si un DBMS soporta el proceso de transacciones. se llama al método rollback.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED). un driver JDBC-compliant debe soportar transacciones. ¿Debería permitirse. la transacción no terminará hasta que se llame al método commit o al método rollback explícitamente. Si ambas actualizaciones tienen éxito se llama al método commit haciendo estos cambios permanentes. cada sentencia es „commitada‟ individualmente. DatabaseMetaData suministra información que describe el nivel de transacción soportado por el DBMS. En este segundo caso. ¿que ocurrirá cuando una transacción cambia un valor y una segunda transacción lee el valor antes de que el cambio haya sido „commitado‟ o descartado?. todas las sentencias de la transacción son “commitadas‟ o deshechas en grupo.1. Normalmente. El método commit hace permanente cualquier cambio que una sentencia SQL realiza en la base de datos.Una conexión nueva se abre por defecto en modo auto-commit. En este caso. El usuario puede especificar un nivel de aislamiento para indicar que nivel de precaución debería ejercitar el DBMS para la resolución de estos conflictos. si uno o los dos fallan. por lo tanto incluirá todas las sentencias que han sido ejecutadas desde la última invocación a uno de los métodos commit o rollback. 2. La interfase Connection define cinco niveles de aislamiento con el nivel más bajo que especifica que no soporte transacciones hasta el más alto que especifica que mientras una transacción esté abierta ninguna otra transacción puede realizar cambios en los datos leídos por esa transacción. Muchos drivers JDBC soportan transacciones. De hecho. por tanto una transacción se compone de una única sentencia. el que más cuidado toma para evitar conflictos. Esto puede hacerse desactivando el modo auto-commit y agrupando ambas actualizaciones en una transacción. El nivel de aislamiento más alto. y libera cualquier bloqueo mantenido por la transacción.(debido a que 240 . El método rollback descarta estos cambios. restaurando los valores existentes l inicio de la transacción. tendrá que manejar de alguna forma los potenciales conflictos que puedan surgir cuando dos transacciones están operando sobre una base de datos concurrentemente. y esto significa que cuando se completa se llama automáticamente al método commit. Un usuario JDBC puede instruir a la DBMS para que un valor que ha sido leído antes del „commit‟ (“dirty reads”) con el siguiente código donde con es el objeto de la actual conexión: con. dado que el valor cambiado leído por la segunda transacción será invalido si la primera transacción ha hecho rollback?.

La clase DriverManager mantiene una lista de clases disponibles que han sido registrados mediante el método DriverManager. El desarrollador debe balancear la necesidad de rendimiento con la necesidad de la consistencia de los datos al tomar la decisión del nivel de aislamiento a usar. Para aplicaciones simples.1 Mantenimiento de la lista de drivers disponibles. 3. Un usuario puede llamar al método setIsolationLevel para cambiar el nivel de aislamiento de la transacción. es necesario fijar este antes de la transacción comience y volverlo a situar en su valor anterior una vez que la transacción haya terminado. Como su nombre indica. LA CLASE DriverManager 3. provocando que los cambios hasta ese punto se hagan permanentes en la base de datos. y trabaja como intermediaria entre el usuario y los drivers. este método establece una conexión con la base de datos. Además el usuario normalmente no debería llamar a DriverManager. JDBC permite al usuario llamar a los métodos de DriverManager getDriver. Guarda la lista de los drivers que están disponibles y establece la conexión entre la base de datos y el driver apropiado.registerDriver.1. el único método en esta clase que necesita un programador general para su uso directamente es DriverManager. getDrivers y registerDriver así como al método de Driver connect.1 Vista preliminar La clase DriverManager implementa la capa de gestión de JDBC. pero normalmente por defecto es el de la DBMS subyacente. Todas las clases Driver deben escribirse con una sección estática que cree una instancia de la clase y luego la registre en la clase DriverManager cuando se cargue. Para cambiar el nivel de aislamiento solo para una transacción. y este nuevo nivel estará efectivo durante la sesión de conexión. No se recomienda cambiar el nivel de aislamiento de transacción en medio de una puesto que lanzará una llamada inmediata al método commit. 3.getConnection. debería llamarse 241 . su nivel de aislamiento depende del driver.registerDriver directamente. Además la clase DriverManager se ocupa de cosas cómo gestionar los límites de tiempo de „login‟ en el driver y de la salida de los mensajes de traza y log. el nivel de aislamiento que pueda soportarse depende de las posibilidades de la base de datos subyacente. Cuando se crea un nuevo objeto Connection. Por supuesto.se incrementan los bloqueos y se disminuye la concurrencia de los usuarios). pero en la mayoría de los casos es preferible dejar que la clase DriverManager gestione los detalles al establecer la conexión.

forName para cargar explicitamente cada driver.System.Esta es una lista de nombres de clases de drivers. Cuando la clase DriverManager se inicializa.sql.db. Notese que en esta segunda manera de cargar los drivers es necesario una preparación del entorno que es persistente. El siguiente código ilutsra como un programador debería introducir estas tres clases en ~/. entonces este estará en la lista de drivers disponibles para efectuar la conexión. Entonces cuando la clase DriverManager abre una conexión solo usará los drivers que vienen desde el sistema de ficheros local o desde las mismas clases cargadoras como el código que solicita la conexión.bah. mira en la propiedad jdbc.ourDriver.drivers. Si existe alguna duda sobre esto es preferible y más seguro llamar al método Class.registerDriver.drivers=foo.automáticamente por el driver cuando esté se carga. Por razones de seguridad. 2 Mediante la adición del driver a la propiedad jdbc.hotjava/properties jdbc. Una clase Driver se carga.lang. la clase DriverManager intenta cargarlos. Si acme. esta forma de cargar drivers es la recomendada.forName("acme. Este es también el método 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.test.Driver: Class.Driver se ha escrito para que al cargar produzca una instancia y llame al método DriverManager. separadas por dos puntos. que es la que carga la clase DriverManager. 242 . esto debería hacerse automáticamente al cargar la clase. y si el usuario ha introducido uno o más drivers.Driver:wombat.db. El siguiente código carga la clase acme. la capa de gestión de JDBC guardará traza de que clases de cargadores provee cada driver.. En ambos casos.Driver").drivers de java.drivers. es responsabilidad de la clase Driver recién cargada registrarse a si misma mediante una llamada a DriverManager.Driver:bad.forName. de dos formas diferentes: 1 Mediante una llamada al método Class. La primera llamada a el método DriverManager hará que estos drivers se carguen automáticamente. Este carga explícitamente la clase driver.db. y por tanto se registra.registerDriver con esta instancia como argumento (es lo que debería hacer). Dado que no depende de ningún „setup‟ externo. Como se ha mencionado anteriormente.

4. En tales casos.1. DriverManager.forName("sun. Actualmente hay tres tipos de objetos Statement.JdbcOdbcDriver"). Una primera ojeda puede parecer insuficiente.1 Vista Preliminar Un objeto Statement se usa para enviar sentencias SQL a la base de datos. //loads the driver String url = "jdbc:odbc:fred". podría ser posible usar un driver puente JDBC-ODBC. El primer driver que reconozca la URL realiza la conexión. 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. LA CLASE Statement 4. o un driver JDBC de protocolo genérico de red.3. pero son necesarias solo unas pocas llamadas a procedimientos y comparaciones de cadena por conexión puesto que no es probable que haya docenas de drivers se carguen concurrentemente. se está en condiciones de establecer la conexión con la base de datos.getConnection(url. "userID". pasándole como argumento la URL que el usuario ha pasado originalmente al método DriverManager. A veces puede darse el caso de que más de un driver JDBC pueda establecer la conexión para una URL dada. Primero DriverManager intenta usar cada driver en el orden en que ha sido registrado ( los drivers listados en la propiedad jdbc. Testea los drivers mediante la llamada al método Driver.2 Establecer una conexión Una vez que la clase Driver ha sido cargada y registrada con la clase DriverManager. 243 .getConnection. La solicitud de la conexión se realiza mediante una llamada al método DriverManager. y DriverManager testea los drivers regsitrados para ver si puede establecer la conexión. Por ejemplo.jdbc. El siguiente código es un ejemplo de todo lo necesario normalmente para establecer una conexión con un driver puente JDBC-ODBC: Class. o un driver suministrado por el vendedor. Saltará cualquier driver con código „untrusted‟. "passwd").odbc. cuando conectamos con una base de datos remota.drivers son siempre los registrados primero). a menos que se cargue desde el mismo código fuente que el código que intenta abrir la conexión.getConnection.connect cada uno por turno. todos los cuales actúan como contenedores para la ejecución de sentencias en una conexión dada: Statement.

El efecto de una sentencia INSERT. El método a usar esta determinado por el producto de la sentencia SQL El método executeQuery esta diseñado para sentencias que producen como resultado un único result set tal como las sentencias SELECT. Para sentencias tales como CREATE TABLE o DROP TABLE.2 Ejecución de sentencias usando objetos Statement. Un objeto Statement se crea mediante el método de Connection createStatement. Connection con = DriverManager. 244 .1 Creación de objetos Statement Una vez establecida la conexión con una base de datos particular. Por ejemplo: ResultSet rs = stmt.PreparedStatement que hereda de Statement y CallableStatement que hereda de PreparedStatement. como podemos ver en el siguiente fragmento de código. UPDATE ó DELETE así como sentencias SQL DDL (Data Definition Language) como CREATE TABLE o DROP TABLE. le valor devuelto por executeUpdate es siempre cero.1. El método executeUpdate se usa para ejecutar sentencias INSERT. esta conexión puede usarse para enviar sentencias SQL. c FROM Table2"). La sentencia SQL que será enviada a la base de datos es alimentada como un argumento a uno de los métodos de ejecución del objeto Statement. 4. 4. "").createStatement(). La interfase PreparedStatement añade métodos para trabajat con los parámetros IN. y la interfase CallableStatement añade métodos para trabajar con parameters OUT.getConnection(url. y un objeto CallableStatement se usa para ejecutar un procedimieno de base de datos almacenado. La interfase Statement suminstra métodos básicos para ejecutar sentencias y devolver resultados. Statement stmt = con. que no operan sobre filas. Estas estàn especializadas para enviar tipos particulares de sentencias SQL. Un objeto PreparedStatement se usa para ejecutar sentencias SQL precompiladas con o sin parámetros IN. Un objeto Statement se usa para ejecutar una sentencia SQL simple sin parámetros. "sunny".1. UPDATE o DELETE es una modificación de una o más columnas en cero o más filas de una tabla. b. El valor devuelto de executeUpdate es un entero que indica el número de filas que han sido afectadas (referido como update count). executeUpdate y execute. executeQuery.executeQuery("SELECT a. La interfase Statement nos suministra tres métodos diferentes para ejecutar sentencias SQL.

Otros tratan el procedimiento entero como una sentencia compuesta. que hereda los métodos de la interfase Statement. se commiten todas juntas. Pare el método executeQuery.El método execute se usa para ejecutar sentencias que devuelven más de un result set. Esto quiere decir que es necesario completar el proceso con el actual objeto Resulset antes de reejecutar una sentencia Statement. Los objetos Statement se cerrarán automáticamente por el colector de basura de Java (garbage collector). más que un update count o una combinación de ambos. cada sentencia individual es commitada. Debe notarse que la interfase PreparedStatement. Los objetos Statement en si mismos no contienen una sentencia SQL. Un sentencia se considera completa cuando ha sido ejecutada y se han devuelto todos los resultados. por tanto debe suministrarse como un argumento a los métodos Statement. Esto 245 .. En el primer caso. 4.1. tiene sus propias versiones de los métodos executeQuery. que devuelve un único result set. 4. la sentencia se completa cuando todas las filas del objeto ResultSet se han devuelto. En el segundo. No obstante se recomienda como una buena práctica de programación que se cierren explicitamente cuando no sean ya necesarios. Algunos DBMS tratan cada sentencia en un procedimiento almacenado como sentencias separadas. Los objetos CallableStatement heredan las formas de estos métodos de PreparedStatement. Usar un parametro de query con las versiones de los métodos de PreparedStatement o CallableStatement producirái una SQLException. Todos los métodos que ejecutan sentencias cierran los objetos Resultset abiertos como resultado de las llamadas a Statement. Esta diferencia se convierte en importante cuando está activo el modo auto-commit porque afecta cuando se llama al método commit. un sentencia se completa cuando se ejecuta. las sentencias ejecutadas son „comitadas‟ o rechazadas cuando se completan. no se completa hasta que los result sets o update counts que se generan han sido devueltos. Los objetos PreparedStatement no suministran una sentencia SQL como argumento a estos métodos puesto que ya tienen la sentencia precompilada. Como es esta una característica avanzada que muchos programadores no necesitarñan nunca se verá en su propia sección.1.3 Realización de Statement Cuando una conexión está en modo auto-commit.4 Cerrar objetos Statement. de cualquier modo. En los raros casos en que se llama al método execute. executeUpdate y execute.execute. Para el método executeUpdate.

executeQuery("SELECT COF_NAME. La segunda columna de cada fila almacena un valor del tipo FLOAT de SQL. cubierto en la siguiente sección. y el método para recuperar valores de ese tipo es getFloat. contiene las filas de cafés y sus precios mostrados en el juego de resultados de la página anterior. JDBC devuelve los resultados en un objeto ResultSet. Sin embargo. Como el cursor inicialmente se posiciona justo encima de la primera fila de un objeto ResultSet. ResultSet rs = stmt. Para acceder a los nombres y los precios. pero requiere permisos especiales y normalmente lo hace un administrador de bases de datos). El método next mueve algo llamado cursor a la siguiente fila y hace que esa fila (llamada fila actual) sea con la que podamos operar. El siguiente código presenta el objeto ResultSet: rs y le asigna el resultado de una consulta anterior.ODBC. primero debemos llamar al método next para mover el cursor a la primera fila y convertirla en la fila actual. Recuperar Valores desde una Hoja de Resultados Ahora veremos como enviar la sentencia SELECT de la página anterior desde un programa escrito en Java y como obtener los resultados que hemos mostrado. hacia posiciones específicas y a posiciones relativas a la fila actual además de mover el cursor hacia adelante. Por ejemplo.libera recursos DBMS inmediatamente y ayuda a evitar potenciales problemas de memoria. Después. Este código ha sido probado en la mayoría de los controladores de base de datos. y luego. se puede mover el cursor hacia atrás. podríamos encontrar algunos problemas de compatibilidad su utilizamos antiguos drivers ODB con el puente JDBC. (crear una base de datos no es nada díficil. ya que JDBC puede enviar codigo SQL a nuestro controlador. la primera columna de cada fila de rs es COF_NAME. que almacena un valor del tipo VARCHAR de SQL. iremos a la fila y recuperaremos los valores de acuerdo con sus tipos. El siguiente 246 .0 CONEXIONES A BASES DE DATOS CON JAVA Seleccionar una Base de Datos A lo largo de la sección asumiremos que la base de datos COFFEES ya existe. El método para recuperar un valor VARCHAR es getString. Utilizar el Método next La variable rs. PRICE FROM COFFEES"). que es un ejemplar de ResultSet. por eso necesitamos declarar un ejemplar de la clase ResultSet para contener los resultados. veremos lo sencillo que es utilizar JDBC para pasar esas sentencias SQL a nuestro controlador de bases de datos y procesar los resultados devueltos. Observa que con el JDBC 2.0. Para ser un poco manejable se tendrá un número pequeño de tablas. Utilizar los métodos getXXX Los métodos getXXX del tipo apropiado se utilizan para recuperar el valor de cada columna. Sucesivas invocaciones del método next moverán el cursor de línea en línea de arriba a abajo. 5. demostraremos algún código SQL. Primero veremos como abrir una conexión con nuestro controlador de base de datos.

lo convierte a un float de Java y lo asigna a n. La segunda línea de código obtiene el valor de la segunda columna de la fila actual de rs. que es un FLOAT de SQL. Recuerda que el número de columna se refiere al número de columna en la hoja de resultados no en la tabla original. PRICE FROM COFFEES". Colombian 7.getString("COF_NAME"). Observa que utilizamos la variable s en la expresión println mostrada arriba. de esta forma: println(s + " " + n) La situación es similar con el método getFloat excepto en que recupera el valor almacenado en la columna PRICE. la siguiente fila se convierte en la actual.next()) { String s = rs. convirtiéndolo a un objeto String de Java y asignándolo a s. como se ha hecho arriba. con un 1 significando la primera columna.99 Colombian_Decaf 8. float n = rs.println(s + " " + n). String s = rs. Float n = rs. System.99 Veamos cómo funcionan los métodos getXXX examinando las dos sentencias getXXX de este código. y el bucle continúa hasta que no haya más filas en rs. String query = "SELECT COF_NAME.getString("COF_NAME"). 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.out. En suma.99 French_Roast_Decaf 9.getFloat("PRICE"). un 2 para la segunda.código accede a los valores almacenados en la fila actual de rs e imprime una línea con el nombre seguido por tres espacios y el precio. Si utilizáramos el número de columna en vez del nombre de columna el código anterior se podría parecer a esto. JDBC ofrece dos formas para identificar la columna de la que un método getXXX obtiene un valor. Una forma es dar el nombre de la columna. El método getString es invocado sobre el objeto ResultSet: rs. y lo convierte a un float de Java antes de asignarlo a la variable n. while (rs.executeQuery(query). JDBC permite utilizar tanto el nombre cómo el número de la columna como argumento a un método getXXX. Cada vez que se llama al método next. Primero examinaremos getString. 247 . por eso getString recuperará (obtendrá) el valor almacenado en la columna COF_NAME de la fila actual de rs. Utilizar el número de columna es un poco más eficiente.getString(1). ResultSet rs = stmt. etc.99 French_Roast 8. La primera línea de código obtiene el valor de la primera columna de la fila actual de rs (columna COF_NAME). y hay algunos casos donde es necesario utilizarlo. } La salida se parecerá a esto. La segunda forma es dar el índice de la columna (el número de columna).getFloat(2). String s = rs.99 Espresso 9.

y el valor tendrá que ser convertido de nuevo a número antes de poder operar con él. Métodos para Recuperar Tipos SQL muestra qué métodos pueden utilizarse legalmente para recuperar tipos SQL.JDBC permite muchas lateralidades para utilizar los métodos getXXX para obtener diferentes tipos de datos SQL. no se pueden recuperar los nuevos tipos de datoas del SQL3. Observa que esta tabla utiliza el término "JDBC type" en lugar de "SQL type. y más importante. La sentencia SQL para actualizar una columna se podría parecer a esto. DATE. Explicaremos el SQL3 más adelante). VARBINARY. si se está utilizando para recuperar un tipo numérico. o TIMESTAMP de SQL.getXXX para Recuperar tipos JDBC Una "x" indica que el método getXXX se puede utilizar legalmente para recuperar el tipo JDBC dado. LONGVARBINARY.99 8. esto es. pero tiene sus limitaciones. Utilizar los métodos de ResultSet. String updateString = "UPDATE COFFEES " + "SET SALES = 75 " + "WHERE COF_NAME LIKE 'Colombian'". si el tipo SQL es VARCHAR. Utilizar el método getString Aunque el metodo getString está recomendado para recuperar tipos CHAR y VARCHAR de SQL. 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. Se recomienda utilizar el método getInt sólo para recuperar INTEGER de SQL. sin embargo.sql. Por ejemplo. el método getInt puede ser utilizado para recuperar cualquier tipo numérico de caracteres. Actualizar Tablas Supongamos que después de una primera semana exitosa.99 SALES 75 0 0 0 0 TOTAL 0 0 0 0 0 . TIME." Ambos términos se refieren a los tipos genéricos de SQL definidos en java. qué métodos están recomendados para recuperar los distintos tipos SQL.99 9. Una "X" indica que el método getXXX está recomendado para recuperar el tipo JDBC dado.Types.executeUpdate(updateString). no puede utilizarse con los tipos BINARY. este código JDBC ejecuta la sentencia SQL contenida en updateString. stmt.99 8. getString lo convertirá en un String de Java. (Sin embargo. y ambos son intercambiables.99 9. Uitlizando el objeto stmt. Obtener un valor con getString puede ser muy útil. Los datos recuperados serán convertidos a un int. es posible recuperar cualquier tipo básico SQL con él. JDBC intentará convertirlo en un entero. Por ejemplo. el propietario de "The Coffee Break" quiere actualizar la columna SALES de la tabla COFFEES introduciendo el número de libras vendidas de cada tipo de café.

System.getInt("SALES").") } Observa que en este ejemplo. sólo se llama una vez a next. rs. si sucediera que existiera una línea. y luego imprimiremos el número de libras vendidas hasta la fecha. Ahora actualizaremos la columna TOTAL añadiendo la cantidad vendida durante la semana a la cantidad total existente.next()) { String s = rs. necesitamos utilizar el método next para acceder a ella. while (rs.out.executeUpdate(updateString). Un objeto ResultSet se crea con un cursor apuntando por encima de la primera fila. La primera llamada al método next posiciona el cursor en la primera fila (y en este caso.getInt(2). Por ejemplo. while (rs. String query = "SELECT COF_NAME. ResultSet rs = stmt.executeQuery(query).") } Esto imprimira lo siguiente.out. String query = "SELECT COF_NAME. sería posible escribir el código sin un bucle while.println(n + " pounds of " + s + " sold this week. stmt.getString("COF_NAME").getString(1). int n = rs. int n = rs. Cómo la claúsula WHERE límita la selección a una sóla línea. y el índice 2 a getInt (la segunda columna de la hoja de resultados es TOTAL).out.getInt(2).") Aunque hay una sóla línea en la hoja de resultados.getString(1).Observa que todavía no hemos actualizado la columna 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. 75 pounds of Colombian sold this week. String updateString = "UPDATE COFFEES " + "SET TOTAL = TOTAL + 75 " + "WHERE COF_NAME LIKE 'Colombian'". sólo hay una línea en la ResultSet: rs y una línea en la salida.next()) { String s = rs. SALES FROM COFFEES " + "WHERE COF_NAME LIKE 'Colombian'".println(n + " pounds of " + s + " sold to date.println(n + " pounds of " + s + " sold this week. System. recuperando los valores de las columnas COF_NAME y SALES. nunca se accedería a ella.next(). ResultSet rs = stmt. TOTAL FROM COFFEES " + "WHERE COF_NAME LIKE 'Colombian'". System. y por eso tiene valor 0. utilizamos el índice de columna en vez del nombre de columna. Ahora seleccionaremos la fila que hemos actualizado. e imprimiendo esos valores. En este código.executeQuery(query). TOTAL es la quinta columna en la tabla COFFEES pero es la segunda columna en la hoja de resultados generada por la petición del ejemplo anterior. suministrando el índice 1 a getString (la primera columna de la hoja de resultados es COF_NAME). Por lo tanto. 249 . int n = rs. String s = rs. la única) de rs.

Crear un Objeto PreparedStatement Al igual que los objetos Statement. que ya conocemos. Statement. antes de ejecutar un objeto PreparedStatement. Esto significa que cuando se ejecuta la PreparedStatement. La caracterísitca principal de un objeto PreparedStatement es que. Veremos un ejemplo de esto en las página siguientes. Aunque los objetos PreparedStatement se pueden utilizar con sentencias SQL sin parámetros. Este tipo especial de sentencias se deriva de una clase más general. podemos llamar al método setString. En general. Suministrar Valores para los Parámetros de un PreparedStatement Necesitamos suministrar los valores que se utilizarán en los luegares donde están las marcas de interrogación. donde será compilado. probablemente nosotros utilizaremos más frecuentemente sentencias con parámetros. Si el valor que queremos sustituir por una marca de interrogación es un int de Java. el controlador de base de datos puede ejecutarla sin tener que compilarla primero. Podemos hacer esto llamado a uno de los métodos setXXX definidos en la clase PreparedStatement.setInt(1. en la mayoría de los casos. Utilizando nuestra conexión con abierta en ejemplos anteriores. updateSales. "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?". podríamos escribir lo siguiente para crear un objeto PreparedStatement que tome dos parámetros de entrada. si hay alguno. esta sentencia SQL se enviará al controlador de la base de datos inmediatamente. Si el valor que queremos sustituir es un String de Java. creamos un objeto PreparedStatement con un objeto Connection. podemos llamar al método setInt. etc. hay un método setXXX para cada tipo Java. el objeto PreparedStatement no sólo contiene una sentencia SQL. con un valor de 75. Como resultado. PreparedStatement updateSales = con. la siguiente línea de código selecciona la primera marca de interrogación para un int de Java. La ventajA de utilizar sentencias SQL que utilizan parámetros es que podemos utilizar la misma sentencia y suministrar distintos valores cada vez que la ejecutemos. que también ha sido. La variable updateSales contiene la sentencia SQL. 75). enviada al controlador de la base de datos. sino una sentencia SQL que ha sido precompilada. reduciremos el tiempo de ejecución si utilizamos un objeto PreparedStatement. y ha sido precompilado.Utilizar Sentencias Preparadas Algunas veces es más conveniente o eficiente utilizar objetos PreparedStatement para enviar sentencias SQL a la base de datos. se le entrega una sentencia SQL cuando se crea. en su lugar. al contrario que un objeto Statement. La ventaja de esto es que en la mayoría de los casos. Cuándo utilizar un Objeto PreparedStatement Si queremos ejecutar muchas veces un objeto Statement.prepareStatement( "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?"). Utilizando el objeto updateSales del ejemplo anterior. 250 .

75).executeUpdate(updateString). Código 1. // changes SALES column of French Roast row to 100 updateSales.executeUpdate().setInt(1. Si actualizáramos la columna SALES sólo una o dos veces. Esto es cierto porque updateSales ya contiene la sentencia SQL a ejecutar. el primer argumento de un método setXXX indica la marca de interrogación que queremos seleccionar. Después de que estos valores hayan sido asignados para sus dos parámetros.setString(2. updateSales. tuvieramos que actualizarla frecuentemente. que no se suministran argumentos a executeUpdate cuando se utiliza para ejecutar updateSales.prepareStatement( "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? "). updateSales. Observa. los dos fragmentos de código siguientes consiguen la misma cosa. dejando el otro igual. stmt. no sería necesario utilizar una sentencia SQL con parámetros. Si por otro lado. el siguiente fragmento de código reutiliza una sentencia prepared después de resetar el valor de uno de sus parámetros. la sentencia SQL de updateSales será equivalente a la sentencia SQL que hay en string updateString que utilizando en el ejemplo anterior. "Espresso"). and the second parameter was reset // to "Espresso") 251 . updateSales. updateSales. updateSales. Veremos este ejemplo más adelante en esta sección.setString(2. updateSales.setString(2. Utilizamos el método executeUpdate para ejecutar ambas sentencias stmt updateSales. Utilizando el objeto PreparedStatement: updateSales. y el segundo argumento el valor que queremos ponerle. sin embargo. podría ser más fácil utilizar un objeto PreparedStatement. Una vez que a un parámetro se ha asignado un valor. El siguiente ejemplo selecciona la segunda marca de interrogación con el string "Colombian".setInt(1. Código 2.Cómo podríamos asumir a partir de este ejemplo.setString(2.executeUpdate(). "French_Roast").executeUpdate(). String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE 'Colombian'". Mirando esto ejemplos podríamos preguntarnos por qué utilizar un objeto PreparedStatement con parámetros en vez de una simple sentencia. updateSales. // changes SALES column of Espresso row to 100 (the first // parameter stayed 100. especialmente en situaciones cuando la utilizamos con un bucle while para seleccionar un parámetro a una sucesión de valores. Por lo tanto. updateSales. PreparedStatement updateSales = con. "Colombian"). "Colombian"). 100). el valor permanece hasta que lo resetee otro valor o se llame al método clearParameters. ya que la sentencia simple implica menos pasos.

String [] coffees = {"Colombian".executeUpdate(). coffees[i]).setString(2. "Espresso". updateSales. updateSales. i++) { updateSales. El siguiente fragmento de código demuestra la utilización de un bucle for para asignar los parámetros en un objeto PreparedStatement: updateSales.int [] salesForWeek = {175. int n = executeUpdate(createTableCoffees). etc.prepareStatement(updateString). Este fragmento de código demuestra la actualización de la columna SALES para todos los cafés de la tabla COFFEES PreparedStatement updateSales. en el siguiente fragmento de código. la segunda cantidad de salesForWeek (150) se aplica al segundo nombre de café en coffees ("French_Roast"). updateSales. La actualización afecta sólo a una línea de la tabla. "Espresso"). el siguiente código muestra el valor de retorno de executeUpdate asignado a la variable n. devuelve el int: 0. por eso no necesitan cambiarse. Valores de retorno del método executeUpdate Siempre que executeQuery devuelve un objeto ResultSet que contiene los resultados de una petición al controlador de la base datos. los valores probablemente serían introducidos por el usuario en vez de desde un array inicializado). for(int i = 0. int n = updateSales. Todo lo que tiene que haces es introducir las nuevas cantidades en el orden apropiado en el array salesForWeek. n tendrá el valor 0. Cuando el método executeUpdate es utilizado para ejecutar una sentecia DDL.Utilizar una Bucle para asignar Valores Normalmente se codifica más sencillo utilizando un bucle for o while para asignar valores de los parámetros de entrada.setString(2. por eso n es igual a 1. } Cuando el propietario quiera actualizar las ventas de la semana siguiente. Estas cantidades corresponden con los nombres de los cafés listados en el array coffees.setInt(1. como la creación de una tabla. updateSales = con. "Colombian_Decaf". puede utilizar el mismo código como una plantilla.executeUpdate(). updateSales. 60. salesForWeek[i]).length. "French_Roast_Decaf"}. Los nombres de cafés del array coffees permanecen constantes. int len = coffees. "French_Roast". 155. String updateString = "update COFFEES " + "set SALES = ? where COF_NAME like ?".setInt(1. 90}. Por ejemplo. 50). 150. por eso la primera cantidad de salesForWeek (175) se aplica al primer nombre de café de coffees ("Colombian"). (En una aplicación real. // n = 0 252 . el valor devuelto por executeUpdate es un int que indica cuántas líneas de la tabla fueron actualizadas. i < len. que ejecuta la sentencia DDL utilizada pra crear la tabla COFFEES. // 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. Consecuentemente. El array salesForWeek contiene las cantidades vendidas semanalmente.

System. Como ambas tablas tienen la columna SUP_ID.executeUpdate("insert into SUPPLIERS values (101.SUP_ID" para indicar que queremos referirnos a la columna SUP_ID de la tabla COFFEES. El siguiente código crea la tabla SUPPLIERS. "COFFEES. El siguiente código selecciona la tabla y nos permite verla. stmt.'" + "and SUPPLIERS.executeUpdate("Insert into SUPPLIERS values (49." + "'Superior Coffee'. necesitamos crear la tabla SUPPLIERS y rellenarla con valores. Este es el caso en que se necesitan los "joins" (unión). Inc. puede significar dos cosas: (1) la sentencia ejecutada no ha actualizado ninguna fila. Inc. System. Por ejemplo. stmt. ResultSet rs = stmt. stmt. '1 Party Place'. " + "'93966'"). ResultSet rs = stmt.println(" " + coffeeName).Observa que cuando el valor devuelto por executeUpdate sea 0.'. El siguiente código inserta filas para tres suministradores dentro de SUPPLIERS.next()) { String coffeeName = getString("COF_NAME").println("Coffees bought from Acme. " + "'CA'. 'CA'.out. o (2) la sentencia ejecutada fue una sentencia DDL. Los nombres de los suminstradores están en la tabla SUPPLIERS. String query = " SELECT COFFEES. Esto implica información de la tabla COFFEES y también de la que vamos a crear SUPPLIERS. Utilizar Uniones Algunas veces necesitamos utilizar una o más tablas para obtener los datos que queremos. Lo siguiente que necesitamos es la forma de distinguir la columna SUP_ID a la que nos referimos. '100 Coffee Lane'. CITY VARCHAR(20). SUPPLIERS " + "WHERE SUPPLIERS.: "). En nuestro ejemplo.out. que puede ser utilizada para unirlas. } 253 .executeUpdate(createSUPPLIERS). En el siguiente código. 'CA'. '95199'"). podremos proceder con el escenario en que el propietario quería una lista de los cafés comprados a un suministrador particular.SUP_ID = COFFEES. " + "'The High Ground'.executeQuery("select * from SUPPLIERS"). 'Groundsville'.executeUpdate("Insert into SUPPLIERS values (150.. Inc. 'Mendocino'.SUP_NAME LIKE 'Acme. Esto se hace precediendo el nombre de la columna con el nombre de la tabla. las tablas COFFEES y SUPPLIERS tienen la columna SUP_ID. Ahora que tenemos las tablas COFFEES y SUPPLIERS. podemos utilizar esta columna en una unión. " + "'Acme. while (rs. " + "'95460'"). ZIP CHAR(5))". supongamos que el propietario del "The Coffee Break" quiere una lista de los cafés que le compra a Acme. Inc. stmt. 'Meadows'. String createSUPPLIERS = "create table SUPPLIERS " + "(SUP_ID INTEGER. donde stmt es un objeto Statement. " + "STATE CHAR(2). '99 Market Street'.COF_NAME " + "FROM COFFEES. seleccionamos los cafés comprados a Acme. Una unión es una operación de base de datos que relaciona dos o más tablas por medio de los valores que comparten. SUP_NAME VARCHAR(40). " + "STREET VARCHAR(40). Inc.SUP_ID".executeQuery(query). y los nombres de los cafés en la tabla COFFEES. Antes de ir más allá.

. el segundo inserta valores en la tabla e imprime los resultados de una petición. Utilizar el Método main() Si una clase se va a ejecutar. Por eso si tenemos una clase llamada MySQLStatement. Si no incluimos "import java. Todas las clases de nuestros ejemplos utilizan el paquete java. Inc." más el nombre de la clase delante de todos los campos o métodos JDBC que utilicemos cada vez que los utilicemos. El sexto ejemplo de código es una aplicación que demuestra una transación y también muestra como configurar las posiciones de los parámetros en un objeto PreparedStatement utilizando un bucle for. Colombian Colombian_Decaf Crear Aplicaciones JDBC Completas Hasta ahora sólo hemos visto fragmentos de código.sql (el API JDBC).*. La terecera aplicación crea la tabla SUPPLIERS. incluyen algunos elementos del lenguaje Java que no hemos visto en los fragmentos anteriores.java.sql. debe contener un método static public main. 254 . pero al hacerlo el código se hace mucho más conveniente. Coffees bought from Acme.sql.sql serán importadas. Aquí explicaremos estos elementos brevemente. su definición debería estar en un fichero llamado MySQLStatement. Después de haber ejecutado este código. podemos intentar una petición que una las tablas COFFEES y SUPPLIERS. como en el quinto código de ejemplo. cualquier código que querramos ejecutar debe estar dentro de una definición de clase." en nuestro código. El asterisco (*) indica que todas las clases del paquete java. import java.sql. tendríamos que escribir "java. que es donde deben estar para hacer visibles las clases importadas a la clase que está siendo definida. Importar una clase la hace visible y significa que no tendremos que escribir su nombre totalmente cualificado cuando utilicemos un método o un campo de esa clase.java Importar Clases para Hacerlas Visibles Lo primero es importar los paquetes o clases que se van a utilizar en la nueva clase. que se hace visible cuando la siguiente línea de código precede a la definición de clase. Poner Código en una Definición de Clase En el lenguaje Java. Más adelante veremos programas de ejemplo que son aplicaciones completas que podremos ejecutar. El primer código de ejemplo crea la tabla COFFEES.*. Como son aplicaciones completas. y el cuarto la rellena con valores. Este método viene justo después de la línea que declara la clase y llama a los otros métodos de la clase. Cualquier línea que importe clases aparece en la parte superior de los ejemplos de código. Java no requiere que importemos clases o paquetes. Tecleamos la definición de clase en un fichero y a éste le damos el nombre de la clase con la extensión . Observa que también podemos importar clases individuales selectivamente en vez de importar un paquete completo.Esto producirá la siguiente salida. La definición real de la clase sigue a cualquier línea que importe clases.

podemos tener un bloque catch que las imprima. Como no estamos definiendo clases sólo para ser ejecutadas por otras clases sino que queremos ejecutarlas.println("SQLException: " + ex. } try { Class.err. State 1. try { // Aquí va el código que podría generar la excepción. La palabra clave public significa que los miembros de cualquier clase pueden acceder a este método.getMessage()). realmente existen tres componentes.lang.err.forName("myDriverClassName"). Por ejemplo. Primero. Para ver las excepciones. } Si ejecutarámos CreateCOFFEES. El siguiente fragmento de código muestra un bloque catch que se ha completado de dos formas. En el código de ejemplo. imprime las tres partes de un objeto SQLException: el 255 . Utilizar bloques try y catch Algo que también incluyen todas las aplicaciones de ejemplo son los bloques try y catch. lo que es suficiente para la mayoría de las situaciones. Este método lanza una ClassNotFoundException. // Si se genera una excepción. Este es un mecanismo del lenguaje Java para manejar excepciones. El primer bloque try contiene el método Class. obtendríamos un mensaje de error similar a éste.print("ClassNotFoundException: "). Line 1 Este ejemplo ilustra la impresión del componente mensaje de un objeto SQLException. Sin embargo. SQLException: There is already an object named 'COFFEES' in the database. } catch(java.println(e.getMessage()). Java requiere que cuando un método lanza un excepción exista un mecanismo que la maneje. } catch(SQLException ex) { System. por eso el bloque catch del final de la aplicación puede manejar el resto de las excepciones que podrían lanzarse ya que todas serían objetos SQLException. El segundo bloque try contiene métodos JDBC.lang. System. Generalmente un bloque catch capturará la excepción y especificará lo que sucederá (que podría ser no hacer nada). el bloque catch imprimirá // información sobre ella. las aplicaciones de ejemplo de este capítulo incluyen un método main. del paquete java. podemos imprimirlos todos. y para ser completos. por eso el bloque catch que le sigue maneja esa excepción.ClassNotFoundException e) { System.java dos veces.err.forName. los dos bloques catch del siguiente código de ejemplo imprimen un mensaje explicando la excepción. utilizamos dos bloques try y dos bloques catch. todos ellos lanzan SQLException. Severity 16.La palabra clave static indica que este método opera a nivel de clase en vez sobre ejemplares individuales de la clase. Recuperar Excepciones JDBC permite ver los avisos y excepciones generados por nuestro controlador de base de datos y por el compilador Java.

println(""). // Si se genera una excepción. ex = ex. } catch(SQLException ex) { System. Recuperar Avisos Los objetos SQLWarning son una subclase de SQLException que trata los avisos de accesos a bases de datos.Operation invalid at this time El código de error del vendedor es específico de cada driver. System.out. State 1.println("\n--. y un código de error del vendedor (un número que es el código de error del vendedor del driver). La segunda forma del siguiente bloque catch completo obtiene todas las exepciones que podrían haber sido lanzada.println("ErrorCode: " + ex. Cada una de 256 .out. por eso se llama a ex. el SQLState (un string que identifica el error de acuerdo a los convenciones X/Open de SQLState). El objeto SQLException. el bucle while continúa e imprime el mensaje de la siguiente excecpción. Line 1 SQLState: 42501 ErrorCode: 2714 SQLState es un código definido en X/Open y ANSI-92 que identifica la excepción. ex es capturado y se accede a sus tres componentes con los métodos getMessage. System. --. 08001 -.SQLException caught --Message: There is already an object named 'COFFEES' in the database. } } Si hubieramos sustituido el bloque catch anterior en el Código de ejemplo 1 (CreateCoffees) y lo hubieramos ejecutado después de que la tabla COFFEES ya se hubiera creado.println("SQLState: " + ex. System. Aquí podemos ver dos ejemplos de códigos SQLState. y getErrorCode. getSQLState. el SQLState.println("Message: " + ex. Por ejemplo.getNextException(). while (ex != null) { System.getErrorCode ()). un aviso podría hacernos saber que un privilegio que queriamos revocar no ha fue revocado. el bloque catch imprimirá // información sobre ella. Un aviso puede reportarse sobre un objeto Connection. O un aviso podría decirnos que ha ocurrido algún error durante una petición de desconexión. simplemente alertan al usuario de que algo no ha salido como se esperaba.out. Esto continúa hasta que no haya más excepciones. un objeto Statement (incluyendo objetios PreparedStatement y CallableStatement). Si las hay. Los Avisos no detienen la ejecución de una aplicación. Si hay una segunda excepción.out.getNextException para ver si hay más excepciones. obtendríamos la siguiente información. Severity 16.out. como las excepciones. sería encadenada a ex.mensaje (un string que describe el error). por lo que debemos revisar la documentación del driver buscando una lista con el significado de estos códigos de error.SQLException caught ---\n").getMessage ()).getSQLState ()). y el código de error del vendedor.No suitable driver HY011 -. o un objeto ResultSet. try { // Aquí va el código que podría generar la excepción.

getWarnings(). Al ejecutar una sentencia se borran automáticamente los avisos de la sentencia anterior. Statement stmt = con. } } SQLWarning warn = rs. Todos los objetos DataTruncation tienen un SQLState 01004. ResultSet rs = stmt. Los métodos de DataTruncation permiten encontrar en que columna o parámetro se truncaron los datos. System.sql que forma parte de la versión JDK 1.println("").1 (referenciado como el API JDBC 1. warn = warn.next()) { String coffeeName = rs. System. System. podemos llamar al método getNextWarning de SQLWarning para obtener avisos adicionales.out. if (warn != null) { System. warning = warning.out.getSQLState()).getNextWarning().out.out.println(" " + coffeeName).out.getSQLState()).getWarnings().getMessage()).getErrorCode()). Sin embargo. esto significa que si queremos recuperar los avisos reportados por una sentencia. System. rs.createStatement(). una subclase de SQLWarning. System.out.print("Vendor error code: ").getNextWarning().executeQuery("select COF_NAME from COFFEES"). el aviso más común es un DataTruncation.println("\n---Warning---\n"). System.println("SQLState: " + warning. if (warning != null) { System.print("Vendor error code: ").esas clases tiene un método getWarnings.println("\n---Warning---\n"). 257 .out.println("SQLState: " + warn.sql que está incluido en la versión JDK 1. System.println("Coffees available at the Coffee Break: "). por eso no se apilan. El API de JDBC 2.println("Message: " + warning. El siguiente fragmento de código ilustra como obtener información completa sobre los avisos reportados por el objeto Statement.2 (conocido como el API JDBC 2. stmt y también por el objeto ResultSet.getErrorCode()). indicando que ha habido un problema al leer o escribir datos.out.println(""). System. al que debemos llamar para ver el primer aviso reportado en la llamada al objeto.0) incluye muchas nuevas características no incluidas en el paquete java. System.out. debemos hacerlo antes de ejecutar otra sentencia. si la ruptura se produjo en una operación de lectura o de escritura..0 El paquete java. while (warn != null) { System.out. SQLWarning warning = stmt.getMessage()). y cuántos bytes se transmitieron realmente.out.out.0).out. De aquellos que son reportados. while (warning != null) { System.println(warn. Si getWarnings devuelve un aviso. } } } Los avisos no son muy comunes.out. System.getString("COF_NAME"). cuántos bytes deberían haber sido transmitidos.println("Message: " + warn. while (rs.println(warning.

1.2 siguiendo las instrucciones de descarga 2. Antes de poder aprovechar estas ventajas. También hay métodos que nos permiten mover el cursor a una fila particular y comprobar la posición del cursor.CONCUR_READ_ONLY). podremos hacer las siguientes cosas: Ir hacia adelante o hacia atrás en una hoja de resultados o movernos a un fila específica. y TYPE_SCROLL_SENSITIVE. necesitamos hacer lo siguiente. Statement stmt = con. PRICE FROM COFFEES"). Otro uso será movernos a una fila para actualizarla. ResultSet srs = stmt. Lo que debemos recordar aquí es que si especificamos un tipo. Instalar un Driver JDBC que implemente las características del JDBC 2.  Hacer actualizaciones de las tablas de la base datos utilizando métodos Java en lugar de utilizar comandos SQL. Inicialización para Utilizar JDBC 2. El segundo argumento es una de las dos constantes de ResultSet para especificar si la hoja de resultados es de sólo lectura o actualizable:CONCUR_READ_ONLY y CONCUR_UPDATABLE. También. Descargar el JDK 1. 258 . no es posible probar el código de desmostración de las características del JDBC 2. Enviar múltiples secuencias SQL a la base de datos como una unidad. también debemos especificar si es de sólo lectura o actualizable. excepto en que añade dos argumentos al método createStatement. pero no se asegura que éstos funcionen.TYPE_SCROLL_SENSITIVE. Mover el Cursor por una Hoja de Resultados Una de las nuevas características del API JDBC 2. El primer argumento es una de las tres constantes añadidas al API ResultSet para indicar el tipo de un objeto ResultSet: TYPE_FORWARD_ONLY.  Uitlizar los nuevos tipos de datos SQL3 como valores de columnas. ResultSet.0. Como consecuencia.0 es la habilidad de mover el cursor en una hoja de resultados tanto hacia atrás como hacia adelante. La hoja de resultados Scrollable hace posible crear una herramienta GUI (Interface Gráfico de Usuario) para navegar a través de ella. debemos especificar primero el tipo. no se había implementado ningún driver que soportara las nuevas características.0.0. o batch.0 utilizadas en el código. Este código es similar al utilizado anteriormente. y como ambos parámetros son int. pero hay muchos en desarrollo. Podemos aprender de los ejemplos. En el momento de escribir esto.Con el API JDBC 2. necesitamos crear un objeto ResultSet Scrollable.createStatement(ResultSet. TYPE_SCROLL_INSENSITIVE. lo que probablemente será uno de los principales usos de esta característica. Acceder a un controlador de base de datos que implemente las características del JDBC utilizadas en el código.0 Si queremos ejecutar código que emplee alguna de las características del JDBC 2. el compilador no comprobará si los hemos intercambiado.executeQuery("SELECT COF_NAME. 3.

que mueve el cursor una fila hacia delante (hacia el final de la hoja de resultados).getString("COF_NAME"). El siguiente fragmento de código imprime los valores de cada fila de srs. Colombian 7. pero ahora tenemos muchas más formas para mover el cursor. Si no se especifican constantes para el tipo y actualización de un objeto ResultSet. Incluso aunque una hoja de resultados se seleccione desplazable.out.0. lo que hace posible utilizarlos en un bucle while. El bucle termina cuando alcanza la última fila.99 259 . float price = srs. while (srs. Generalmente hablando. podemos utilizarlo para mover el cursor sobre la hoja de resultados.0). Este método todavía es apropiado si queremos acceder a las filas una a una. } La salida se podría parecer a esto. En este momento.createStatement(ResultSet. srs en el ejemplo anterior.getFloat("PRICE"). que mueve el cursor una fila hacia atrás (hacia el inicio de la hoja de resultados). Statement stmt = con. el cursor también se posiciona inicialmente delante de la primera fila. Obtendremos un objeto ResultSet desplazable si utilizamos una de estas constantes:TYPE_SCROLL_INSENSITIVE o TYPE_SCROLL_SENSITIVE. pero para refrescar la memoria.next()) { String name = srs. Los tres tipos de hojas de resultados harán visibles los resultados si se cierran y se vuelve a abrir. entraremos en más detalle más adelante. una hoja en la que el cursor sólo se mueve hacia adelante. Una vez que tengamos un objeto ResultSet desplazable. En el API JDBC 1. PRICE FROM COFFEES"). ResultSet. obtendremos automáticamente una TYPE_FORWARD_ONLY y CONCUR_READ_ONLY (exactamente igual que en el API del JDBC 1. La contrapartida del método next. es el nuevo método previous.CONCUR_READ_ONLY). tenía el cursor posicionado antes de la primera fila. Ye hemos utilizado un método next en un bucle while. 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. no necesitamos preocuparnos de los puntos delicados de las capacidades de un objeto ResultSet.TYPE_SCROLL_SENSITIVE.Especificando la constante TYPE_FORWARD_ONLY se crea una hoja de resultados no desplazable. yendo de la primera fila a la última. con cinco espacios en blanco entre el nombre y el precio. siempre estaremos limitados por nuestro controlador de base de datos y el driver utilizados.println(name + " " + price). 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 métodos para detectar estos cambios.99 French_Roast 8. System.executeQuery("SELECT COF_NAME. es decir. Aunque deberíamos tener en mente el hecho de que no importa el tipo de hoja de resultados que especifiquemos. 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. haciendo que el método next devuelva false. ResultSet srs = stmt. Recuerda que cuando creabamos un objeto ResultSet anteriormente. la única forma de mover el cursor era llamar al método next. Ambos métodos devuelven false cuando el cursor se sale de la hoja de resultados (posición antes de la primera o después de la última fila).

// cursor está en la tercera fila 260 . srs.getString("COF_NAME"). se puede especificar cuántas filas se moverá desde la fila actual y también la dirección en la que se moverá. el cursor debe estar detrás de la última fila. srs. cuando el método previous devuelve false. srs.CONCUR_READ_ONLY). Se puede mover el cursor explícitamente a esa posicón con el método afterLast. el método next mueve el cursor a la fila siguiente.println(name + " " + price).out.TYPE_SCROLL_INSENSITIVE. . PRICE FROM COFFEES"). srs. Tres métodos mueven el cursor a una posición relativa a su posición actual.99 Como se puede ver.createStatement(ResultSet.previous()) { String name = srs. la siguiente línea de código movería el cursor a la fila 497.99 Colombian_Decaf 8.99 Espresso 9. en el siguiente fragmente de código. Si el número es positivo. y afterLast mueven el cursor a la fila indicada en sus nombres. El bucle termina cuando el cursor alcanza la posición anterior a la primera fila.99 Al igual que en el fragmento anterior. Con el método relative.99 Colombian_Decaf 8. beforeFirst. ResultSet.executeQuery("SELECT COF_NAME. La siguiente línea de código mueve el cursor a la cuarta fila de srs. ResultSet srs = stmt.absolute(4). while (srs. y luego a la fila anterior en cada iteracción del bucle while. srs. pero las filas están en orden inverso. Se puede mover el cursor a una fila particular en un objeto ResultSet. French_Roast_Decaf 9. System. Si srs tuviera 500 filas. // cursor está en la primera fila . Si el número es negativo. Un número positivo mueve el cursor hacia adelante el número de filas dado.99 French_Roast 8. el cursor se mueve a la cuarta fila. Por ejemplo. . Statement stmt = con. luego a la primera y por último a la tercera.afterLast().absolute(4).absolute(-4). pero para hacer esto. por eso llamar a absolute(1) pone el cursor en la primera fila.relative(2). un número negativo mueve el cursor hacia atrás el número de filas dado. Los métodos first. Como hemos podido ver. El método absolute moverá el cursor al número de fila indicado en su argumento. last. podemos procesar todas las filas de srs hacia atrás. .getFloat("PRICE"). y el método previous lo mueve a la fila anterior. . float price = srs. Luego el método previous mueve el cursor desde la posicón detrás de la última fila a la última fila.99 French_Roast_Decaf 9. srs. // cursor está en la cuarta fila . } La salida se podría parecer a esto. por eso llamar a absolute(-1) pone el cursor en la última fila.99 Colombian 7. las dos salidas tienen los mismos valores.relative(-3). el cursor se mueve al número dado desde el principio. mueve el cursor al número dado desde el final.Espresso 9.

el cursor no estará después de la última fila. Statement stmt = con.out.isAfterLast() == false) { srs. La posición se indica en sus nombres:isFirst.println(name + " " + price).createStatement(ResultSet. Connection con = DriverManager. podemos obtener el nuevo valor de una fila después de haberlo cambiado. El siguiente fragmento de código ilustra la creacción de un objeto ResultSet actualizable. por eso se llama al método afterLast.TYPE_SCROLL_SENSITIVE.relative(-3). el siguiente fragmento de código comprueba si el cursor está después de la última fila antes de llamar al método previous en un bucle while. uprs. // rowNum debería ser 3 Existen cuatro métodos adicionales que permiten verificar si el cursor se encuentra en una posición particular. srs. } En la siguiente página. veremos cómo utilizar otros dos métodos de ResultSet para mover el cursor. if (srs. // rowNum debería ser 1 srs.afterLast(). Para hacer esto.getString("COF_NAME"). } while (srs. 261 . int rowNum = srs. suministramos la constante CONCUR_UPDATABLE de ResulSet al método createStatement. También veremos ejemplos que ilustran por qué podríamos querer mover el cursor a ciertas posiciones.relative(2).getConnection("jdbc:mySubprotocol:mySubName").0 es la habilidad de actualizar filas en una hoja de resultados utilizando métodos Java en vez de tener que enviar comandos SQL.absolute(4). System. se puede utilizar getRow para verificar la posición actual del cursor en el ejemplo anterior. necesitamos crear un objeto ResultSet actualizable. isBeforeFirst.getFloat("PRICE").getRow(). Hacer Actualizaciones en una Hoja de Resultados Otra nueva característica del API JDBC 2. isLast. Por ejemplo.CONCUR_UPDATABLE). isAfterLast. podemos movernos a las filas que queremos cambiar. Si el método isAfterLast devuelve false.getRow(). // rowNum debería ser 4 srs.getRow(). float price = srs. generalmente queremos poder movernos por ella. pero cuando se hacen cambios en una hoja de resultados. Todos estos métodos devuelven un boolean y por lo tanto pueden ser utilizados en una sentencia condicional.previous()) { String name = srs. Observa que el código también lo hace desplazable. moveToInsertRow y moveToCurrentRow. int rowNum = srs.El método getRow permite comprobar el número de fila donde está el cursor. Pero antes de poder aprovechar esta capacidad. Con una hoja de resultados desplazable. como se ha visto en ejemplos anteriores. Un objeto ResultSet actualizable no tiene porque ser desplazable. El objeto Statement creado producirá un objeto ResultSet actualizable cada vez que se ejecute una petición. Esto garantiza que el cursor estára después de la última fila antes de utilizar el método previous en el bucle while para cubrir todas las filas de srs. y si el tipo es TYPE_SCROLL_SENSITIVE. ResultSet. int rowNum = srs. Por ejemplo.

uprs. borrar una fila de uprs. PRICE FROM COFFEES"). esta vez utilizando el API JDBC 2. Supongamos que queremos aumentar el precio del café "French Roast Decaf" a 10. Aquí está el código para actualizar tanto uprs como COFFEES.99 llamando al método updateFloat. pero el precio en la tabla COFFEES de la base de datos será todavía 9.----Colombian 7.99.) En este punto. 10.99. Las operaciones de actualización en el API JDBC 2. Al igual que en los métodos getXXX de ResultSet. o modificar un valor de una columna de uprs. la actualización podría ser algo como esto. stmt. uprs resultante se podría parecer a esto. 10.executeUpdate("UPDATE COFFEES SET PRICE = 10. uprs. debemos llamar al método updateRow de ResultSet.executeQuery("SELECT COF_NAME. Una vez situado el cursor. Existe un método updateXXX diferente para cada tipo (updateString.last().99" + "WHERE COF_NAME = FRENCH_ROAST_DECAF"). el precio en uprs para "French Roast Decaf" será 10..last().99 French_Roast_Decaf 9. el parámetro que designa la columna podría ser el nombre de la columna o el número de la columna.0. updateInt. uprs. La segunda línea de código cambia el valor de la columna PRICE a 10. uprs.ResultSet uprs = stmt. Los métodos updateXXX de ResultSet toman dos parámetros: la columna a actualizar y el nuevo valor a colocar en ella.99).updateRow().99 Colombian_Decaf 8.99 French_Roast 8. updateBigDecimal. Se utiliza este método porque el valor de la columna que queremos actualizar es un float Java.0 afectan a los valores de columna de la fila en la que se encuentra el cursor. utilizando el API JDBC 1.99). etc.0.99 Podemos utilizar los nuevos métodos del JDBC 2. todos los métodos de actualización que llamemos operarán sobre esa fila hasta que movamos el cursor a otra fila. COF_NAME PRICE -----------------. El siguiente fragmento de código muesta otra forma de realizar la actualización.updateFloat("PRICE". uprs.0 en el interface ResultSet para insertar una nueva fila en uprs.updateFloat("PRICE".99 Espresso 9. 262 .99. Actualizar una Hoja de Resultados Programáticamente Una actualización es la modificación del valor de una columna de la fila actual. Para que la actualización tenga efecto en la base de datos y no sólo en la hoja de resultados. El objeto ResultSet. por eso en le primera línea se llama al método last para mover el cursor a la última fila (la fila donde la columna COF_NAME tiene el valor FRENCH_ROAST_DECAF).

updateRow(). no a filas de la 263 . Observa que cancelRowUpdates cancela todas las actualizaciones en una fila. El siguiente fragmento de código primero cancela el precio 10. uprs. Tenemos que llamar al método cancelRowUpdates antes de llamar al método updateRow. sólo se hace una llamada al método updateRow para actualizar la base de datos con todos los cambios realizados en la fila actual. Si.79). uprs.99). 9. uprs. por el contrario.updateFloat("PRICE". uprs.79.79). no podemos cancelar sólo una de ellas. En este ejemplo.99 y luego lo actualiza a 10. El siguiente fragmento de código cambia el precio de esa fila a 9.cancelRowUpdates(). tenemos que mover el cursor a la fila que contiene ese café.Si hubiéramos movido el cursor a una fila diferente antes de llamar al método updateRow. llamar a cancelRowUpdates no hará nada.last(). una vez que se llama a updateRow. El concepto a recordar es que las actualizaciones y las operaciones relacionadas se aplican sobre la fila en la que se encuentra el cursor. Si también queremos actualizar el precio de COLOMBIAN_DECAF. uprs. podemos llamar al método previous para posicionar el cursor en la fila de COLOMBIAN_DECAF.updateFloat("PRICE". Todos los movimientos de cursor se refieren a filas del objeto ResultSet. uprs.99 podríamos haber cancelado la actualización llamando al método cancelRowUpdates. Cómo la fila de COLOMBIAN_DECAF precede inmediatamente a la fila de FRENCH_ROAST_DECAF.updateRow().79 en vez de 10. sólo se había actualizado una columna.updateFloat("PRICE". 10.previous(). uprs. 10. Incluso si hay muchas llamadas a métodos updateXXX. nos damos cuenta de que el precio debería haber sido 10.79 tanto en la hoja de resultados como en la base de datos. por eso. pero podemos llamar a un método updateXXX apropiado para cada una de las columnas de la fila. si había muchas llamadas a método updateXXX en la misma fila. la actualización se habría perdido. uprs.

tabla de la base de datos. Supongamos que nuestro propietario del café ha obtenido una nueva variedad de café de uno de sus suministradores. en una hoja de resultados con cinco filas. El siguiente fragmento de código. Observa que estos son los mismos métodos updateXXX utilizados en la página anterior para cambiar el valor de una columna. en el que stmt es un objeto Statement. lo que podemos hacer llamando al método moveToInsertRow. 10. no hay forma de saber donde será insertada dentro de la tabla. que contiene todas las filas y columnas de la tabla COFFEES. Este único método inserta la fila simultáneamente tanto en el objeto ResultSet como en la tabla de la base de datos de la que la hoja de datos fue seleccionada.0 en vez de utilizar comandos SQL. Insertar y Borrar filas Programáticamente En la sección anterior hemos visto cómo modificar el valor de una columna utilizando métodos del API JDBC 2. Utilizando el API JDBC 1. y. etc. pero podrían estar localizadas en cualquier lugar de la tabla. 150. Esta fila realmente no forma parte de la hoja de resultados. y quiere añadirlo a su base de datos. 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. y hace las actualizaciones en la fila apropiada. Hacemos esto llamando a los métodos updateXXX apropiados para cada valor. muestra esta aproximación. podemos llamar al método insertRow para insertar la fila que hemos rellenado en la hoja de resultados.99. la fila 5 será la última. El primer paso será mover el cursor a la fila de inserción. Se construye una nueva fila en una llamada "fila de inserción".0. El "High Ground". la sengunda siendo la fila 2. Si una petición selecciona cinco filas de la tabla de la base de datos. uprs.0 podría escribir el código que pasa una sentencia insert de SQL al controlador de la báse de datos. 0)"). El siguiente paso será seleccionar un valor para cada columna de la fila. . habrá cinco filas en la hoja de resultados.executeUpdate("INSERT INTO COFFEES " + "VALUES ('Kona'. El controlador de la base de datos sigue la pista de las filas seleccionadas. De hecho. La fila 1 puede ser identificada como la primera. 0. con la primera fila siendo la fila 1. podemos constuir una nueva fila insertándola tanto en la hoja de resultados como en la tabla COFFEES en un sólo paso. después de tener un objeto ResultSet con los resultados de la tabla COFFEES. una fila especial asociada con cada objeto ResultSet. Finalmente. Básicamente. Con el API JDBC 2. por ejemplo. podemos pensar en ella como un buffer separado en el que componer una nueva fila. el orden de las filas en la tabla de la base de datos es indeterminado. stmt. El siguiente fragmento de código crea un objeto ResultSet actualizable y desplazable. Se puede hacer esto mismo sin comandos SQL utilizando los métodos de ResultSet del API JDBC 2.0 también podemos insertar una fila en una tabla o borrar una fila existente programáticamente. Cuando se inserta una fila.

150).updateInt("SALES".updateFloat(3. uprs. ResultSet uprs = stmt. obtendremos una SQLException. Cuando el cursor está sobre la fila de inserción. 0). Como podemos utilizar el nombre o el número de la columna para indicar la columna seleccionada. "Kona"). uprs. 10. el valor seleccionado con un método updateXXX reemplazaba inmediatamente el valor de la columna en la hoja de resultados.updateInt(2. 0). Esto también es cierto si falta una columna de la tabla en nuestro objeto ResultSet. mostrada en el ejemplo SQL. En el ejemplo anterior.CONCUR_UPDATABLE).99). uprs para insertar la fila para "kona". Se debe llamar al método updateRow para hacer que las actualizaciones ocurran en la base de datos.moveToInsertRow().executeQuery("SELECT * FROM COFFEES"). uprs. uprs. Si la columna no acepta valores null. 150).Connection con = DriverManager. Tanto en actualizaciones como en inserciones. uprs.99). uprs. nuestro código para seleccionar los valores de columna se podría parecer a esto. 0).createStatement(ResultSet. pero lo es en la fila de inserción y no en la propia hoja de resultados.updateString(1. selecciona los cinco valores de columna e inserta la fila dentro de uprs y COFFEES. "Kona"). Para el inserciones. Mueve el cursor a la fila de inserción. Podríamos preguntarnos que sucedería si insertáramos una fila pero sin suministrar los valores para cada columna. llamar a los métodos updateXXX no afectan a la tabla de la base de datos.updateInt(4. Especialmente si nuestra tabla tiene cientos o miles de filas. el método insertRow inserta la nueva fila en la hoja de resultados y en la base de datos al mismo tiempo. lo que producía una hoja de resultados con todas las columnas y todas las filas. uprs. 0). uprs. uprs. nuestra petición no tiene porque seleccionar todas las filas.updateFloat("PRICE". querremos utilizar una claúsula WHERE para límitar el número de filas devueltas por la sentencia SELECT. En aquellos ejemplos. el valor asignado a esa columna es NULL. la petición era SELECT * FROM COFFEES. Cuando queremos insertar una o más filas.updateInt("SUP_ID". 10. uprs. Esto era porque el cursor estaba sobre una fila de la hoja de resultados.insertRow(). pero sí todas las columnas. ResultSet. el valor seleccionado con un método updateXXX también es automáticamente seleccionado.getConnection("jdbc:mySubprotocol:mySubName").updateInt(5. Podríamos habernos preguntado por qué los métodos updateXXX parecen tener un comportamiento distinto a como lo hacían en los ejemplos de actualización. uprs.updateString("COF_NAME". 265 . Si no suministramos valores para una columna que estaba definida para aceptar valores NULL de SQL.updateInt("TOTAL". uprs. Statement stmt = con.TYPE_SCROLL_SENSITIVE. El siguiente fragmento de código utiliza el objeto ResultSet.

} catch(java. 266 . relative. 10. el código inserta la fila en la hoja de resultados y en la base de datos con el método insertRow.err. Hay algunas cosas que podríamos observar sobre el código. desplazable y sensible a los cambios hechos por ella y por otros. Después de haber introducido los valores de una fila con los métodos updateXXX. selecciona valores para otra nueva fila. uprs es actualizable. que requieren movimientos relativos a la fila actual.99f). llamar a cualquier método que ponga el cursor en una fila específica.ClassName"). Connection con. uprs. que por definición es la fila actual.updateFloat("PRICE".CONCUR_UPDATABLE). uprs.moveToInsertRow(). uprs. Aunque es TYPE_SCROLL_SENSITIVE.Después de haber llamado al método insertRow. y absolute. la hoja de resultados graba la fila en la que se encontraba el cursor. System.getConnection(url.forName("myDriver. 1. afterLast. "Kona"). Como consecuencia.executeQuery("SELECT * FROM COFFEES"). Observa que sólo podemos llamar a moveToCurrentRow cuando el cursor está en la fila de inserción.err.lang.*. es posible que los métodos getXXX llamados después de las inserciones no recuperen los valores de la nuevas filas. como first. Luego. uprs. 0). "myLogin".getMessage()). uprs.println(e.TYPE_SCROLL_SENSITIVE. ResultSet. o podemos mover el cursor de nuevo a la hoja de resultados. El objeto ResultSet. Insertar una Fila El siguiente ejemplo de código es un programa completo que debería funcionar si tenemos un driver JDBC 2. uprs.print("ClassNotFoundException: "). last.0 que implemente una hoja de resultados desplazable. beforeFirst.ClassNotFoundException e) { System. podemos. import java. Por ejemplo. Cuando llamamos al método moveToInsertRow.updateInt("SALES". podemos construir otra fila. try { Class. } try { con = DriverManager. También podemos utilizar los métodos previous. stmt = con.updateInt("SUP_ID". y moveToCurrentRow. 2.createStatement(ResultSet. 0). 150). ResultSet uprs = stmt.updateString("COF_NAME". estándo todavía en la "fila de inserción".sql. public class InsertRows { public static void main(String args[]) { String url = "jdbc:mySubprotocol:myDataSource". Hay métodos en el interface DatabaseMetaData que nos dirán 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. Statement stmt.updateInt("TOTAL". Esto también explica porque podemos utilizar los métodos previous y relative. el método moveToCurrentRow puede mover el cursor desde la fila de inserción a la fila en la que se encontraba anteriormente. "myPassword").

getInt("SALES"). } uprs. nuestro código se parecería a esto.println(" " + sales + " " + total).getFloat("PRICE"). uprs.beforeFirst().println("Table COFFEES after insertion:").updateInt("TOTAL". uprs. Ver los cambios en una Hoja de Resultados Si modificamos los datos en un objeto ResultSet. Con algunos driver JDBC.updateInt("SUP_ID".getInt("SUP_ID"). while (uprs. 11. si queremos borrar la cuarta fila de la hoja de resultados uprs.deleteRow(). } catch(SQLException ex) { System.getMessage()). En otras palabras. 267 . stmt. uprs. los cambios se harán visibles si lo cerramos y lo abrimos de nuevo. Borrar una fila es la tercera forma de modificar un objeto ResultSet. Todo lo que tenemos que hacer es mover el cursor a la fila que queremos borrar y luego llamar al método deleteRow. int total = uprs. uprs. deberíamos recordar que los drivers JDBC manejan las eliminaciones de forma diferente.out. uprs. 0). int sales = uprs. se puede utilizar el método absolute con la posición original de la fila para mover el cursor. float price = uprs.close(). hemos visto cómo actualizar un valor y cómo insertar una nueva fila. uprs. En cualquier caso.print(name + " " + id + " " + price). System.99f). si escribimos una aplicación para ejecutarse con diferentes bases de datos.updateFloat("PRICE".close(). una línea borrada es eliminada y ya no es visible en una hoja de resultados. El único problema con las eliminaciones es lo que ResultSet realmente hace cuando se borra una fila. System. } } Borrar una Fila Hasta ahora.insertRow().insertRow().uprs.out.absolute(4). La cuarta fila ha sido eliminada de uprs y de la base de datos.err.updateInt("SALES". con. porque el número de filas en la hoja de resultados no ha cambiado.close(). System. uprs.out. int id = uprs. Algunos drives JDBC utilizan una fila en blanco en su lugar pone (un "hole") donde la fila borrada fuera utilizada. si re-ejecutamos la misma petición. 150). "Kona_Decaf").next()) { String name = uprs. uprs.updateString("COF_NAME".println("SQLException: " + ex. Si existe una fila en blanco en lugar de la fila borrada.getString("COF_NAME"). Por ejemplo.getInt("TOTAL"). uprs. Por ejemplo. 0). y es la más simple. no deberíamos escribir código que dependiera de si hay una fila vacía en la hoja de resultados.

(La urgencia de obtener los últimos datos es bastante improvable en la tabla COFFEES. El siguiente código ilustra cómo una aplicación podría utilizar el método refreshRow cuando es absolutamente crítico ver los últimos valores. Se puede utilizar el método refreshRow para obtener los últimos valores de una fila en la base de datos. Y siempre estamos limitados por lo que proporcionan las bases de datos y los drivers.setTransactionIsolation(TRANSACTION_READ_COMMITTED). Con este nivel de aislamiento. (Generalmente. basada en los datos actuales de la tabla. generalmente no podremos ver los cambios hechos mientras esté abierta. especialmente si el controlador de la base de datos devuelve múltiples filas cada vez que se llama a refreshRow. donde con es un objeto Connection activo. pero la única forma de estar seguros es utilizar los métodos DatabaseMetaData que devuelven esta información. Esta hoja de resultados reflejara naturalmente los cambios que hayamos hecho anteriormente. nuestro objeto ResultSet no mostrará ningún cambio antes de ser enviado. O. depende de conocer los últimos precios en la amplia fluctuación del mercado del café. Normalmente veremos inserciones y eliminaciones. Con un objeto ResultSet que sea TYPE_SCROLL_SENSITIVE. 268 . utilizar el método refreshRow es el único método para asegurarnos que estamos viendo los últimos datos. Por ejemplo. estaremos más interesados en los cambios hechos por otros). Observa que la hoja se resultados debería ser sensible. Por eso. una aplicación no podría ver siempre los últimos cambios si el driver recupera varias filas a la vez y las almacena. En un objeto ResultSet que sea TYPE_SCROLL_INSENSITIVE. del driver. siempre podremos ver las actualizaciones que alguien haga en los valores de las columnas. el rendimiento se empobrece. la siguiente línea de código.producirá una nueva hoja de resultados. pero puede mostrar los cambios que podrían tener problemas de consistencia. El problema es que a niveles más altos de aislamiento. pero la fortuna de un inversor. podríamos subir el nive de aislamiento a TRANSACTION_REPEATABLE_READ. Algunos programadores utilizan sólo este tipo de objeto ResultSet porque quieren una vista consistente de los datos y no quieren ver los cambios hechos por otros. La cuestión es si podemos ver los cambios que hayamos realizado mientras el objeto ResultSet esté todavía abierto. Incluso aunque una hoja de resultados sea sensible y los cambios sean visibles. La respuesta depende del controlador de la base de datos. no hará nada. Podemos regular la extensión de que los cambios sean visibles aumentando o bajando el nivel de aislamiento de la transación con la base de datos. con. De todas formas. Para permitir menores niveles de inconsistencia. selecciona el nivel de aislamiento de la conexión a TRANSACTION_READ_COMMITTED. Este método puede utilizar muchos recursos. y del tipo del objeto ResultSet utilizado. puede utilizarse cuando es crítico tener los últimos datos. si queremos utilizar el método refreshRow con un objeto ResultSet que sea TYPE_SCROLL_INSENSITIVE.

uprs. ResultSet.absolute(4).createStatement( ResultSet.getFloat("PRICE"). . . if (price2 > price1) { // do something. uprs. Float price2 = uprs.por ejemplo. uprs. // do something. querriamos asegurarnos de que el nuestro asiento reservado en el avión de regreso todavía está disponible). Statement stmt = con. ResultSet uprs = stmt. .absolute(4).getFloat("PRICE").CONCUR_UPDATABLE). Float price1 = uprs.refreshRow().TYPE_SCROLL_SENSITIVE. PRICE FROM COFFEES). .executeQuery(SELECT COF_NAME. } 269 .