Java
Java
1
Manual de Java
índice
excepciones ..................................................................................................... 71
introducción a las excepciones....................................................................................................... 71
try y catch..................................................................................................................................... 71
manejo de excepciones ................................................................................................................. 73
throws.......................................................................................................................................... 74
throw ........................................................................................................................................... 75
finally........................................................................................................................................... 75
clases fundamentales (I)................................................................................. 77
la clase Object ............................................................................................................................. 77
clase Class ................................................................................................................................... 79
reflexión ....................................................................................................................................... 82
clases para tipos básicos ............................................................................................................... 84
clase StringBuffer .......................................................................................................................... 85
números aleatorios ....................................................................................................................... 86
fechas .......................................................................................................................................... 87
cadenas delimitadas. StringTokenizer.............................................................................................. 92
entrada y salida en Java................................................................................ 93
clases para la entrada y la salida ................................................................................................... 93
entrada y salida estándar............................................................................................................... 96
Ficheros ........................................................................................................... 99
clase File...................................................................................................................................... 99
secuencias de archivo ................................................................................................................. 102
RandomAccessFile ...................................................................................................................... 106
el administrador de seguridad...................................................................................................... 107
serialización ............................................................................................................................... 107
clases fundamentales (II) colecciones .......................................................... 109
estructuras estáticas de datos y estructuras dinámicas .................................................................... 109
interfaz Collection ....................................................................................................................... 110
Listas enlazadas .......................................................................................................................... 111
colecciones sin duplicados........................................................................................................... 112
árboles. SortedSet ....................................................................................................................... 113
mapas ....................................................................................................................................... 114
colecciones de la versión 1.0 y 1.1 .............................................................................................. 114
la clase Collections ..................................................................................................................... 117
clases fundamentales (y III).......................................................................... 119
números grandes ........................................................................................................................ 119
internacionalización. clase Locale................................................................................................. 122
formatos numéricos..................................................................................................................... 124
Propiedades ............................................................................................................................... 125
temporizador .............................................................................................................................. 127
2
© Jorge Sánchez Asenjo’ 2004
Swing............................................................................................................. 129
AWT y Swing .............................................................................................................................. 129
componentes.............................................................................................................................. 129
Contenedores............................................................................................................................. 135
eventos ...................................................................................................................................... 139
mensajes hacia el usuario. clase JOptionPane............................................................................... 156
Programación de gráficos. Java2D .............................................................. 160
Java2D ...................................................................................................................................... 160
paneles de dibujo ....................................................................................................................... 160
clases de dibujo y contextos gráficos............................................................................................. 164
representación de gráficos con Java 2D........................................................................................ 166
formas 2D.................................................................................................................................. 167
áreas ......................................................................................................................................... 172
trazos......................................................................................................................................... 173
pintura ....................................................................................................................................... 174
transformaciones ........................................................................................................................ 175
recorte ....................................................................................................................................... 177
composición............................................................................................................................... 177
fuentes ....................................................................................................................................... 178
imágenes de mapas de bits.......................................................................................................... 183
Threads ......................................................................................................... 185
Introducción ............................................................................................................................... 185
clase Thread y la interfaz Runnable ............................................................................................... 185
creación de threads..................................................................................................................... 186
control de Threads ...................................................................................................................... 188
estados de un thread................................................................................................................... 189
sincronización............................................................................................................................. 190
componentes Swing ...................................................................................... 193
introducción ............................................................................................................................... 193
administración de diseño ............................................................................................................. 193
apariencia.................................................................................................................................. 201
etiquetas .................................................................................................................................... 203
cuadros de texto ......................................................................................................................... 206
cuadros de contraseña ................................................................................................................ 208
botones...................................................................................................................................... 208
eventos ActionEvent..................................................................................................................... 210
casillas de activación................................................................................................................... 211
botones de opción ...................................................................................................................... 212
viewport ..................................................................................................................................... 214
JScrollPane................................................................................................................................. 215
Barras de desplazamiento............................................................................................................ 216
deslizadores ............................................................................................................................... 218
listas .......................................................................................................................................... 220
cuadros combinados ................................................................................................................... 223
cuadros de diálogo Swing ........................................................................................................... 225
3
Manual de Java
índice
applets .......................................................................................................... 233
introducción ............................................................................................................................... 233
métodos de una applet................................................................................................................ 235
la etiqueta applet ........................................................................................................................ 237
parámetros................................................................................................................................. 238
manejar el navegador desde la applet.......................................................................................... 239
paquetes .................................................................................................................................... 240
archivos JAR ............................................................................................................................... 240
el administrador de seguridad...................................................................................................... 242
programación en red.................................................................................... 245
introducción ............................................................................................................................... 245
sockets....................................................................................................................................... 245
clientes ...................................................................................................................................... 245
servidores................................................................................................................................... 247
métodos de Socket...................................................................................................................... 249
clase InetAddress ........................................................................................................................ 250
conexiones URL .......................................................................................................................... 251
JEditorPane ................................................................................................................................ 253
conexiones URLConnection.......................................................................................................... 255
JDBC .............................................................................................................. 259
introducción ............................................................................................................................... 259
conexión .................................................................................................................................... 262
ejecución de comandos SQL. interfaz Statement ............................................................................ 263
Excepciones en la base de datos .................................................................................................. 265
resultados con posibilidades de desplazamiento y actualización. JDBC 2.0...................................... 266
metadatos .................................................................................................................................. 269
proceso por lotes ........................................................................................................................ 276
Servlets y JSP ................................................................................................ 277
tecnologías del lado del servidor .................................................................................................. 277
J2EE .......................................................................................................................................... 280
empaquetamiento de las aplicaciones web ................................................................................... 280
http............................................................................................................................................ 281
Servlets ...................................................................................................................................... 283
JSP ............................................................................................................................................ 293
colaboración entre Servlets y/o JSPs ............................................................................................. 299
JavaBeans..................................................................................................... 305
introducción ............................................................................................................................... 305
empaquetamiento de JavaBeans .................................................................................................. 306
propiedades de los JavaBeans ..................................................................................................... 307
4
introducción
historia de Java
C++ pasó a ser el lenguaje de programación más popular a principios de los 90 (sigue
siendo un lenguaje muy utilizado).
Otras adaptaciones famosas fueron:
1Microsoft Foundation Classes, librería creada por Microsoft para facilitar la creación de
programas para el sistema Windows.
1
Manual de Java
introducción
Fortran
1954
CPL
Basic 1963 Simula
1964 1964
B Logo
Pascal 1969 1968
1970
C Sh
1971 1971
SmallTalk
1973
Modula
1975 Awk
1978
C++
1983
Quick Basic Perl
1984 1987
Turbo Pascal
1988
VBScript
1993
Delphi JavaScript Java PHP
1995 1995 1995 1995
Java 2
ASP 1998
1996
C#
2000
la llegada de Java
En 1991, la empresa Sun Microsystems crea el lenguaje Oak (de la mano del llamado
proyecto Green). Mediante este lenguaje se pretendía crear un sistema de televisión
interactiva. Este lenguaje sólo se llegó a utilizar de forma interna. Su propósito era crear
un lenguaje independiente de la plataforma y para uso en dispositivos electrónicos.
Se intentaba con este lenguaje paliar el problema fundamental del C++; que consiste
en que al compilar se produce un fichero ejecutable cuyo código sólo vale para la
plataforma en la que se realizó la compilación. Sun deseaba un lenguaje para programar
2
© Jorge Sánchez Asenjo’ 2004
Es interpretado (lo que acelera su ejecución remota, aunque provoca que las
aplicaciones Java se ejecuten más lentamente que las C++ en un ordenador local).
Permite multihilos
Es independiente de la plataforma
Java y JavaScript
Una de las confusiones actuales la provoca el parecido nombre que tienen estos dos
lenguajes. Sin embargo no tienen nada que ver entre sí; Sun creo Java y Netscape creo
JavaScript. Java es un lenguaje completo que permite realizar todo tipo de aplicaciones.
JavaScript es código que está inmerso en una página web.
La finalidad de JavaScript es mejorar el dinamismo de las páginas web. La finalidad de
Java es crear aplicaciones de todo tipo (aunque está muy preparado para crear sobre todo
aplicaciones en red). Finalmente la sintaxis de ambos lenguajes apenas se parece,
características de Java
bytecodes
Un programa C o C++ es totalmente ejecutable y eso hace que no sea independiente de la
plataforma y que su tamaño normalmente se dispare ya que dentro del código final hay
que incluir las librerías de la plataforma
3
Manual de Java
introducción
Intérprete MacIntosh
Aplicación MacIntosh
En Java la unidad fundamental del código es la clase. Son las clases las que se distribuyen
en el formato bytecode de Java. Estas clases se cargan dinámicamente durante la
ejecución del programa Java.
seguridad
Al interpretar el código, la JVM puede delimitar las operaciones peligrosas, con lo cual la
seguridad es fácilmente controlable. Además, Java elimina las instrucciones dependientes
de la máquina y los punteros que generaban terribles errores en C y la posibilidad de
4
© Jorge Sánchez Asenjo’ 2004
generar programas para atacar sistemas. Tampoco se permite el acceso directo a memoria
y además.
La primera línea de seguridad de Java es un verificador del bytecode que permite
comprobar que el comportamiento del código es correcto y que sigue las reglas de Java.
Normalmente los compiladores de Java no pueden generar código que se salte las reglas
de seguridad de Java. Pero un programador malévolo podría generar artificialmente
código bytecode que se salte las reglas. El verificador intenta eliminar esta posibilidad.
Hay un segundo paso que verifica la seguridad del código que es el verificador de
clase que es el programa que proporciona las clases necesarias al código. Lo que hace es
asegurarse que las clases que se cargan son realmente las del sistema original de Java y no
clases creadas reemplazadas artificialmente.
Finalmente hay un administrador de seguridad que es un programa configurable
que permite al usuario indicar niveles de seguridad a su sistema para todos los programas
de Java.
Hay también una forma de seguridad relacionada con la confianza. Esto se basa es
saber que el código Java procede de un sitio de confianza y no de una fuente no
identificada. En Java se permite añadir firmas digitales al código para verificar al autor del
mismo.
applet
Son programas Java pensados para ser colocados dentro de una página web. Pueden ser
interpretados por cualquier navegador con capacidades Java. Estos programas se insertan
en las páginas usando una etiqueta especial (como también se insertan vídeos,
animaciones flash u otros objetos).
Los applets son programas independientes, pero al estar incluidos dentro de una
página web las reglas de éstas le afectan. Normalmente un applet sólo puede actuar sobre
el navegador.
Hoy día mediante applets se pueden integrar en las páginas web aplicaciones
multimedia avanzadas (incluso con imágenes 3D o sonido y vídeo de alta calidad)
aplicaciones de consola
Son programas independientes al igual que los creados con los lenguajes tradicionales.
aplicaciones gráficas
Aquellas que utilizan las clases con capacidades gráficas (como awt por ejemplo).
servlets
Son aplicaciones que se ejecutan en un servidor de aplicaciones web y que como resultado
de su ejecución resulta una página web.
5
Manual de Java
introducción
(que además es gratuito) es el Java Developer Kit (JDK) de Sun, que se encuentra
disponible en la dirección http://java.sun.com.
Actualmente ya no se le llama así sino que se le llama SDK y en la página se referencia
la plataforma en concreto.
versiones de Java
Como se ha comentado anteriormente, para poder crear los bytecodes de un programa
Java, hace falta el JDK de Sun. Sin embargo, Sun va renovando este kit actualizando el
lenguaje. De ahí que se hable de Java 1.1, Java 1.2, etc.
Actualmente se habla de Java 2 para indicar las mejoras en la versión. Desde la versión
1.2 del JDK, el Kit de desarrollo se llama Java 2 Developer Kit en lugar de Java Developer
Kit. La última versión es la 1.4.2.
Lo que ocurre (como siempre) con las versiones, es que para que un programa que
utilice instrucciones del JDK 1.4.1, sólo funcionará si la máquina en la que se ejecutan los
bytecodes dispone de un intérprete compatible con esa versión.
Java 1.0
Fue la primera versión de Java y propuso el marco general en el que se desenvuelve Java.
está oficialmente obsoleto, pero hay todavía muchos clientes con esta versión.
Java 1.1
Mejoró la versión anterior incorporando las siguientes mejoras:
JDBC que es por ejemplo. Es soportado de forma nativa tanto por Internet Explorer
como por Netscape Navigator.
RMI llamadas a métodos remotos. Se utilizan por ejemplo para llamar a métodos de
objetos alojados en servidor.
Java 2
Apareció en Diciembre de 1998 al aparecer el JDK 1.2. Incorporó notables mejoras como
por ejemplo:
JFC. Java Foundation classes. El conjunto de clases de todo para crear programas
más atractivos de todo tipo. Dentro de este conjunto están:
El paquete Swing. Sin duda la mejora más importante, este paquete permite
realizar lo mismo que AWT pero superándole ampliamente.
Java Media
6
© Jorge Sánchez Asenjo’ 2004
Java 2D. Paquete (parte de JFC) que permite crear gráficos tridimensionales.
Java Media Framework. Paquete marco para elementos multimedia
Java Speech. Reconocimiento de voz.
Java Sound. Audio de alta calidad
Java TV. Televisión interactiva
plataformas
Actualmente hay tres ediciones de la plataforma Java 2
J2SE
Se denomina así al entorno de Sun relacionado con la creación de aplicaciones y applets en
lenguaje Java. la última versión del kit de desarrollo de este entorno es el J2SE 1.4.2.
J2EE
Pensada para la creación de aplicaciones Java empresariales y del lado del servidor. Su
última versión es la 1.4
J2ME
Pensada para la creación de aplicaciones Java para dispositivos móviles.
entornos de trabajo
El código en Java se puede escribir en cualquier editor de texto. Y para compilar el código
en bytecodes, sólo hace falta descargar la versión del JDK deseada. Sin embargo, la
escritura y compilación de programas así utilizada es un poco incomoda. Por ello
numerosas empresas fabrican sus propios entornos de edición, algunos incluyen el
compilador y otras utilizan el propio JDK de Sun.
7
Manual de Java
introducción
lenguajes, extensiones de todo tipo (CORBA, Servlets,...). Incluye además un
servidor de aplicaciones Tomcat para probar aplicaciones de servidor. Se descarga
en www.netbeans.org.
Sun ONE Studio. Entorno para la creación de aplicaciones Java creado por la propia
empresa Sun a partir de NetBeans (casi es clavado a éste). la versión Community
Edition es gratuita (es más que suficiente), el resto son de pago. Está basado en el
anterior. Antes se le conocía con el nombre Forte for Java. Está implicado con los
servidores ONE de Java.
Microsoft Visual J++ y Visual J#. Ofrece un compilador. El más recomendable para
los conocedores de los editores y compiladores de Microsoft (como Visual Basic por
ejemplo) aunque el Java que edita está más orientado a las plataformas de servidor
de Microsoft.
JBuilder. Entorno completo creado por la empresa Borland (famosa por su lenguaje
Delphi) para la creación de todo tipo de aplicaciones Java, incluidas aplicaciones
para móviles.
JCreator Pro. Es un editor comercial muy potente y de precio bajo. Ideal (junto con
Kawa) para centrarse en el código Java. No es un IDE completo y eso lo hace más
ligero, de hecho funciona casi en cualquier máquina.
La codificación Unicode2 usa 16 bits (2 bytes por carácter) e incluye la mayoría de los
códigos del mundo.
notas previas
Los archivos con código fuente en Java deben guardarse con la extensión .java. Como se
ha comentado cualquier editor de texto basta para crearle. Algunos detalles importantes
son:
Los comentarios; si son de una línea debe comenzar con “//” y si ocupan más de una
línea deben comenzar con “/*” y terminar con “*/”
/* Comentario
de varias líneas */
//Comentario de una línea
A veces se marcan bloques de código, los cuales comienza con { y terminan con } El
código dentro de esos símbolos se considera interno al bloque
{
...código dentro del bloque
}
código fuera del bloque
Este código escribe “¡Mi primer programa!” en la pantalla. El archivo debería llamarse
app.java ya que esa es la clase pública. El resto define el método main que es el que se
ejecutará al lanzarse la aplicación. Ese método utiliza la instrucción que escribe en
pantalla.
proceso de compilación
Hay que entender que Java es estricto en cuanto a la interpretación de la programación
orientada a objetos. Así, se sobrentiende que un archivo java crea una (y sólo) clase. Por
eso al compilar se dice que lo que se está compilando es una clase.
javadoc archivo.java
El resultado de esto es un archivo con el mismo nombre que el archivo java pero con la
extensión class. Esto ya es el archivo con el código en forma de bytecodes. Es decir con
el código precompilado.
Si la clase es ejecutable (sólo lo son si contienen el método main), el código se puede
interpretar usando el programa java del kit de desarrollo. Sintaxis:
java archivoClass
Estos comandos hay que escribirlos desde la línea de comandos de en la carpeta en la que
se encuentre el programa. Pero antes hay que asegurarse de que los programas del kit de
desarrollo son accesibles desde cualquier carpeta del sistema. Para ello hay que comprobar
que la carpeta con los ejecutables del kit de desarrollo está incluida en la variable de
entorno path.
Esto lo podemos comprobar escribiendo path en la línea de comandos. Si la carpeta
del kit de desarrollo no está incluida, habrá que hacerlo. Para ello en Windows 2000 o XP:
4> Añadir a la lista de la variable Path la ruta a la carpeta con los programas del
JDK.
PATH=C:\WINNT\SYSTEM32;C:\WINNT;C:\WINNT\SYSTEM32\WBEM;C:\Arch
ivos de programa\Microsoft Visual
Studio\Common\Tools\WinNT;C:\Archivos de programa\Microsoft
Visual Studio\Common\MSDev98\Bin;C:\Archivos de
programa\Microsoft Visual Studio\Common\Tools;C:\Archivos de
programa\Microsoft Visual Studio\VC98\bin;C:\Archivos de
programa\j2sdk_nb\j2sdk1.4.2\bin
En negrita está señalada la ruta a la carpeta de ejecutables (carpeta bin) del kit de
desarrollo. Está carpeta varía según la instalación
10
© Jorge Sánchez Asenjo’ 2004
javadoc
Javadoc es una herramienta muy interesante del kit de desarrollo de Java para generar
automáticamente documentación Java. genera documentación para paquetes completos o
para archivos java. Su sintaxis básica
es:
El funcionamiento es el siguiente. Los comentarios que comienzan con los códigos /** se
llaman comentarios de documento y serán utilizados por los programas de generación de
documentación javadoc.
Los comentarios javadoc comienzan con el símbolo /** y terminan con */ Cada línea
javadoc se inicia con un símbolo de asterisco. Dentro se puede incluir cualquier texto.
Incluso se pueden utilizar códigos HTML para que al generar la documentación se tenga
en cuenta el código HTML indicado.
En el código javadoc se pueden usar etiquetas especiales, las cuales comienzan con el
símbolo @. Pueden ser:
@see. Tras esta palabra se indica una referencia a otro código Java relacionado con
éste.
@deprecated. Palabra a la que no sigue ningún otro texto en la línea y que indica
que esta clase o método esta obsoleta u obsoleto.
@param. Palabra a la que le sigue texto qué describe a los parámetros que requiere
el código para su utilización (el código en este caso es un método de clase). Cada
parámetro se coloca en una etiqueta @param distinta, por lo que puede haber varios
@param para el mismo método.
@return. Tras esta palabra se describe los valores que devuelve el código (el código
en este caso es un método de clase)
El código javadoc hay que colocarle en tres sitios distintos dentro del código java de la
aplicación:
1> Al principio del código de la clase (antes de cualquier código Java). En esta
zona se colocan comentarios generales sobre la clase o interfaz que se crea
mediante el código Java. Dentro de estos comentarios se pueden utilizar las
etiquetas: @author, @version, @see, @since y @deprecated
2> Delante de cada método. Los métodos describen las cosas que puede
realizar una clase. Delante de cada método los comentarios javadoc se usan
para describir al método en concreto. Además de los comentarios, en esta zona
11
Manual de Java
introducción
se pueden incluir las etiquetas: @see, @param, @exception, @return, @since y
@deprecated
3> Delante de cada atributo. Se describe para qué sirve cada atributo en cada
clase. Puede poseer las etiquetas: @since y @deprecated
Ejemplo:
Tras ejecutar la aplicación javadoc, aparece como resultado la página web de la página
siguiente.
12
© Jorge Sánchez Asenjo’ 2004
13
Manual de Java
introducción
instrucción import
Hay código que se puede utilizar en los programas que realicemos en Java. Se importar
clases de objetos que están contenidas, a su vez, en paquetes estándares.
Por ejemplo la clase Date es una de las más utilizadas, sirve para manipular fechas. Si
alguien quisiera utilizar en su código objetos de esta clase, necesita incluir una instrucción
que permita utilizar esta clase. La sintaxis de esta instrucción es:
import paquete.subpaquete.subsubapquete....clase
Esta instrucción se coloca arriba del todo en el código. Para la clase Date sería:
import java.util.Date
Lo que significa, importar en el código la clase Date que se encuentra dentro del paquete
util que, a su vez, está dentro del gran paquete llamado java.
También se puede utilizar el asterisco en esta forma:
import java.util.*
Esto significa que se va a incluir en el código todas las clases que están dentro del paquete
util de java.
14
variables
introducción
Las variables son los contenedores de los datos que utiliza un programa. Cada variable
ocupa un espacio en la memoria RAM del ordenador para almacenar un dato
determinado.
Las variables tienen un nombre (un identificador) que sólo puede contener letras,
números y el carácter de subrayado (también vale el símbolo $). El nombre puede
contener cualquier carácter Unicode.
declaración de variables
Antes de poder utilizar una variable, ésta se debe declarar. Lo cual se debe hacer de esta
forma:
tipo nombrevariable;
Donde tipo es el tipo de datos que almacenará la variable (texto, números enteros,...) y
nombrevariable es el nombre con el que se conocerá la variable. Ejemplos:
int dias;
boolean decision;
int dias=365;
alcance o ámbito
Esas dos palabras sinónimas, hacen referencia a la duración de una variable. En el
ejemplo:
{
int x=12;
}
System.out.println(x); //Error
15
Manual de Java
variables
Java dará error, porque la variable se usa fuera del bloque en el que se creo. Eso no es
posible, porque una variable tiene como ámbito el bloque de código en el que fue creada
(salvo que sea una propiedad de un objeto).
int 4 -2.147.483.648 a
2.147.483.649
enteros
Los tipos byte, short, int y long sirven para almacenar datos enteros. Los enteros son
números sin decimales. Se pueden asignar enteros normales o enteros octales y
hexadecimales. Los octales se indican anteponiendo un cero al número, los
hexadecimales anteponiendo 0x.
int i=12;
byte b=i; //error de compilación
La solución es hacer un cast. Esta operación permite convertir valores de un tipo a otro.
Se usa así:
int i=12;
byte b=(byte) i; //No hay problema por el (cast)
16
© Jorge Sánchez Asenjo’ 2004
Hay que tener en cuenta en estos cast que si el valor asignado sobrepasa el rango del
elemento, el valor convertido no tendrá ningún sentido:
int i=1200;
byte b=(byte) i; //El valor de b no tiene sentido
booleanos
Los valores booleanos (o lógicos) sirven para indicar si algo es verdadero (true) o falso
(false). En C se puede utilizar cualquier valor lógico como si fuera un número; así
verdadero es el valor -1 y falso el 0. Eso no es posible en Java.
Si a un valor booleano no se le da un valor inicial, se toma como valor inicial el valor
false. Por otro lado, a diferencia del lenguaje C, no se pueden en Java asignar números
a una variable booleana (en C, el valor false se asocia al número 0, y cualquier valor
distinto de cero se asocia a true).
caracteres
Los valores de tipo carácter sirven para almacenar símbolos de escritura (en Java se
puede almacenar cualquier código Unicode). Los valores Unicode son los que Java
utiliza para los caracteres. Ejemplo:
char letra;
letra=’C’; //Los caracteres van entre comillas
letra=67; //El código Unicode de la C es el 67. Esta línea
//hace lo mismo que la anterior
También hay una serie de caracteres especiales que van precedidos por el símbolo \, son
estos:
carácter significado
\b Retroceso
\t Tabulador
\n Nueva línea
\f Alimentación de página
\r Retorno de carro
17
Manual de Java
variables
carácter significado
\” Dobles comillas
\’ Comillas simples
\udddd Las cuatro letras d, son en realidad números en hexadecimal.
Representa el carácter Unicode cuyo código es representado
por las dddd
La duda está en si esto se puede realizar. La respuesta es que sí. Sí porque un dato byte
es más pequeño que uno int y Java le convertirá de forma implícita. Sin embargo en:
int a=1;
byte b;
b=a;
El compilador devolverá error aunque el número 1 sea válido para un dato byte. Para
ello hay que hacer un casting. Eso significa poner el tipo deseado entre paréntesis
delante de la expresión.
int a=1;
byte b;
b= (byte) a; //No da error
En el siguiente ejemplo:
Aunque el resultado es 100, y ese resultado es válido para un tipo byte; lo que ocurrirá
en realidad es que ocurrirá un error. Eso es debido a que primero multiplica 100 * 100 y
como eso da 10000, no tiene más remedio el compilador que pasarlo a entero y así
quedará aunque se vuelva a dividir. La solución correcta sería:
operadores
introducción
Los datos se manipulan muchas veces utilizando operaciones con ellos. Los datos se
suman, se restan, ... y a veces se realizan operaciones más complejas.
18
© Jorge Sánchez Asenjo’ 2004
operadores aritméticos
Son:
operador significado
+ Suma
- Resta
* Producto
/ División
% Módulo (resto)
Hay que tener en cuenta que el resultado de estos operadores varía notablemente si
usamos enteros o si usamos números de coma flotante.
Por ejemplo:
resultado1= d1 / d2;
resultado2= i1 / i2;
double resultado;
int i1=7,i2=2;
resultado=i1/i2; //Resultado valdrá 3
resultado=(double)i1/(double)i2; //Resultado valdrá 3.5
El operador del módulo (%) para calcular el resto de una división entera. Ejemplo:
operadores condicionales
Sirven para comparar valores. Siempre devuelven valores booleanos. Son:
operador significado
< Menor
> Mayor
>= Mayor o igual
<= Menor o igual
== Igual
19
Manual de Java
variables
operador significado
!= Distinto
! No lógico (NOT)
&& “Y” lógico (AND)
|| “O” lógico (OR)
Los operadores lógicos (AND, OR y NOT), sirven para evaluar condiciones complejas.
NOT sirve para negar una condición. Ejemplo:
El operador && (AND) sirve para evaluar dos expresiones de modo que si ambas son
ciertas, el resultado será true sino el resultado será false. Ejemplo:
boolean carnetConducir=true;
int edad=20;
boolean puedeConducir= (edad>=18) && carnetConducir;
//Si la edad es de al menos 18 años y carnetConducir es
//true, puedeConducir es true
El operador || (OR) sirve también para evaluar dos expresiones. El resultado será true
si al menos uno de las expresiones es true. Ejemplo:
operadores de BIT
Manipulan los bits de los números. Son:
operador significado
& AND
| OR
~ NOT
^ XOR
>> Desplazamiento a la derecha
<< Desplazamiento a la izquierda
>>> Desplazamiento derecha con relleno de ceros
<<< Desplazamiento izquierda con relleno de ceros
20
© Jorge Sánchez Asenjo’ 2004
operadores de asignación
Permiten asignar valores a una variable. El fundamental es “=”. Pero sin embargo se
pueden usar expresiones más complejas como:
x += 3;
En el ejemplo anterior lo que se hace es sumar 3 a la x (es lo mismo x+=3, que x=x+3).
Eso se puede hacer también con todos estos operadores:
+= -= *= /=
&= |= ^= %=
>>= <<=
x1 = x2 = x3 = 5;
Pero hay dos formas de utilizar el incremento y el decremento. Se puede usar por
ejemplo x++ o ++x
La diferencia estriba en el modo en el que se comporta la asignación. Ejemplo:
operador ?
Este operador (conocido como if de una línea) permite ejecutar una instrucción u otra
según el valor de la expresión. Sintaxis:
expresionlogica?valorSiVerdadero:valorSiFalso;
Ejemplo:
paga=(edad>18)?6000:3000;
En este caso si la variable edad es mayor de 18, la paga será de 6000, sino será de 3000.
Se evalúa una condición y según es cierta o no se devuelve un valor u otro. Nótese que
esta función ha de devolver un valor y no una expresión correcta. Es decir, no
funcionaría:
21
Manual de Java
variables
precedencia
A veces hay expresiones con operadores que resultan confusas. Por ejemplo en:
resultado = 8 + 4 / 2;
Es difícil saber el resultado. ¿Cuál es? ¿seis o diez? La respuesta es 10 y la razón es que
el operador de división siempre precede en el orden de ejecución al de la suma. Es decir,
siempre se ejecuta antes la división que la suma. Siempre se pueden usar paréntesis
para forzar el orden deseado:
resultado = (8 + 4) / 2;
En la tabla anterior los operadores con mayor precedencia está en la parte superior, los
de menor precedencia en la parte inferior. De izquierda a derecha la precedencia es la
misma. Es decir, tiene la misma precedencia el operador de suma que el de resta.
Esto último provoca conflictos, por ejemplo en:
resultado = 9 / 3 * 3;
El resultado podría ser uno ó nueve. En este caso el resultado es nueve, porque la
división y el producto tienen la misma precedencia; por ello el compilador de Java
realiza primero la operación que este más a la izquierda, que en este caso es la división.
Una vez más los paréntesis podrían evitar estos conflictos.
22
© Jorge Sánchez Asenjo’ 2004
la clase Math
Se echan de menos operadores matemáticos más potentes en Java. Por ello se ha
incluido una clase especial llamada Math dentro del paquete java.lang.Para poder
utilizar esta clase, se debe incluir esta instrucción:
import java.lang.Math;
Esta clase posee métodos muy interesantes para realizar cálculos matemáticos
complejos. Por ejemplo:
constante significado
double E El número e (2, 7182818245...)
double PI El número ∏ (3,14159265...)
operador significado
double ceil(double x) Redondea x al entero mayor siguiente:
Math.ceil(2.8) vale 3
Math.ceil(2.4) vale 3
Math.ceil(-2.8) vale -2
double floor(double x) Redondea x al entero menor siguiente:
Math.floor(2.8) vale 2
Math.round(2.8) vale 3
23
Manual de Java
variables
operador significado
tiponúmero min( Devuelve el menor valor de x o y
tiponúmero x,
tiponúmero y)
tiponúmero max( Devuelve el mayor valor de x o y
tiponúmero x,
tiponúmero y)
double sqrt(double x) Calcula la raíz cuadrada de x
double pow(double x, Calcula xy
double y)
double exp(double x) Calcula ex
double log(double x) Calcula el logaritmo neperiano de x
double acos(double x) Calcula el arco coseno de x
double asin(double x) Calcula el arco seno de x
double atan(double x) Calcula el arco tangente de x
double sin(double x) Calcula el seno de x
double cos(double x) Calcula el coseno de x
double tan(double x) Calcula la tangente de x
double toDegrees(double Convierte de radianes a grados
anguloEnRadianes)
double toRadians(double Convierte de grados a radianes
anguloEnGrados)
24
estructuras de control del flujo
if
Permite crear estructuras condicionales simples; en las que al cumplirse una condición
se ejecutan una serie de instrucciones. Se puede hacer que otro conjunto de
instrucciones se ejecute si la condición es falsa. La condición es cualquier expresión que
devuelva un resultado de true o false. La sintaxis de la instrucción if es:
if (condición) {
instrucciones que se ejecutan si la condición es true
}
else {
instrucciones que se ejecutan si la condición es false
}
if (diasemana==1) dia=”Lunes”;
else if (diasemana==2) dia=”Martes”;
else if (diasemana==3) dia=”Miércoles”;
else if (diasemana==4) dia=”Jueves”;
else if (diasemana==5) dia=”Viernes”;
else if (diasemana==6) dia=”Sábado”;
else if (diasemana==7) dia=”Domingo”;
else dia=”?”;
switch
Es la estructura condicional compleja porque permite evaluar varios valores a la vez.
Sintaxis:
switch (expresión) {
case valor1:
sentencias si la expresiona es igual al valor1;
25
Manual de Java
Estructuras de control del flujo
[break]
case valor2:
sentencias si la expresiona es igual al valor2;
[break]
.
.
.
default:
sentencias que se ejecutan si no se cumple ninguna
de las anteriores
}
Esta instrucción evalúa una expresión (que debe ser short, int, byte o char), y según
el valor de la misma ejecuta instrucciones. Cada case contiene un valor de la expresión;
si efectivamente la expresión equivale a ese valor, se ejecutan las instrucciones de ese
case y de los siguientes.
La instrucción break se utiliza para salir del switch. De tal modo que si queremos
que para un determinado valor se ejecuten las instrucciones de un apartado case y sólo
las de ese apartado, entonces habrá que finalizar ese case con un break.
El bloque default sirve para ejecutar instrucciones para los casos en los que la
expresión no se ajuste a ningún case.
Ejemplo 1:
switch (diasemana) {
case 1:
dia=”Lunes”;
break;
case 2:
dia=”Martes”;
break;
case 3:
dia=”Miércoles”;
break;
case 4:
dia=”Jueves”;
break;
case 5:
dia=”Viernes”;
break;
case 6:
dia=”Sábado”;
break;
case 7:
dia=”Domingo”;
26
© Jorge Sánchez Asenjo’ 2004
break;
default:
dia=”?”;
}
Ejemplo 2:
switch (diasemana) {
case 1:
case 2:
case 3:
case 4:
case 5:
laborable=true;
break;
case 6:
case 7:
laborable=false;
}
while
La instrucción while permite crear bucles. Un bucle es un conjunto de sentencias que
se repiten si se cumple una determinada condición. Los bucles while agrupan
instrucciones las cuales se ejecutan continuamente hasta que una condición que se
evalúa sea falsa.
La condición se mira antes de entrar dentro del while y cada vez que se termina de
ejecutar las instrucciones del while
Sintaxis:
while (condición) {
sentencias que se ejecutan si la condición es true
}
//factorial de 4
int n=4, factorial=1, temporal=n;
while (temporal>0) {
factorial*=temporal--;
}
27
Manual de Java
Estructuras de control del flujo
do while
Crea un bucle muy similar al anterior, en la que también las instrucciones del bucle se
ejecutan hasta que una condición pasa a ser falsa. La diferencia estriba en que en este
tipo de bucle la condición se evalúa después de ejecutar las instrucciones; lo cual
significa que al menos el bucle se ejecuta una vez. Sintaxis:
do {
instrucciones
} while (condición)
for
Es un bucle más complejo especialmente pensado para rellenar arrays o para ejecutar
instrucciones controladas por un contador. Una vez más se ejecutan una serie de
instrucciones en el caso de que se cumpla una determinada condición. Sintaxis:
La expresión inicial es una instrucción que se ejecuta una sola vez: al entrar por
primera vez en el bucle for (normalmente esa expresión lo que hace es dar valor inicial
al contador del bucle).
La condición es cualquier expresión que devuelve un valor lógico. En el caso de que
esa expresión sea verdadera se ejecutan las instrucciones. Cuando la condición pasa a
ser falsa, el bucle deja de ejecutarse. La condición se valora cada vez que se terminan de
ejecutar las instrucciones del bucle.
Después de ejecutarse las instrucciones interiores del bucle, se realiza la expresión
que tiene lugar tras ejecutarse las instrucciones del bucle (que, generalmente,
incrementa o decrementa al contador). Luego se vuelve a evaluar la condición y así
sucesivamente hasta que la condición sea falsa.
Ejemplo (factorial):
//factorial de 4
int n=4, factorial=1, temporal=n;
for (temporal=n;temporal>0;temporal--){
factorial *=temporal;
}
break
Es una sentencia que permite salir del bucle en el que se encuentra inmediatamente.
Hay que intentar evitar su uso ya que produce malos hábitos al programar.
28
© Jorge Sánchez Asenjo’ 2004
continue
Instrucción que siempre va colocada dentro de un bucle y que hace que el flujo del
programa ignore el resto de instrucciones del bucle; dicho de otra forma, va hasta la
siguiente iteración del bucle. Al igual que ocurría con break, hay que intentar evitar su
uso.
29
arrays y cadenas
arrays
unidimensionales
Un array es una colección de valores de un mismo tipo engrosados en la misma variable.
De forma que se puede acceder a cada valor independientemente. Para Java además un
array es un objeto que tiene propiedades que se pueden manipular.
Los arrays solucionan problemas concernientes al manejo de muchas variables que
se refieren a datos similares. Por ejemplo si tuviéramos la necesidad de almacenar las
notas de una clase con 18 alumnos, necesitaríamos 18 variables, con la tremenda
lentitud de manejo que supone eso. Solamente calcular la nota media requeriría una
tremenda línea de código. Almacenar las notas supondría al menos 18 líneas de código.
Gracias a los arrays se puede crear un conjunto de variables con el mismo nombre.
La diferencia será que un número (índice del array) distinguirá a cada variable.
En el caso de las notas, se puede crear un array llamado notas, que representa a
todas las notas de la clase. Para poner la nota del primer alumno se usaría notas[0], el
segundo sería notas[1], etc. (los corchetes permiten especificar el índice en concreto del
array).
La declaración de un array unidimensional se hace con esta sintaxis.
tipo nombre[];
Ejemplo:
Declara un array de tipo double. Esta declaración indica para qué servirá el array, pero
no reserva espacio en la RAM al no saberse todavía el tamaño del mismo.
Tras la declaración del array, se tiene que iniciar. Eso lo realiza el operador new,
que es el que realmente crea el array indicando un tamaño. Cuando se usa new es
cuando se reserva el espacio necesario en memoria. Un array no inicializado es un array
null. Ejemplo:
En el ejemplo anterior se crea un array de tres enteros (con los tipos básicos se crea en
memoria el array y se inicializan los valores, los números se inician a 0).
31
Manual de Java
Arrays y cadenas
Los valores del array se asignan utilizando el índice del mismo entre corchetes:
notas[2]=8;
Esto declara e inicializa un array de tres elementos. En el ejemplo lo que significa es que
notas[0] vale 8, notas[1] vale 7 y notas[2] vale 9.
En Java (como en otros lenguajes) el primer elemento de un array es el cero. El
primer elemento del array notas, es notas[0]. Se pueden declarar arrays a cualquier tipo
de datos (enteros, booleanos, doubles, ... e incluso objetos).
La ventaja de usar arrays (volviendo al caso de las notas) es que gracias a un simple
bucle for se puede rellenar o leer fácilmente todos los elementos de un array:
int notas[];
int ejemplo[]=new int[18];
notas=ejemplo;
En el último punto, notas equivale a ejemplo. Esta asignación provoca que cualquier
cambio en notas también cambie el array ejemplos. Es decir esta asignación anterior, no
copia los valores del array, sino que notas y ejemplo son referencias al mismo array.
Ejemplo:
int notas[]={3,3,3};
int ejemplo[]=notas;
ejemplo= notas;
32
© Jorge Sánchez Asenjo’ 2004
ejemplo[0]=8;
System.out.println(notas[0]);//Escribirá el número 8
arrays multidimensionales
Los arrays además pueden tener varias dimensiones. Entonces se habla de arrays de
arrays (arrays que contienen arrays) Ejemplo:
int notas[][];
Hay que tener en cuenta que en el ejemplo anterior, notas[0] es un array de 100
enteros. Mientras que notas, es un array de 5 arrays de enteros.
Se pueden utilizar más de dos dimensiones si es necesario.
longitud de un array
Los arrays poseen un método que permite determinar cuánto mide un array. Se trata de
length. Ejemplo (continuando del anterior):
System.out.println(notas.length); //Sale 5
System.out.println(notas[2].length); //Sale 400
la clase Arrays
En el paquete java.utils se encuentra una clase estática llamada Arrays. Una clase
estática permite ser utilizada como si fuera un objeto (como ocurre con Math). Esta
clase posee métodos muy interesantes para utilizar sobre arrays.
Su uso es
Arrays.método(argumentos);
33
Manual de Java
Arrays y cadenas
fill
Permite rellenar todo un array unidimensional con un determinado valor. Sus
argumentos son el array a rellenar y el valor deseado:
También permite decidir desde que índice hasta qué índice rellenamos:
equals
Compara dos arrays y devuelve true si son iguales. Se consideran iguales si son del
mismo tipo, tamaño y contienen los mismos valores.
sort
Permite ordenar un array en orden ascendente. Se pueden ordenar sólo una serie de
elementos desde un determinado punto hasta un determinado punto.
int x[]={4,5,2,3,7,8,2,3,9,5};
Arrays.sort(x);//Estará ordenado
Arrays.sort(x,2,5);//Ordena del 2º al 4º elemento
binarySearch
Permite buscar un elemento de forma ultrarrápida en un array ordenado (en un array
desordenado sus resultados son impredecibles). Devuelve el índice en el que está
colocado el elemento. Ejemplo:
int x[]={1,2,3,4,5,6,7,8,9,10,11,12};
Arrays.sort(x);
System.out.println(Arrays.binarySearch(x,8));//Da 7
el método System.arraysCopy
La clase System también posee un método relacionado con los arrays, dicho método
permite copiar un array en otro. Recibe cinco argumentos: el array que se copia, el
índice desde que se empieza a copia en el origen, el array destino de la copia, el índice
desde el que se copia en el destino, y el tamaño de la copia (número de elementos de la
copia).
int uno[]={1,1,2};
int dos[]={3,3,3,3,3,3,3,3,3};
System.arraycopy(uno, 0, dos, 0, uno.length);
for (int i=0;i<=8;i++){
System.out.print(dos[i]+" ");
} //Sale 112333333
34
© Jorge Sánchez Asenjo’ 2004
clase String
introducción
Para Java las cadenas de texto son objetos especiales. Los textos deben manejarse
creando objetos de tipo String. Ejemplo:
Las cadenas pueden ocupar varias líneas utilizando el operador de concatenación “+”.
También se pueden crear objetos String sin utilizar constantes entrecomilladas, usando
otros constructores:
En el último ejemplo la cadena codificada se crea desde un array de tipo byte que
contiene números que serán interpretados como códigos Unicode. Al asignar, el valor
8859_1 indica la tabla de códigos a utilizar.
35
Manual de Java
Arrays y cadenas
String.valueOf
Este método pertenece no sólo a la clase String, sino a otras y siempre es un método que
convierte valores de una clase a otra. En el caso de los objetos String, permite convertir
valores que no son de cadena a forma de cadena. Ejemplos:
length
Permite devolver la longitud de una cadena (el número de caracteres de la cadena):
String texto1=”Prueba”;
System.out.println(texto1.length());//Escribe 6
concatenar cadenas
Se puede hacer de dos formas, utilizando el método concat o con el operador +.
Ejemplo:
charAt
Devuelve un carácter de la cadena. El carácter a devolver se indica por su posición (el
primer carácter es la posición 0) Si la posición es negativa o sobrepasa el tamaño de la
cadena, ocurre un error de ejecución, una excepción tipo IndexOutOfBounds-
Exception. Ejemplo:
String s1=”Prueba”;
char c1=s1.charAt(2); //c1 valdrá ‘u’
substring
Da como resultado una porción del texto de la cadena. La porción se toma desde una
posición inicial hasta una posición final (sin incluir esa posición final). Si las posiciones
indicadas no son válidas ocurre una excepción de tipo IndexOutOfBounds-
Exception. Se empieza a contar desde la posición 0. Ejemplo:
36
© Jorge Sánchez Asenjo’ 2004
indexOf
Devuelve la primera posición en la que aparece un determinado texto en la cadena. En
el caso de que la cadena buscada no se encuentre, devuelve -1. El texto a buscar puede
ser char o String. Ejemplo:
System.out.println(s1.indexOf(“que”,16)); //Ahora da 26
lastIndexOf
Devuelve la última posición en la que aparece un determinado texto en la cadena. Es
casi idéntica a la anterior, sólo que busca desde el final. Ejemplo:
endsWith
Devuelve true si la cadena termina con un determinado texto. Ejemplo:
startsWith
Devuelve true si la cadena empieza con un determinado texto.
replace
Cambia todas las apariciones de un carácter por otro en el texto que se indique y lo
almacena como resultado. El texto original no se cambia, por lo que hay que asignar el
resultado de replace a un String para almacenar el texto cambiado:
String s1=”Mariposa”;
System.out.println(s1.replace(‘a’,’e’));//Da Meripose
System.out.println(s1);//Sigue valiendo Mariposa
replaceAll
Modifica en un texto cada entrada de una cadena por otra y devuelve el resultado. El
primer parámetro es el texto que se busca (que puede ser una expresión regular), el
segundo parámetro es el texto con el que se reemplaza el buscado. La cadena original no
se modifica.
37
Manual de Java
Arrays y cadenas
toUpperCase
Devuelve la versión en mayúsculas de la cadena.
toLowerCase
Devuelve la versión en minúsculas de la cadena.
toCharArray
Obtiene un array de caracteres a partir de una cadena.
38
© Jorge Sánchez Asenjo’ 2004
método descripción
int lastIndexOf(String s, int primeraPos) Devuelve la última posición en la cadena del
texto s, empezando a buscar desde la
posición PrimeraPos
int length() Devuelve la longitud de la cadena
String replace(char carAnterior, char Devuelve una cadena idéntica al original
ncarNuevo) pero que ha cambiando los caracteres
iguales a carAnterior por carNuevo
String replaceFirst(String str1, String Cambia la primera aparición de la cadena
str2) str1 por la cadena str2
String replaceFirst(String str1, String Cambia la primera aparición de la cadena
str2) uno por la cadena dos
String replaceAll(String str1, String Cambia la todas las apariciones de la cadena
str2) uno por la cadena dos
String startsWith(String s) Devuelve true si la cadena comienza con el
texto s.
String substring(int primeraPos, int Devuelve el texto que va desde primeraPos a
segundaPos) segunaPos.
char[] toCharArray() Devuelve un array de caracteres a partir de
la cadena dada
String toLowerCase() Convierte la cadena a minúsculas
String toLowerCase(Locale local) Lo mismo pero siguiendo las instrucciones
del argumento local
String toUpperCase() Convierte la cadena a mayúsculas
String toUpperCase(Locale local) Lo mismo pero siguiendo las instrucciones
del argumento local
String trim() Elimina los blancos que tenga la cadena
tanto por delante como por detrás
Static String valueOf(tipo elemento) Devuelve la cadena que representa el valor
elemento. Si elemento es booleano, por
ejemplo devolvería una cadena con el valor
true o false
39
objetos y clases
propiedades de la POO
Ocultación. Hay una zona oculta al definir la clases (zona privada) que sólo es
utilizada por esa clases y por alguna clase relacionada. Hay una zona pública
(llamada también interfaz de la clase) que puede ser utilizada por cualquier
parte del código.
41
Manual de Java
Objetos y clases
clases
Las clases son las plantillas para hacer objetos. Una clase sirve para definir una serie de
objetos con propiedades (atributos), comportamientos (operaciones o métodos), y
semántica comunes. Hay que pensar en una clase como un molde. A través de las clases
se obtienen los objetos en sí.
Es decir antes de poder utilizar un objeto se debe definir la clase a la que pertenece,
esa definición incluye:
Sus atributos. Es decir, los datos miembros de esa clase. Los datos pueden ser
públicos (accesibles desde otra clase) o privados (sólo accesibles por código de su
propia clase. También se las llama campos.
Código de inicialización. Para crear una clase normalmente hace falta realizar
operaciones previas (es lo que se conoce como el constructor de la clase).
Otras clases. Dentro de una clase se pueden definir otras clases (clases internas,
son consideradas como asociaciones dentro de UML).
Nombre de clase
Atributos
Métodos
Ilustración 5, Clase en notación UML
42
© Jorge Sánchez Asenjo’ 2004
La palabra opcional static sirve para hacer que el método o la propiedad a la que
precede se pueda utilizar de manera genérica (más adelante se hablará de clases
genéricas), los métodos o propiedades así definidos se llaman atributos de clase y
métodos de clase respectivamente. Su uso se verá más adelante. Ejemplo;
class Noria {
double radio;
void girar(int velocidad){
...//definición del método
}
void parar(){...
}
Noria
radio:double
parar()
girar(int)
Ilustración 6, Clase Noria
bajo notación UML
43
Manual de Java
Objetos y clases
objetos
Se les llama instancias de clase. Son un elemento en sí de la clase (en el ejemplo del
parchís, una ficha en concreto). Un objeto se crea utilizando el llamado constructor de
la clase. El constructor es el método que permite iniciar el objeto.
objeto.atributo
Por ejemplo:
Noria.radio;
métodos
Los métodos se utilizan de la misma forma que los atributos, excepto porque los
métodos poseen siempre paréntesis, dentro de los cuales pueden ir valore snecesarios
para la ejecución del método (parámetros):
objeto.método(argumentosDelMétodo)
Los métodos siempre tienen paréntesis (es la diferencia con las propiedades) y dentro
de los paréntesis se colocan los argumentos del método. Que son los datos que necesita
el método para funcionar. Por ejemplo:
MiNoria.gira(5);
herencia
En la POO tiene mucha importancia este concepto, la herencia es el mecanismo que
permite crear clases basadas en otras existentes. Se dice que esas clases descienden de
las primeras. Así por ejemplo, se podría crear una clase llamada vehículo cuyos
métodos serían mover, parar, acelerar y frenar. Y después se podría crear una clase
coche basada en la anterior que tendría esos mismos métodos (les heredaría) y además
añadiría algunos propios, por ejemplo abrirCapó o cambiarRueda.
Noria noriaDePalencia;
Eso declara el objeto noriaDePalencia como objeto de tipo Noria; se supone que
previamente se ha definido la clase Noria.
44
© Jorge Sánchez Asenjo’ 2004
Para poder utilizar un objeto, hay que crearle de verdad. Eso consiste en utilizar el
operador new. Por ejemplo:
Al hacer esta operación el objeto reserva la memoria que necesita y se inicializa el objeto
mediante su constructor. Más adelante veremos como definir el constructor.
NoriaDePalencia:Noria
especificadores de acceso
Se trata de una palabra que antecede a la declaración de una clase, método o propiedad
de clase. Hay tres posibilidades: public, protected y private. Una cuarta posibilidad
es no utilizar ninguna de estas tres palabras; entonces se dice que se ha utilizado el
modificador por defecto (friendly).
Los especificadores determinan el alcance de la visibilidad del elemento al que se
refieren. Referidos por ejemplo a un método, pueden hacer que el método sea visible
sólo para la clase que lo utiliza (private), para éstas y las heredadas (protected), para
todas las clases del mismo paquete (friendly) o para cualquier clase del tipo que sea
(public).
En la siguiente tabla se puede observar la visibilidad de cada especificador:
sin
private modificador protected public
Misma clase X X X X
Subclase en el X X X
mismo paquete
Clase (no X X
subclase) en el
mismo paquete
Subclase en otro X X
paquete
No subclase en X
otro paquete
45
Manual de Java
Objetos y clases
creación de clases
Ejemplo:
class Persona {
public String nombre;//Se puede acceder desde cualquier clase
private int contraseña;//Sólo se puede acceder desde la
//clase Persona
protected String dirección; //Acceden a esta propiedad
//esta clase y sus descendientes
Por lo general las propiedades de una clase suelen ser privadas o protegidas, a no ser
que se trate de un valor constante, en cuyo caso se declararán como públicos.
Las variables locales de una clase pueden ser inicializadas.
class auto{
public nRuedas=4;
Persona
+nombre:String
-contraseña:String
#direccion:String
46
© Jorge Sánchez Asenjo’ 2004
Los métodos de la clase se definen dentro de ésta. Hay que indicar un modificador de
acceso (public, private, protected o ninguno, al igual que ocurre con las variables y
con la propia clase) y un tipo de datos, que indica qué tipo de valores devuelve el
método.
Esto último se debe a que los métodos son funciones que pueden devolver un
determinado valor (un entero, un texto, un valor lógico,...) mediante el comando
return. Si el método no devuelve ningún valor, entonces se utiliza el tipo void que
significa que no devuelve valores (en ese caso el método no tendrá instrucción return).
El último detalle a tener en cuenta es que los métodos casi siempre necesitan datos
para realizar la operación, estos datos van entre paréntesis y se les llama argumentos. Al
definir el método hay que indicar que argumentos se necesitan y de qué tipo son.
Ejemplo:
47
Manual de Java
Objetos y clases
En la clase anterior, los métodos acelerar y frenar son de tipo void por eso no tienen
sentencia return. Sin embargo el método obtenerVelocidad es de tipo double por lo
que su resultado es devuelto por la sentencia return y puede ser escrito en pantalla.
Coche
ruedas:int
-velocidad:double=0
#direccion:String
nombre:String
+acelerar(double)
+frenar(double)
+obtenerVelocidad():double
Ilustración 9, Versión UML de la clase
Coche
48
© Jorge Sánchez Asenjo’ 2004
Se dice que los argumentos son por valor, si la función recibe una copia de esos
datos, es decir la variable que se pasa como argumento no estará afectada por el código.
Ejemplo:
class prueba {
public void metodo1(int entero){
entero=18;
...
}
...
public static void main(String args[]){
int x=24;
prueba miPrueba = new prueba();
miPrueba.metodo1(x);
System.out.println(x); //Escribe 24, no 18
}
class prueba {
public void metodo1(int[] entero){
entero[0]=18;
...
}
...
public static void main(String args[]){
int x[]={24,24};
prueba miPrueba = new prueba();
miPrueba.metodo1(x);
System.out.println(x[0]); //Escribe 18, no 24
49
Manual de Java
Objetos y clases
devolución de valores
Los métodos pueden devolver valores básicos (int, short, double, etc.), Strings, arrays e
incluso objetos.
En todos los casos es el comando return el que realiza esta labor. En el caso de
arrays y objetos, devuelve una referencia a ese array u objeto. Ejemplo:
class FabricaArrays {
public int[] obtenArray(){
int array[]= {1,2,3,4,5};
return array;
}
}
sobrecarga de métodos
Una propiedad de la POO es el polimorfismo. Java posee esa propiedad ya que admite
sobrecargar los métodos. Esto significa crear distintas variantes del mismo método.
Ejemplo:
class Matemáticas{
public double suma(double x, double y) {
return x+y;
}
public double suma(double x, double y, double z){
return x+y+z;
}
public double suma(double[] array){
double total =0;
for(int i=0; i<array.length;i++){
total+=array[i];
}
return total;
}
La clase matemáticas posee tres versiones del método suma. una versión que suma dos
números double, otra que suma tres y la última que suma todos los miembros de un
array de doubles. Desde el código se puede utilizar cualquiera de las tres versiones
según convenga.
50
© Jorge Sánchez Asenjo’ 2004
la referencia this
La palabra this es una referencia al propio objeto en el que estamos. Ejemplo:
class punto {
int posX, posY;//posición del punto
punto(posX, posY){
this.posX=posX;
this.posY=posY;
}
En el ejemplo hace falta la referencia this para clarificar cuando se usan las
propiedades posX y posY, y cuando los argumentos con el mismo nombre. Otro
ejemplo:
class punto {
int posX, posY;
...
/**Suma las coordenadas de otro punto*/
public void suma(punto punto2){
posX = punto2.posX;
posY = punto2.posY;
}
this. Referencia al objeto actual. Se usa por ejemplo pasarle como parámetro a un
método cuando es llamado desde la propia clase.
51
Manual de Java
Objetos y clases
creación de constructores
Un constructor es un método que es llamado automáticamente al crear un objeto de una
clase, es decir al usar la instrucción new. Sin embargo en ninguno de los ejemplos
anteriores se ha definido constructor alguno, por eso no se ha utilizado ningún
constructor al crear el objeto.
Un constructor no es más que un método que tiene el mismo nombre que la clase.
Con lo cual para crear un constructor basta definir un método en el código de la clase
que tenga el mismo nombre que la clase. Ejemplo:
class Ficha {
private int casilla;
Ficha() { //constructor
casilla = 1;
}
public void avanzar(int n) {
casilla += n;
}
public int casillaActual(){
return casilla;
}
}
En la línea Ficha ficha1 = new Ficha(); es cuando se llama al constructor, que es el que
coloca inicialmente la casilla a 1. Pero el constructor puede tener parámetros:
class Ficha {
private int casilla; //Valor inicial de la propiedad
Ficha(int n) { //constructor
casilla = n;
}
public void avanzar(int n) {
casilla += n;
}
public int casillaActual(){
return casilla;
}
}
52
© Jorge Sánchez Asenjo’ 2004
En este otro ejemplo, al crear el objeto ficha1, se le da un valor a la casilla, por lo que la
casilla vale al principio 6.
Hay que tener en cuenta que puede haber más de un constructor para la misma
clase. Al igual que ocurría con los métodos, los constructores se pueden sobrecargar.
De este modo en el código anterior de la clase Ficha se podrían haber colocado los
dos constructores que hemos visto, y sería entonces posible este código:
Clase
Hemos visto que hay que crear objetos para poder utilizar los métodos y propiedades de
una determinada clase. Sin embargo esto no es necesario si la propiedad o el método se
definen precedidos de la palabra clave static. De esta forma se podrá utilizar el método
sin definir objeto alguno, utilizando el nombre de la clase como si fuera un objeto. Así
funciona la clase Math (véase la clase Math, página 23). Ejemplo:
53
Manual de Java
Objetos y clases
class Calculadora {
static public int factorial(int n) {
int fact=1;
while (n>0) {
fact *=n--;
}
return fact;
}
}
public class app {
public static void main(String[] args) {
System.out.println(Calculadora.factorial(5));
}
}
En este ejemplo no ha hecho falta crear objeto alguno para poder calcular el factorial.
Una clase puede tener métodos y propiedades genéricos (static) y métodos y
propiedades dinámicas (normales).
Cada vez que se crea un objeto con new, se almacena éste en memoria. Los métodos
y propiedades normales, gastan memoria por cada objeto que se cree, sin embargo los
métodos estáticos no gastan memoria por cada objeto creado, gastan memoria al definir
la clase sólo. Es decir los métodos y atributos static son los mismos para todos los
objetos creados, gastan por definir la clase, pero no por crear cada objeto.
Hay que crear métodos y propiedades genéricos cuando ese método o propiedad vale
o da el mismo resultado en todos los objetos. Pero hay que utilizar métodos normales
(dinámicos) cuando el método da resultados distintos según el objeto. Por ejemplo en
un clase que represente aviones, la altura sería un atributo dinámico (distinto en cada
objeto), mientras que el número total de aviones, sería un método static (es el mismo
para todos los aviones).
el método main
Hasta ahora hemos utilizado el método main de forma incoherente como único posible
mecanismo para ejecutar programas. De hecho este método dentro de una clase, indica
que la clase es ejecutable desde la consola. Su prototipo es:
Hay que tener en cuenta que el método main es estático, por lo que no podrá utilizar
atributos o métodos dinámicos de la clase.
Los argumentos del método main son un array de caracteres donde cada elemento
del array es un parámetro enviado por el usuario desde la línea de comandos. A este
argumento se le llama comúnmente args. Es decir, si se ejecuta el programa con:
54
© Jorge Sánchez Asenjo’ 2004
Entonces el método main de esta clase recibe un array con dos elementos, el primero es
la cadena “uno” y el segundo la cadena “dos” (es decir args[0]=”uno”; args[1]=”dos”).
destrucción de objetos
En C y C++ todos los programadores saben que los objetos se crean con new y para
eliminarles de la memoria y así ahorrarla, se deben eliminar con la instrucción delete.
Es decir, es responsabilidad del programador eliminar la memoria que gastaban los
objetos que se van a dejar de usar. La instrucción delete del C++ llama al destructor de
la clase, que es una función que se encarga de eliminar adecuadamente el objeto.
La sorpresa de los programadores C++ que empiezan a trabajar en Java es que no
hay instrucción delete en Java. La duda está entonces, en cuándo se elimina la
memoria que ocupa un objeto.
En Java hay un recolector de basura (garbage collector) que se encarga de
gestionar los objetos que se dejan de usar y de eliminarles de memoria. Este proceso es
automático e impredecible y trabajo en un hilo (thread) de baja prioridad.
Por lo general ese proceso de recolección de basura, trabaja cuando detecta que un
objeto hace demasiado tiempo que no se utiliza en un programa. Esta eliminación
depende de la máquina virtual, en casi todas la recolección se realiza periódicamente en
un determinado lapso de tiempo. La implantación de máquina virtual conocida como
HotSpot1 suele hacer la recolección mucho más a menudo
Se puede forzar la eliminación de un objeto asignándole el valor null, pero teniendo
en cuenta que eso no equivale al famoso delete del lenguaje C++. Con null no se libera
inmediatamente la memoria, sino que pasará un cierto tiempo (impredecible, por otro
lado) hasta su total destrucción.
Se puede invocar al recolector de basura desde el código invocando al método
estático System.gc(). Esto hace que el recolector de basura trabaje en cuanto se lea
esa invocación.
Sin embargo puede haber problemas al crear referencias circulares. Como:
class uno {
dos d;
uno() { //constructor
d = new dos();
}
}
class dos {
uno u;
dos() {
u = new uno();
}
55
Manual de Java
Objetos y clases
Al crear un objeto de clase uno, automáticamente se crea uno de la clase dos, que al
crearse creará otro de la clase uno. Eso es un error que provocará que no se libere bien
la memoria salvo que se eliminen previamente los objetos referenciados.
el método finalize
Es equivalente a los destructores del C++. Es un método que es llamado antes de
eliminar definitivamente al objeto para hacer limpieza final. Un uso puede ser eliminar
los objetos creados en la clase para eliminar referencias circulares. Ejemplo:
class uno {
dos d;
uno() {
d = new dos();
}
protected void finalize(){
d = null;//Se elimina d por lo que pudiera pasar
}
}
finalize es un método de tipo protected heredado por todas las clases ya que está
definido en la clase raíz Object.
La diferencia de finalize respecto a los métodos destructores de C++ estriba en que
en Java no se llaman al instante (de hecho es imposible saber cuando son llamados). la
llamada System.gc() llama a todos los finalize pendientes inmediatamente (es una
forma de probar si el método finalize funciona o no).
56
reutilización de clases
herencia
introducción
Es una de las armas fundamentales de la programación orientada a objetos. Permite
crear nuevas clases que heredan características presentas en clases anteriores. Esto
facilita enormemente el trabajo porque ha permitido crear clases estándar para todos
los programadores y a partir de ellas crear nuestras propias clases personales. Esto es
más cómodo que tener que crear nuestras clases desde cero.
Para que una clase herede las características de otra hay que utilizar la palabra clave
extends tras el nombre de la clase. A esta palabra le sigue el nombre de la clase cuyas
características se heredarán. Sólo se puede tener herencia de una clase (a la clase de la
que se hereda se la llama superclase y a la clase heredada se la llama subclase).
Ejemplo:
superclase
vehículo
+ruedas:int;
+velocidad:double
heredado +acelerar(int)
+frenar(int)
subclase
coche
redefinido +ruedas:int=4
+gasolina:int
propio
+repostar(int)
57
Manual de Java
Reutilización de clases
Ejemplo:
class vehiculo {
public int velocidad;
public int ruedas;
public void parar() {
velocidad = 0;
}
public void acelerar(int kmh) {
velocidad += kmh;
}
anulación de métodos
Como se ha visto, las subclases heredan los métodos de las superclases. Pero es más,
también los pueden sobrecargar para proporcionar una versión de un determinado
método.
Por último, si una subclase define un método con el mismo nombre, tipo y
argumentos que un método de la superclase, se dice entonces que se sobrescribe o anula
el método de la superclase. Ejemplo:
58
© Jorge Sánchez Asenjo’ 2004
Animal
comer()
dormir()
reproducir()
toby:Perro
comer()
Mamífero reproducir()
reproducir() dormir()
anula ladrar()
grunir()
Perro
dormir()
anula ladrar()
grunir()
super
A veces se requiere llamar a un método de la superclase. Eso se realiza con la palabra
reservada super. Si this hace referencia a la clase actual, super hace referencia a la
superclase respecto a la clase actual, con lo que es un método imprescindible para poder
acceder a métodos anulados por herencia. Ejemplo
59
Manual de Java
Reutilización de clases
en la clase coche ya que aunque la velocidad varía igual que en la superclase, hay que
tener en cuenta el consumo de gasolina
Se puede incluso llamar a un constructor de una superclase, usando la sentencia
super(). Ejemplo:
casting de clases
Como ocurre con los tipos básicos (ver conversión entre tipos (casting), página 18, es
posible realizar un casting de objetos para convertir entre clases distintas. Lo que
ocurre es que sólo se puede realizar este casting entre subclases. Es decir se realiza un
casting para especificar más una referencia de clase (se realiza sobre una superclase
para convertirla a una referencia de una subclase suya).
En cualquier otro caso no se puede asignar un objeto de un determinado tipo a otro.
60
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
Hay que tener en cuenta que los objetos nunca cambian de tipo, se les prepara para su
asignación pero no pueden acceder a propiedades o métodos que no les sean propios.
Por ejemplo, si repostar() es un método de la clase coche y no de vehículo:
Cuando se fuerza a realizar un casting entre objetos, en caso de que no se pueda realizar
ocurrirá una excepción del tipo ClassCastingException. Realmente sólo se puede
hacer un casting si el objeto originalmente era de ese tipo. Es decir la instrucción:
cocheDepepe=(Coche) vehiculo4;
instanceof
Permite comprobar si un determinado objeto pertenece a una clase concreta. Se utiliza
de esta forma:
ES un coche
ES un vehiculo
61
Manual de Java
Reutilización de clases
clases abstractas
A veces resulta que en las superclases se desean incluir métodos teóricos, métodos que
no se desea implementar del todo, sino que sencillamente se indican en la clase para
que el desarrollador que desee crear una subclase heredada de la clase abstracta, esté
obligado a sobrescribir el método.
A las clases que poseen métodos de este tipo (métodos abstractos) se las llama
clases abstractas. Son clases creadas para ser heredadas por nuevas clases creadas
por el programador. Son clases base para herencia. Las clases abstractas no deben de
ser instanciadas (no se pueden crear objetos de las clases abstractas).
Una clase abstracta debe ser marcada con la palabra clave abstract. Cada método
abstracto de la clase, también llevará el abstract. Ejemplo:
final
Se trata de una palabra que se coloca antecediendo a un método, variable o clase.
Delante de un método en la definición de clase sirve para indicar que ese método no
puede ser sobrescrito por las subclases. Si una subclase intentar sobrescribir el método,
el compilador de Java avisará del error.
62
© Jorge Sánchez Asenjo’ 2004
Si esa misma palabra se coloca delante de una clase, significará que esa clase no
puede tener descendencia.
Por último si se usa la palabra final delante de la definición de una propiedad de
clase, entonces esa propiedad pasará a ser una constante, es decir no se le podrá
cambiar el valor en ninguna parte del código.
clases internas
Se llaman clases internas a las clases que se definen dentro de otra clase. Esto permite
simplificar aun más el problema de crear programas. Ya que un objeto complejo se
puede descomponer en clases más sencillas. Pero requiere esta técnica una mayor
pericia por parte del programador.
Al definir una clase dentro de otra, estamos haciéndola totalmente dependiente.
Normalmente se realiza esta práctica para crear objetos internos a una clase (el motor
de un coche por ejemplo), de modo que esos objetos pasan a ser atributos de la clase.
Por ejemplo:
Las clases internas pueden ser privadas, protegidas o públicas. Fuera de la clase
contenedora no pueden crear objetos (sólo se pueden crear motores dentro de un
coche), salvo que la clase interna sea static en ese caso sí podrían. Por ejemplo (si la
clase motor fuera estática):
63
Manual de Java
Reutilización de clases
Pero eso sólo tiene sentido si todos los Coches tuvieran el mismo motor.
Dejando de lado el tema de las clases static, otro problema está en el operador this.
El problema es que al usar this dentro de una clase interna, this se refiere al objeto de la
clase interna (es decir this dentro de Motor se refiere al objeto Motor). Para poder
referirse al objeto contenedor (al coche) se usa Clase.this (Coche.this). Ejemplo:
Por último las clases internas pueden ser anónimas (se verán más adelante al estar más
relacionadas con interfaces y adaptadores).
interfaces
La limitación de que sólo se puede heredar de una clase, hace que haya problemas ya
que muchas veces se deseará heredar de varias clases. Aunque ésta no es la finalidad
directa de las interfaces, sí que tiene cierta relación
Mediante interfaces se definen una serie de comportamientos de objeto. Estos
comportamientos puede ser “implementados” en una determinada clase. No definen el
tipo de objeto que es, sino lo que puede hacer (sus capacidades). Por ello lo normal es
que el nombre de las interfaces terminen con el texto “able” (configurable,
modificable, cargable).
Por ejemplo en el caso de la clase Coche, esta deriva de la superclase Vehículo, pero
además puesto que es un vehículo a motor, puede implementar métodos de una interfaz
llamada por ejemplo arrancable. Se dirá entonces que la clase Coche es arrancable.
64
© Jorge Sánchez Asenjo’ 2004
utilizar interfaces
Para hacer que una clase utilice una interfaz, se añade detrás del nombre de la clase
la palabra implements seguida del nombre del interfaz. Se pueden poner varios
nombres de interfaces separados por comas (solucionando, en cierto modo, el problema
de la herencia múltiple).
Hay que tener en cuenta que la interfaz arrancable no tiene porque tener ninguna
relación de herencia con la clase vehículo, es más se podría implementar el interfaz
arrancable a una bomba de agua.
creación de interfaces
Una interfaz en realidad es una serie de constantes y métodos abstractos. Cuando
una clase implementa un determinado interfaz debe anular los métodos abstractos de
éste, redefiniéndolos en la propia clase. Esta es la base de una interfaz, en realidad no
hay una relación sino que hay una obligación por parte de la clase que implemente la
interfaz de redefinir los métodos de ésta.
Una interfaz se crea exactamente igual que una clase (se crean en archivos propios
también), la diferencia es que la palabra interface sustituye a la palabra class y que
sólo se pueden definir en un interfaz constantes y métodos abstractos.
Todas las interfaces son abstractas y sus métodos también son todos abstractos y
públicos (no hace falta poner el modificar abstract se toma de manera implícita). Las
variables se tienen obligatoriamente que inicializar. Ejemplo:
interface arrancable(){
boolean motorArrancado=false;
void arrancar();
void detenerMotor();
}
Los métodos son simples prototipos y toda variable se considera una constante (a no ser
que se redefina en una clase que implemente esta interfaz, lo cual no tendría mucho
sentido).
65
Manual de Java
Reutilización de clases
Arrancable
<<interfaz>>
Coche Arrancable Coche
arrancar()
subinterfaces
Una interfaz puede heredarse de otra interfaz, como por ejemplo en:
variables de interfaz
Al definir una interfaz, se pueden crear después variables de interfaz. Se puede
interpretar esto como si el interfaz fuera un tipo especial de datos (que no de clase). La
ventaja que proporciona es que pueden asignarse variables interfaz a cualquier objeto
de una clase que implementa la interfaz. Esto permite cosas como:
El juego que dan estas variables es impresionante, debido a que fuerzan acciones sobre
objetos de todo tipo, y sin importar este tipo; siempre y cuando estos objetos
pertenezcan a clases que implementen el interfaz.
66
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
interface Escribible {
void escribe(String texto);
}
class Prueba {
Escribible escritor;
public Prueba(Escribible e){
escritor=e;
}
public void enviaTexto(String s){
escritor.escribe(s);
}
}
creación de paquetes
Un paquete es una colección de clases e interfaces relacionadas. El compilador de Java
usa los paquetes para organizar la compilación y ejecución. Es decir, un paquete es una
biblioteca. De hecho el nombre completo de una clase es el nombre del paquete en el
que está la clase, punto y luego el nombre de la clase. Es decir si la clase Coche está
dentro del paquete locomoción, el nombre completo de Coche es locomoción.Coche.
A veces resulta que un paquete está dentro de otro paquete, entonces habrá que
indicar la ruta completa a la clase. Por ejemplo locomoción.motor.Coche
Mediante el comando import (visto anteriormente), se evita tener que colocar el
nombre completo. El comando import se coloca antes de definir la clase. Ejemplo:
import locomoción.motor.Coche;
Gracias a esta instrucción para utilizar la clase Coche no hace falta indicar el paquete en
el que se encuentra, basta indicar sólo Coche. Se puede utilizar el símbolo asterisco
como comodín.
67
Manual de Java
Reutilización de clases
Ejemplo:
import locomoción.*;
//Importa todas las clase del paquete locomoción
import locomoción.*;
import locomoción.motor.*;
Cuando desde un programa se hace referencia a una determinada clase se busca ésta en
el paquete en el que está colocada la clase y, sino se encuentra, en los paquetes que se
han importado al programa. Si ese nombre de clase se ha definido en un solo paquete,
se usa. Si no es así podría haber ambigüedad por ello se debe usar un prefijo delante
de la clase con el nombre del paquete.
Es decir:
paquete.clase
O incluso:
paquete1.paquete2......clase
68
© Jorge Sánchez Asenjo’ 2004
Paquete interior
vehiculo
clientes
dosruedas <<access>>
Coche
Moto Bici
Camión
El paquete vehiculo puede ver
la parte pública del paquete clientes
<<import>>
motor
El paquete vehiculo importa el contenido público
del paquete motor como si fuera parte del propio
paquete vehiculo
69
excepciones
Los errores de sintaxis son detectados durante la compilación. Pero las excepciones
pueden provocar situaciones irreversibles, su control debe hacerse en tiempo de
ejecución y eso presenta un gran problema. En Java se puede preparar el código
susceptible a provocar errores de ejecución de modo que si ocurre una excepción, el
código es lanzado (throw) a una determinada rutina previamente preparada por el
programador, que permite manipular esa excepción. Si la excepción no fuera capturada,
la ejecución del programa se detendría irremediablemente.
En Java hay muchos tipos de excepciones (de operaciones de entrada y salida, de
operaciones irreales. El paquete java.lang.Exception y sus subpaquetes contienen
todos los tipos de excepciones.
Cuando se produce un error se genera un objeto asociado a esa excepción. Este
objeto es de la clase Exception o de alguna de sus herederas. Este objeto se pasa al
código que se ha definido para manejar la excepción. Dicho código puede manipular las
propiedades del objeto Exception.
Hay una clase, la java.lang.Error y sus subclases que sirven para definir los
errores irrecuperables más serios. Esos errores causan parada en el programa, por lo
que el programador no hace falta que los manipule. Estos errores les produce el sistema
y son incontrolables para el programador. Las excepciones son fallos más leves, y más
manipulables.
try y catch
Las sentencias que tratan las excepciones son try y catch. La sintaxis es:
try {
instrucciones que se ejecutan salvo que haya un error
}
catch (ClaseExcepción objetoQueCapturaLaExcepción) {
instrucciones que se ejecutan si hay un error}
71
Manual de Java
Excepciones
Puede haber más de una sentencia catch para un mismo bloque try. Ejemplo:
try {
readFromFile(“arch”);
...
}
catch(FileNotFoundException e) {
//archivo no encontrado
...
}
catch (IOException e) {
...
}
java.lang
Object Throwable
Exception Error
IndexOutOfBoundsException IllegalArgumentException
72
© Jorge Sánchez Asenjo’ 2004
Dentro del bloque try se colocan las instrucciones susceptibles de provocar una
excepción, el bloque catch sirve para capturar esa excepción y evitar el fin de la
ejecución del programa. Desde el bloque catch se maneja, en definitiva, la excepción.
Cada catch maneja un tipo de excepción. Cuando se produce una excepción, se busca
el catch que posea el manejador de excepción adecuado, será el que utilice el mismo tipo
de excepción que se ha producido. Esto puede causar problemas si no se tiene cuidado,
ya que la clase Exception es la superclase de todas las demás. Por lo que si se produjo,
por ejemplo, una excepción de tipo AritmethicException y el primer catch captura
el tipo genérico Exception, será ese catch el que se ejecute y no los demás.
Por eso el último catch debe ser el que capture excepciones genéricas y los primeros
deben ser los más específicos. Lógicamente si vamos a tratar a todas las excepciones
(sean del tipo que sean) igual, entonces basta con un solo catch que capture objetos
Exception.
manejo de excepciones
Siempre se debe controlar una excepción, de otra forma nuestro software está a merced
de los fallos. En la programación siempre ha habido dos formas de manejar la
excepción:
boolean indiceNoValido=true;
int i; //Entero que tomará nos aleatorios de 0 a 9
String texto[]={“Uno”,”Dos”,”Tres”,”Cuatro”,”Cinco”};
while(indiceNoValido){
try{
i=Math.round(Math.random()*9);
System.out.println(texto[i];
indiceNoValido=false;
}catch(ArrayIndexOutOfBoundsException exc){
System.out.println(“Fallo en el índice”);
}
}
En el código anterior, el índice i calcula un número del 0 al 9 y con ese número el código
accede al array texto que sólo contiene 5 elementos. Esto producirá muy a menudo una
excepción del tipo ArrayIndexOutOfBoundsException que es manejada por el catch
73
Manual de Java
Excepciones
try{
....
} catch (IOException ioe){
System.out.println(ioe.getMessage());
}
throws
Al llamar a métodos, ocurre un problema con las excepciones. El problema es, si el
método da lugar a una excepción, ¿quién la maneja? ¿El propio método?¿O el código
que hizo la llamada al método?
Con lo visto hasta ahora, sería el propio método quien se encargara de sus
excepciones, pero esto complica el código. Por eso otra posibilidad es hacer que la
excepción la maneje el código que hizo la llamada.
Esto se hace añadiendo la palabra throws tras la primera línea de un método. Tras
esa palabra se indica qué excepciones puede provocar el código del método. Si ocurre
una excepción en el método, el código abandona ese método y regresa al código desde el
que se llamó al método. Allí se posará en el catch apropiado para esa excepción.
Ejemplo:
En este caso se está indicando que el método usarArchivo puede provocar excepciones
del tipo IOException y InterruptedException. Esto significará, además, que el que
utilice este método debe preparar el catch correspondiente para manejar los posibles
errores.
74
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
try{
...
objeto.usarArchivo(“C:\texto.txt”);//puede haber excepción
..
}
catch(IOException ioe){...
}
catch(InterruptedException ie){...
}
...//otros catch para otras posibles excepciones
throw
Esta instrucción nos permite lanzar a nosotros nuestras propias excepciones (o lo que es
lo mismo, crear artificialmente nosotros las excepciones). Ante:
try{
...
} catch(ArrayIndexOutOfBoundsException exc){
throw new IOException();
} catch(IOException){
...
}
finally
La cláusula finally está pensada para limpiar el código en caso de excepción. Su uso es:
try{
...
75
Manual de Java
Excepciones
76
clases fundamentales (I)
la clase Object
Todas las clases de Java poseen una superclase común, esa es la clase Object. Por eso
los métodos de la clase Object son fundamentales ya que todas las clases los heredan.
Esos métodos están pensados para todas las clases, pero hay que redefinirlos para que
funcionen adecuadamente.
Es decir, Object proporciona métodos que son heredados por todas las clase. La idea
es que todas las clases utilicen el mismo nombre y prototipo de método para hacer
operaciones comunes como comprobar igualdad, clonar, .... y para ello habrá que
redefinir esos métodos a fin de que se ajusten adecuadamente a cada clase.
comparar objetos
La clase Object proporciona un método para comprobar si dos objetos son iguales. Este
método es equals. Este método recibe como parámetro un objeto con quien comparar y
devuelve true si los dos objetos son iguales.
No es lo mismo equals que usar la comparación de igualdad. Ejemplos:
En el ejemplo anterior equals devuelve true si los dos coches tienen el mismo modelo,
marca y matrícula . El operador “==” devuelve true si los dos objetos se refieren a la
misma cosa (las dos referencias apuntan al mismo objeto).
Realmente en el ejemplo anterior la respuesta del método equals sólo será válida si
en la clase que se está comparando (Coche en el ejemplo) se ha redefinido el método
equals. Esto no es opcional sino obligatorio si se quiere usar este método. El
resultado de equals depende de cuándo consideremos nosotros que devolver verdadero
o falso. En el ejemplo anterior el método equals sería:
public class Coche extends Vehículo{
public boolean equals (Object o){
if ((o!=null) && (o instanceof Coche)){
if (((Coche)o).matricula==matricula &&
((Coche)o).marca==marca
&& ((Coche)o).modelo==modelo))
return true
}
return false; //Si no se cumple todo lo anterior
77
Manual de Java
Clases fundamentales (I)
Es necesario el uso de instanceOf ya que equals puede recoger cualquier objeto Object.
Para que la comparación sea válida primero hay que verificar que el objeto es un coche.
El argumento o siempre hay que convertirlo al tipo Coche para utilizar sus propiedades
de Coche.
código hash
El método hashCode() permite obtener un número entero llamado código hash.
Este código es un entero único para cada objeto que se genera aleatoriamente según su
contenido. No se suele redefinir salvo que se quiera anularle para modificar su función y
generar códigos hash según se desee.
clonar objetos
El método clone está pensado para conseguir una copia de un objeto. Es un método
protected por lo que sólo podrá ser usado por la propia clase y sus descendientes,
salvo que se le redefina con public.
Además si una determinada clase desea poder clonar sus objetos de esta forma, debe
implementar la interfaz Cloneable (perteneciendo al paquete java.lang), que no
contiene ningún método pero sin ser incluída al usar clone ocurriría una excepción del
tipo CloneNotSupportedException. Esta interfaz es la que permite que el objeto sea
clonable.
Ejemplo:
public class Coche extends Vehiculo implements arrancable,
Cloneable{
public Object clone(){
try{
return (super.clone());
}catch(CloneNotSupportedException cnse){
System.out.println("Error inesperado en clone");
return null;
}
}
....
//Clonación
Coche uno=new Coche();
Coche dos=(Coche)uno.clone();
En la última línea del código anterior, el cast “(Coche)” es obligatorio ya que clone
devuelve forzosamente un objeto tipo Object. Aunque este código generaría dos objetos
distintos, el código hash sería el mismo.
método toString
Este es un método de la clase Object que da como resultado un texto que describe al
objeto. la utiliza, por ejemplo el método println para poder escribir un método por
pantalla. Normalmente en cualquier clase habría que definir el método toString. Sin
redefinirlo el resultado podría ser:
78
© Jorge Sánchez Asenjo’ 2004
clase Class
La clase Object posee un método llamado getClass() que devuelve la clase a la que
pertenece un determinado objeto. La clase Class es también una superclase común a
todas las clase, pero a las clases que están en ejecución.
Class tiene una gran cantidad de métodos que permiten obtener diversa
información sobre la clase de un objeto determinado en tiempo de ejecución. A eso se le
llama reflexión (obtener información sobre el código de un objeto).
79
Manual de Java
Clases fundamentales (I)
Ejemplo:
80
© Jorge Sánchez Asenjo’ 2004
método significado
static Class
forName(String nombre)
throws ClassNotFoundException
Field getDeclaredField(String Devuelve la propiedad declarada en la clase que tiene
nombre) como nombre la cadena que se pasa como argumento.
throws NoSuchFieldException,
SecurityException
Field [] getDeclaredFields() Devuelve todas las propiedades de la clase en forma de
throws SecurityException array de objetos Field.
Method getDeclaredMethod Devuelve el método declarado en la clase que tiene
(String nombre, Class[] como nombre la cadena que se pasa como argumento
TipoDeParametros) como tipo de los argumentos, el que indique el array
throws NoSuchMethodException, Class especificado
SecurityException
Method [] Devuelve todos los métodos de la clase en forma de
getDeclaredMethods() array de objetos Method.
throws SecurityException
Class getDeclaringClass() Devuelve la clase en la que se declaró la actual. Si no se
throws SecurityException declaró dentro de otra, devuelve null.
Field getField(String nombre) Devuelve (en forma de objeto Field) la propiedad
throws NoSuchFieldException, pública cuyo nombre coincida con el que se pasa como
SecurityException argumento.
Field[] getFields() Devuelve un array que contiene una lista de todas las
throws SecurityException propiedades públicas de la clase.
Class[] getInterface() Devuelve un array que representa a todos los interfaces
que forman parte de la clase.
Method getMethod Devuelve el método público de la clase que tiene como
(String nombre, Class[] nombre la cadena que se pasa como argumento como
TipoDeParametros) tipo de los argumentos, el que indique el array Class
throws NoSuchMethodException, especificado
SecurityException
Method [] getMethods() Devuelve todos los métodos públicos de la clase en
throws SecurityException forma de array de objetos Method.
int getModifiers() Devuelve, codificados, los modificadores de la clase
(protected, public,...). Para decodificarlos hace falta
usar la clase Modifier.
String getName() Devuelve el nombre de la clase.
String getPackage() Devuelve el paquete al que pertenece la clase.
ProtectionDomain Devuelve el dominio de protección de la clase. (JDK 1.2)
getProtectionDomain()
URL getResource(String Devuelve, en forma de URL, el recurso cuyo nombre se
nombre) indica
InputStream Devuelve, en forma de InputStream, el recurso cuyo
getResourceAsStream(String nombre se indica
nombre)
class getSuperclass() Devuelve la superclase a la que pertenece ésta. Si no hay
superclase, devuelve null
81
Manual de Java
Clases fundamentales (I)
método significado
boolean isArray() Devuelve true si la clase es un array
boolean Devuelve true si la clase a la que pertenece clas2 es
isAssignableFrom(Class clas2) asignable a la clase actual.
boolean isInstance(Object o) Devuelve true si el objeto o es compatible con la clase.
Es el equivalente dinámico al operador instanceof.
boolean isInterface() Devuelve true si el objeto class representa a una
interfaz.
boolean isPrimitive() Devuelve true si la clase no tiene superclase.
Object newInstance() Crea un nuevo objeto a partir de la clase actual. El
throws InstantiationException, objeto se crea usando el constructor por defecto.
IllegalAccessException
String toString() Obtiene un texto descriptivo del objeto. Suele ser lo
mismo que el resultado del método getName().
reflexión
En Java, por traducción del término reflection, se denomina reflexión a la capacidad
de un objeto de examinarse a sí mismo. En el paquete java.lang.reflect hay diversas
clases que tienen capacidad de realizar este examen. Casi todas estas clases han sido
referenciadas al describir los métodos de la clase Class.
Class permite acceder a cada elemento de reflexión de una clase mediante dos pares
de métodos. El primer par permite acceder a los métodos públicos (getField y
getFields por ejemplo), el segundo par accede a cualquier elemento miembro
(getDeclaredField y getDeclaredFields) por ejemplo.
clase Field
La clase java.lang.reflection.Field, permite acceder a las propiedades (campos) de una
clase. Métodos interesantes:
método significado
Object get () Devuelve el valor del objeto Field.
Class getDeclaringClass() Devuelve la clase en la que se declaro la propiedad.
int getModifiers() Devuelve, codificados, los modificadores de la clase
(protected, public,...). Para decodificarlos hace falta
usar la clase Modifier.
String getName() Devuelve el nombre del campo.
Class getType() Devuelve, en forma de objeto Class, el tipo de la
propiedad.
void set(Object o, Object value) Asigna al objeto un determinado valor.
String toString() Cadena que describe al objeto.
82
© Jorge Sánchez Asenjo’ 2004
clase Method
Representa métodos de una clase. Sus propios métodos son:
método significado
Class getDeclaringClass() Devuelve la clase en la que se declaro la propiedad.
Class[] getExceptionTypes() Devuelve un array con todos los tipos de excepción que
es capaz de lanzar el método.
int getModifiers() Devuelve, codificados, los modificadores de la clase
(protected, public,...). Para decodificarlos hace falta
usar la clase Modifier.
String getName() Devuelve el nombre del método.
Class getParameterTypes() Devuelve, en forma de array Class, los tipos de datos de
los argumentos del método.
Class getReturnType() Devuelve, en forma de objeto Class, el tipo de datos que
devuelve el método.
void invoke(Object o, Object[] Invoca al método o usando la lista de parámetros
argumentos) indicada.
String toString() Cadena que describe al objeto.
clase Constructor
Representa constructores. Tiene casi los mismos métodos de la clase anterior.
método significado
Class getDeclaringClass() Devuelve la clase en la que se declaro la propiedad.
Class[] getExceptionTypes() Devuelve un array con todos los tipos de excepción que
es capaz de lanzar el método.
int getModifiers() Devuelve, codificados, los modificadores de la clase
(protected, public,...). Para decodificarlos hace falta
usar la clase Modifier.
String getName() Devuelve el nombre del método.
Class getParameterTypes() Devuelve, en forma de array Class, los tipos de datos de
los argumentos del método.
Object newInstance(Object[] Crea un nuevo objeto usando el constructor de clase que
argumentos) se corresponda con la lista de argumentos pasada.
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
String toString() Cadena que describe al objeto.
83
Manual de Java
Clases fundamentales (I)
Hay que tener en cuenta que no son equivalentes a los tipos básicos. La creación de
estos tipos lógicamente requiere usar constructores, ya que son objetos y no tipos
básicos.
El constructor admite valores del tipo básico relacionado e incluso valores String que
contengan texto convertible a ese tipo básico. Si ese texto no es convertible, ocurre una
excepción del tipo NumberFormatException.
La conversión de un String a un tipo básico es una de las utilidades básicas de estas
clases, por ello estas clases poseen el método estático valueOf entre otros para
convertir un String en uno de esos tipos. Ejemplos:
String s=”2500”;
Integer a=Integer.valueOf(s);
Short b=Short.valueOf(s);
Double c=Short.valueOf(s);
Byte d=Byte.valueOf(s);//Excepción!!!
Hay otro método en cada una de esas clases que se llama parse. La diferencia estriba en
que en los métodos parse la conversión se realiza hacia tipos básicos (int, double, float,
boolean,...) y no hacia las clase anteriores. Ejemplo:
String s=”2500”;
int y=Integer.parseInt(s);
short z=Short.parseShort(s);
double c=Short.parseDouble(s);
byte x=Byte.parseByte(s);
84
© Jorge Sánchez Asenjo’ 2004
Estos métodos son todos estáticos. Todas las clases además poseen métodos dinámicos
para convertir a otros tipos (intValue, longValue,... o el conocido toString).
Todos estos métodos lanzan excepciones del tipo NumberFormatException, que
habrá que capturar con el try y el catch pertinentes.
Además han redefinido el método equals para comparar objetos de este tipo.
Además poseen el método compareTo que permite comparar dos elementos de este
tipo (este método se maneja igual que el compareTo de la clase String, ver comparación
entre objetos String, página 35)
clase StringBuffer
La clase String tiene una característica que puede causar problemas, y es que los objetos
String se crean cada vez que se les asigna o amplia el texto. Esto hace que la ejecución
sea más lenta. Este código:
String frase=”Esta “;
frase += “es ”;
frase += “la ”;
frase += “frase”;
En este código se crean cuatro objetos String y los valores de cada uno son copiados al
siguiente. Por ello se ha añadido la clase StringBuffer que mejora el rendimiento. La
concatenación de textos se hace con el método append:
Por otro lado el método toString permite pasar un StringBuffer a forma de cadena
String.
Se recomienda usar StringBuffer cuando se requieren cadenas a las que se las cambia el
texto a menudo. Posee métodos propios que son muy interesantes para realizar estas
modificaciones (insert, delete, replace,…).
métodos de StringBuffer
método descripción
StringBuffer append(tipo variable) Añade al StringBuffer el valor en forma de
cadena de la variable
char charAt(int pos) Devuelve el carácter que se encuentra en la
posición pos
85
Manual de Java
Clases fundamentales (I)
método descripción
int capacity() Da como resultado la capacidad actual del
StringBuffer
StringBuffer delete(int inicio, int fin) Borra del StringBuffer los caracteres que
van desde la posición inicio a la posición fin
StringBuffer deleteCharAt(int pos) Borra del StringBuffer el carácter situado en
la posición pos
void ensureCapacity(int capadMinima) Asegura que la capacidad del StringBuffer
sea al menos la dada en la función
void getChars(int srcInicio, int srcFin, Copia a un array de caracteres cuyo nombre
char[] dst, int dstInicio) es dado por el tercer parámetro, los
caracteres del StringBuffer que van desde
srcInicio a srcFin. Dichos caracteres se
copiarán en el array desde la posición
dstInicio
StringBuffer insert(int pos, tipo valor) Inserta el valor en forma de cadena a partir
de la posición pos del StringBuffer
int length() Devuelve el tamaño del StringBuffer
StringBuffer replace(int inicio, int fin, Reemplaza la subcadena del StringBuffer
String texto) que va desde inicio a fin por el texto indicado
StringBuffer reverse() Se cambia el StringBuffer por su inverso
void setLength(int tamaño) Cambia el tamaño del StringBuffer al
tamaño indicado.
String substring(int inicio) Devuelve una cadena desde la posición inicio
String substring(int inicio, int fin) Devuelve una cadena desde la posición inicio
hasta la posición fin
String toString() Devuelve el StringBuffer en forma de cadena
String
números aleatorios
La clase java.util.Random está pensada para la producción de elementos aleatorios.
Los números aleatorios producen dicha aleatoriedad usando una fórmula matemática
muy compleja que se basa en, a partir de un determinado número obtener
aleatoriamente el siguiente. Ese primer número es la semilla.
El constructor por defecto de esta clase crea un número aleatorio utilizando una
semilla obtenida a partir de la fecha y la hora. Pero si se desea repetir continuamente la
misma semilla, se puede iniciar usando un determinado número long:
métodos de Random
método devuelve
boolean nextBoolean() true o false aleatoriamente
int nextInt() un int
86
© Jorge Sánchez Asenjo’ 2004
método devuelve
int nextInt(int n) Un número entero de 0 a n-1
long nextLong() Un long
float nextFloat() Número decimal de -1,0 a 1.0
double nextDouble() Número doble de -1,0 a 1.0
void setSeed(long semilla) Permite cambiar la semilla.
fechas
Sin duda alguna el control de fechas y horas es uno de los temas más pesados de la
programación. Por ello desde Java hay varias clase dedicadas a su control.
La clase java.util.Calendar permite usar datos en forma de día mes y año, su
descendiente java.util.GregorianCalendar añade compatibilidad con el calendario
Gregoriano, la clase java.util.Date permite trabajar con datos que representan un
determinado instante en el tiempo y la clase java.text.DateFormat está encargada de
generar distintas representaciones de datos de fecha y hora.
clase Calendar
Se trata de una clase abstracta (no se pueden por tanto crear objetos Calendar) que
define la funcionalidad de las fechas de calendario y define una serie de atributos
estáticos muy importante para trabajar con las fechas. Entre ellos (se usan siempre con
el nombre Calendar, por ejemplo Calendar.DAY_OF_WEEK):
Mes: MONTH es el mes del año (del 0, enero, al 11, diciembre). Se pueden usar
las constantes: JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY,
AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER.
Día del mes: DAY_OF_MONTH número del día del mes (empezando por 1).
Día del año: DAY_OF_YEAR número del día del año (de 1 a 365).
AM_PM. Propiedad que sirve para indicar en qué parte del día estamos, AM o
PM.
Minutos. MINUTE
87
Manual de Java
Clases fundamentales (I)
clase GregorianCalendar
Es subclase de la anterior (por lo que hereda todos sus atributos). Permite crear datos
de calendario gregoriano. Tiene numerosos constructores, algunos de ellos son:
método get
El método get heredado de la clase Calendar sirve para poder obtener un detalle de
una fecha. A este método se le pasa el atributo a obtener (véase lista de campos en la
clase Calendar). Ejemplos:
GregorianCalendar fecha=new
GregorianCalendar(2003,7,2,12,30,23);
System.out.println(fecha.get(Calendar.MONTH));
System.out.println(fecha.get(Calendar.DAY_OF_YEAR));
System.out.println(fecha.get(Calendar.SECOND));
System.out.println(fecha.get(Calendar.MILLISECOND));
/* La salida es
7
214
23
0
*/
método set
Es el contrario del anterior, sirve para modificar un campo del objeto de calendario.
Tiene dos parámetros: el campo a cambiar (MONTH, YEAR,...) y el valor que valdrá ese
campo:
88
© Jorge Sánchez Asenjo’ 2004
fecha.set(Calendar.MONTH, Calendar.MAY);
fecha.set(Calendar.DAY_OF_MONTH, 12)
Otro uso de set consiste en cambiar la fecha indicando, año, mes y día y, opcionalmente,
hora y minutos.
fecha.set(2003,17,9);
método getTime
Obtiene el objeto Date equivalente a al representado por el GregorianCalendar. En Java
los objetos Date son fundamentales para poder dar formato a las fechas.
método setTime
Hace que el objeto de calendario tome como fecha la representada por un objeto date.
Es el inverso del anterior
método getTimeInMillis
Devuelve el número de milisegundos que representa esa fecha.
clase Date
Representa una fecha en forma de milisegundos transcurridos, su idea es representar
un instante. Cuenta fechas desde el 1900. Normalmente se utiliza conjuntamente con la
clase GregorianCalendar. Pero tiene algunos métodos interesantes.
construcción
Hay varias formas de crear objetos Date:
método after
Se le pasa como parámetro otro objeto Date. Devuelve true en el caso de que la segunda
fecha no sea más moderna. Ejemplo:
89
Manual de Java
Clases fundamentales (I)
método before
Inverso al anterior. Devuelve true si la fecha que recibe como parámetro no es más
reciente.
clase DateFormat
A pesar de la potencia de las clases relacionadas con las fechas vistas anteriormente,
sigue siendo complicado y pesado el hecho de hacer que esas fechas aparezcan con un
formato más legible por un usuario normal.
La clase DateFormat nos da la posibilidad de formatear las fechas. Se encuentra en
el paquete java.text. Hay que tener en cuenta que no representa fechas, sino maneras
de dar formato a las fechas. Es decir un objeto DateFormat representa un formato de
fecha (formato de fecha larga, formato de fecha corta,...).
creación básica
Por defecto un objeto DateFormat con opciones básicas se crea con:
DateFormat sencillo=DateFormat.getInstance();
el método format
Todos los objetos DateFormat poseen un método llamado format que da como
resultado una cadena String y que posee como parámetro un objeto de tipo Date. El
texto devuelto representa la fecha de una determinada forma. El formato es el indicado
durante la creación del objeto DateFormat. Ejemplo:
90
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
DateFormat df=DateFormat.getDateInstance(DateFormat.LONG);
System.out.println(df.format(new Date()));
//14 de abril de 2004
DateFormat
df2=DateFormat.getDateTimeInstance(DateFormat.LONG);
System.out.println(df2.format(new Date()));
// 14/04/04 00H52' CEST
La fecha sale con el formato por defecto del sistema (por eso sale en español si el
sistema Windows está en español).
método parse
Inverso al método format. Devuelve un objeto Date a partir de un String que es pasado
como parámetro. Este método lanza excepciones del tipo ParseException (clase que
se encuentra en el paquete java.text), que estamos obligados a capturar. Ejemplo:
DateFormat df=DateFormat.getDateTimeInstance(
DateFormat.SHORT,DateFormat.FULL);
try{
fecha=df2.parse("14/3/2004 00H23' CEST");
}
catch(ParseException pe){
System.out.println("cadena no válida");
}
Obsérvese que el contenido de la cadena debe ser idéntica al formato de salida del
objeto DateFormat de otra forma se generaría la excepción.
Es un método muy poco usado.
91
Manual de Java
Clases fundamentales (I)
introducción
Se denomina cadena delimitada a aquellas que contienen texto que está dividido en
partes (tokens) y esas partes se dividen mediante un carácter (o una cadena) especial.
Por ejemplo la cadena 7647-34-123223-1-234 está delimitada por el guión y forma 5
tokens.
Es muy común querer obtener cada zona delimitada, cada token, de la cadena. Se
puede hacer con las clases que ya hemos visto, pero en el paquete java.util disponemos
de la clase más apropiada para hacerlo, StringTokenizer.
Esa clase representa a una cadena delimitada de modo además que en cada
momento hay un puntero interno que señala al siguiente token de la cadena. Con los
métodos apropiados podremos avanzar por la cadena.
construcción
La forma común de construcción es usar dos parámetros: el texto delimitado y la cadena
delimitadora. Ejemplo:
Se puede construir también el tokenizer sólo con la cadena, sin el delimitador. En ese
caso se toma como delimitador el carácter de nueva línea (\n), el retorno de carro (\r),
el tabulador (\t) o el espacio. Los tokens son considerados sin el delimitador (en el
ejemplo sería 1234, 5, 678, 9 y 00, el guión no cuenta).
uso
Para obtener las distintas partes de la cadena se usan estos métodos:
Ejemplo:
String tokenizada="10034-23-43423-1-3445";
StringTokenizer st=new StringTokenizer(tokenizada,"-");
while (st.hasMoreTokens()){
System.out.println(st.nextToken());
}// Obtiene:10034 23 43423 1 y 3445
92
entrada y salida en Java
El paquete java.io contiene todas las clases relacionadas con las funciones de entrada
(input) y salida (output). Se habla de E/S (o de I/O) refiriéndose a la entrada y salida.
En términos de programación se denomina entrada a la posibilidad de introducir
datos hacia un programa; salida sería la capacidad de un programa de mostrar
información al usuario.
InputStream/ OutputStream
Clases abstractas que definen las funciones básicas de lectura y escritura de una
secuencia de bytes pura (sin estructurar). Esas son corrientes de bits, no representan ni
textos ni objetos. Poseen numerosas subclases, de hecho casi todas las clases preparadas
para la lectura y la escritura, derivan de estas.
Aquí se definen los métodos read() (Leer) y write() (escribir). Ambos son métodos
que trabajan con los datos, byte a byte.
Reader/Writer
Clases abstractas que definen las funciones básicas de escritura y lectura basada en
caracteres Unicode. Se dice que estas clases pertenecen a la jerarquía de
lectura/escritura orientada a caracteres, mientras que las anteriores pertenecen a la
jerarquía orientada a bytes.
Aparecieron en la versión 1.1 y no substituyen a las anteriores. Siempre que se pueda
es más recomendable usar clases que deriven de estas.
Posee métodos read y write adaptados para leer arrays de caracteres.
InputStreamReader/ OutputStreamWriter
Son clases que sirven para adaptar la entrada y la salida. El problema está en que las
clases anteriores trabajan de forma muy distinta y ambas son necesarias. Por ello
InputStreamReader convierte una corriente de datos de tipo InputStream a forma de
Reader.
DataInputStream/DataOutputStream
Leen corrientes de datos de entrada en forma de byte, pero adaptándola a los tipos
simples de datos (int, short, byte,..., String). Tienen varios métodos read y write
para leer y escribir datos de todo tipo. En el caso de DataInputStream son:
93
Manual de Java
Entrada y Salida en Java
ByteArrayInputStream
BufferedInputStream
FileInputStream
DataInputStream
FilterInputStream
PushbackInputStream
Object
ObjectInputStream DataInput
InputStream
PipedInputStream
ObjectInput
File SequenceInputStream
BufferedOutputStream
RandomAccessFile ByteArrayOutputStream
DataOutputStream
FileDescriptor FilterOutputStream
java.io PushbackOutputStream
ObjectOutputStream
OutputStream DataOutput
PipedOutputStream
ObjectStreamClass
FileOuputStream ObjectOutput
StreamTokenizer PushBackreader
FilterReader
BufferedReader LineNumberReader
StringReader FileWriter
CharArrayReader FilterWriter
PipedReader
OutputStreamWriter
BufferedWriter
Writer
StringWriter
Serializable Externalizable
CharArrayWriter
PipedWriter
ObjectInputValidation
PrintWriter
94
© Jorge Sánchez Asenjo’ 2004
ObjectInputStream/ObjectOutputStream
Filtros de secuencia que permiten leer y escribir objetos de una corriente de datos
orientada a bytes. Sólo tiene sentido si los datos almacenados son objetos. Aporta un
nuevo método de lectura:
BufferedInputStream/BufferedOutputStream/BufferedReader/BufferedWriter
La palabra buffered hace referencia a la capacidad de almacenamiento temporal en la
lectura y escritura. Los datos se almacenan en una memoria temporal antes de ser
realmente leídos o escritos. Se trata de cuatro clase que trabajan con métodos distintos
pero que suelen trabajar con las mismas corrientes de entrada que podrán ser de bytes
(Input/OutputStream) o de caracteres (Reader/Writer).
La clase BufferedReader aporta el método readLine que permite leer caracteres
hasta la presencia de null o del salto de línea.
95
Manual de Java
Entrada y Salida en Java
PrintWriter
Secuencia pensada para impresión de texto. Es una clase escritora de caracteres en
flujos de salida, que posee los métodos print y println ya comentados anteriormente,
que otorgan gran potencia a la escritura.
FileInputStream/FileOutputStream/FileReader/FileWriter
Leen y escriben en archivos (File=Archivo).
PipedInputStream/PipedOutputStream
Permiten realizar canalizaciones entre la entrada y la salida; es decir lo que se lee se
utiliza para una secuencia de escritura o al revés.
El método read() permite leer un byte. Este método puede lanzar excepciones del tipo
IOException por lo que debe ser capturada dicha excepción.
int valor=0;
try{
valor=System.in.read();
}
catch(IOException e){
...
}
System.out.println(valor);
No tiene sentido el listado anterior, ya que read() lee un byte de la entrada estándar, y
en esta entrada se suelen enviar caracteres, por lo que el método read no es el
apropiado. El método read puede poseer un argumento que es un array de bytes que
almacenará cada carácter leído y devolverá el número de caracteres leído
InputStream stdin=System.in;
int n=0;
byte[] caracter=new byte[1024];
96
© Jorge Sánchez Asenjo’ 2004
try{
n=System.in.read(caracter);
}
catch(IOException e){
System.out.println("Error en la lectura");
}
for (int i=0;i<=n;i++)
System.out.print((char)caracter[i]);
El lista anterior lee una serie de bytes y luego los escribe. La lectura almacena el código
del carácter leído, por eso hay que hacer una conversión a char.
Para saber que tamaño dar al array de bytes, se puede usar el método available()
de la clase InputStream la tercera línea del código anterior sería:
97
Manual de Java
Entrada y Salida en Java
String texto="";
try{
//Obtención del objeto Reader
InputStreamReader conv=new InputStreamReader(System.in);
//Obtención del BufferedReader
BufferedReader entrada=new BufferedReader(conv);
texto=entrada.readLine();
}
catch(IOException e){
System.out.println("Error");
}
System.out.println(texto);
98
Ficheros
Una aplicación Java puede escribir en un archivo, salvo que se haya restringido su
acceso al disco mediante políticas de seguridad. La dificultad de este tipo de operaciones
está en que los sistemas de ficheros son distintos en cada sistema y aunque Java
intentar aislar la configuración específica de un sistema, no consigue evitarlo del todo.
clase File
En el paquete java.io se encuentra la clase File pensada para poder realizar
operaciones de información sobre archivos. No proporciona métodos de acceso a los
archivos, sino operaciones a nivel de sistema de archivos (listado de archivos, crear
carpetas, borrar ficheros, cambiar nombre,...).
El primer formato utiliza una ruta absoluta y el segundo una ruta relativa. La ruta
absoluta se realiza desde la raíz de la unidad de disco en la que se está trabajando y la
relativa cuenta desde la carpeta actual de trabajo.
Otra posibilidad de construcción es utilizar como primer parámetro un objeto File ya
hecho. A esto se añade un segundo parámetro que es una ruta que cuenta desde la
posición actual.
Si el archivo o carpeta que se intenta examinar no existe, la clase File no devuelve una
excepción. Habrá que utilizar el método exists. Este método recibe true si la carpeta o
archivo es válido (puede provocar excepciones SecurityException).
También se puede construir un objeto File a partir de un objeto URL.
99
Manual de Java
Ficheros
También se pueden utilizar las variables estáticas que posee File. Estas son:
propiedad uso
char separatorChar El carácter separador de nombres de archivo y
carpetas. En Linux/Unix es “/” y en Windows es
“\”, que se debe escribir como \\, ya que el
carácter \ permite colocar caracteres de control,
de ahí que haya que usar la doble barra.
String separator Como el anterior pero en forma de String
char pathSeparatorChar El carácter separador de rutas de archivo que
permite poner más de un archivo en una ruta. En
Linux/Unix suele ser “:”, en Windows es “;”
String pathSeparator Como el anterior, pero en forma de String
String ruta=”documentos/manuales/2003/java.doc”;
ruta=ruta.replace(‘/’,File.separatorChar);
métodos generales
método uso
String toString() Para obtener la cadena descriptiva del objeto
boolean exists() Devuelve true si existe la carpeta o archivo.
boolean canRead() Devuelve true si el archivo se puede leer
boolean canWrite() Devuelve true si el archivo se puede escribir
boolean isHidden() Devuelve true si el objeto File es oculto
boolean isAbsolute() Devuelve true si la ruta indicada en el objeto File
es absoluta
boolean equals(File f2) Compara f2 con el objeto File y devuelve
verdadero si son iguales.
String getAbsolutePath() Devuelve una cadena con la ruta absoluta al objeto
File.
File getAbsoluteFile() Como la anterior pero el resultado es un objeto
File
String getName() Devuelve el nombre del objeto File.
String getParent() Devuelve el nombre de su carpeta superior si la
hay y si no null
File getParentFile() Como la anterior pero la respuesta se obtiene en
forma de objeto File.
boolean setReadOnly() Activa el atributo de sólo lectura en la carpeta o
archivo.
100
© Jorge Sánchez Asenjo’ 2004
método uso
URL toURL() Convierte el archivo a su notación URL
throws MalformedURLException correspondiente
URI toURI() Convierte el archivo a su notación URI
correspondiente
métodos de carpetas
método uso
boolean isDirectory() Devuelve true si el objeto File es una carpeta y
false si es un archivo o si no existe.
boolean mkdir() Intenta crear una carpeta y devuelve true si fue
posible hacerlo
boolean mkdirs() Usa el objeto para crear una carpeta con la ruta
creada para el objeto y si hace falta crea toda la
estructura de carpetas necesaria para crearla.
boolean delete() Borra la carpeta y devuelve true si puedo hacerlo
String[] list() Devuelve la lista de archivos de la carpeta
representada en el objeto File.
static File[] listRoots() Devuelve un array de objetos File, donde cada
objeto del array representa la carpeta raíz de una
unidad de disco.
File[] listfiles() Igual que la anterior, pero el resultado es un array
de objetos File.
métodos de archivos
método uso
boolean isFile() Devuelve true si el objeto File es un archivo y
false si es carpeta o si no existe.
boolean renameTo(File f2) Cambia el nombre del archivo por el que posee el
archivo pasado como argumento. Devuelve true
si se pudo completar la operación.
boolean delete() Borra el archivo y devuelve true si puedo hacerlo
long length() Devuelve el tamaño del archivo en bytes
boolean createNewFile() Crea un nuevo archivo basado en la ruta dada al
Throws IOException objeto File. Hay que capturar la excepción
IOException que ocurriría si hubo error crítico al
crear el archivo.
Devuelve true si se hizo la creación del archivo
vacío y false si ya había otro archivo con ese
nombre.
101
Manual de Java
Ficheros
método uso
static File createTempFile(String Crea un objeto File de tipo archivo temporal con el
prefijo, String sufijo) prefijo y sufijo indicados. Se creará en la carpeta
de archivos temporales por defecto del sistema.
El prefijo y el sufijo deben de tener al menos
tres caracteres (el sufijo suele ser la extensión), de
otro modo se produce una excepción del tipo
IllegalArgumentsException
Requiere capturar la excepción IOException
que se produce ante cualquier fallo en la creación
del archivo
static File createTempFile(String Igual que el anterior, pero utiliza el directorio
prefijo, String sufijo, File directorio) indicado.
void deleteOnExit() Borra el archivo cuando finaliza la ejecución del
programa
secuencias de archivo
102
© Jorge Sánchez Asenjo’ 2004
escritura
El proceso sería:
3> Usar el objeto del punto 2 para escribir los datos mediante los métodos
writeTipo donde tipo es el tipo de datos a escribir (Int, Double, ...). A este
método se le pasa como único argumento los datos a escribir.
Ejemplo:
lectura
El proceso es análogo. Sólo que hay que tener en cuenta que al leer se puede alcanzar el
final del archivo. Al llegar al final del archivo, se produce una excepción del tipo
EOFException (que es subclase de IOException), por lo que habrá que controlarla.
Ejemplo, leer los números del ejemplo anterior :
103
Manual de Java
Ficheros
while (!finArchivo){
d=dis.readDouble();
System.out.println(d);
}
dis.close();
}
catch(EOFException e){
finArchivo=true;
}
catch(FileNotFoundException e){
System.out.println("No se encontro el archivo");
}
catch(IOException e){
System.out.println("Error al leer");
}
En este listado, obsérvese como el bucle while que da lugar a la lectura se ejecuta
indefinidamente (no se pone como condición a secas true porque casi ningún
compilador lo acepta), se saldrá de ese bucle cuando ocurra la excepción EOFException
que indicará el fin de archivo.
Las clases DataStream son muy adecuadas para colocar datos binarios en los
archivos.
Ejemplo:
104
© Jorge Sánchez Asenjo’ 2004
try{
FileWriter fw=new FileWriter(f);
fw.write(x);
fw.close();
}
catch(IOException e){
System.out.println("error");
return;
}
//Lectura de los datos
try{
FileReader fr=new FileReader(f);
x=fr.read();
fr.close();
}
catch(FileNotFoundException e){
System.out.println("Error al abrir el archivo");
}
catch(IOException e){
System.out.println("Error al leer");
}
System.out.println(x);
105
Manual de Java
Ficheros
catch(FileNotFoundException e){
System.out.println(“Error al abrir el archivo”);
}
catch(IOException e){
System.out.println(“Error al leer");
}
En este caso el listado permite leer un archivo de texto llamado texto.txt. El fin de
archivo con la clase BufferedReader se detecta comparando con null, ya que en caso de
que lo leído sea null, significará que hemos alcanzado el final del archivo. La gracia de
usar esta clase está en el método readLine que agiliza enormemente la lectura.
RandomAccessFile
Esta clase permite leer archivos en forma aleatoria. Es decir, se permite leer cualquier
posición del archivo en cualquier momento. Los archivos anteriores son llamados
secuenciales, se leen desde el primer byte hasta el último.
Esta es una clase primitiva que implementa los interfaces DataInput y
DataOutput y sirve para leer y escribir datos.
La construcción requiere de una cadena que contenga una ruta válida a un archivo o
de un archivo File. Hay un segundo parámetro obligatorio que se llama modo. El modo
es una cadena que puede contener una r (lectura), w (escritura) o ambas, rw.
Como ocurría en las clases anteriores, hay que capturar la excepción
FileNotFound cuando se ejecuta el constructor.
106
© Jorge Sánchez Asenjo’ 2004
el administrador de seguridad
Llamado Security manager, es el encargado de prohibir que subprogramas y
aplicaciones escriban en cualquier lugar del sistema. Por eso numerosas acciones
podrían dar lugar a excepciones del tipo SecurityException cuando no se permite
escribir o leer en un determinado sitio.
serialización
Es una forma automática de guardar y cargar el estado de un objeto. Se basa en la
interfaz serializable que es la que permite esta operación. Si un objeto ejecuta esta
interfaz puede ser guardado y restaurado mediante una secuencia.
Cuando se desea utilizar un objeto para ser almacenado con esta técnica, debe ser
incluida la instrucción implements Serializable (además de importar la clase
java.io.Serializable) en la cabecera de clase. Esta interfaz no posee métodos, pero es
un requisito obligatorio para hacer que el objeto sea serializable.
La clase ObjectInputStream y la clase ObjectOutputStream se encargan de
realizar este procesos. Son las encargadas de escribir o leer el objeto de un archivo. Son
herederas de InputStream y OutputStream, de hecho son casi iguales a
DataInput/OutputStream sólo que incorporan los métodos readObject y
writeObject que son muy poderosos. Ejemplo:
try{
FileInputStream fos=new FileInputStream("d:/nuevo.out");
ObjectInputStream os=new ObjectInputStream(fos);
Coche c;
boolean finalArchivo=false;
while(!finalArchivo){
c=(Coche) readObject();
System.out.println(c);
}
}
catch(EOFException e){
System.out.println("Se alcanzó el final");
}
catch(ClassNotFoundException e){
System.out.println("Error el tipo de objeto no es compatible");
}
catch(FileNotFoundException e){
System.out.println("No se encontró el archivo");
}
catch(IOException e){
System.out.println("Error "+e.getMessage());
e.printStackTrace();
107
Manual de Java
Ficheros
El listado anterior podría ser el código de lectura de un archivo que guarda coches. Los
métodos readObject y writeObject usan objetos de tipo Object, readObject les
devuelve y writeObject les recibe como parámetro. Ambos métodos lanzan excepciones
del tipo IOException y readObject además lanza excepciones del tipo
ClassNotFoundException.
Obsérvese en el ejemplo como la excepción EOFException ocurre cuando se
alcanzó el final del archivo.
108
clases fundamentales (II)
colecciones
java.util
produce
<<interfaz>> <<interfaz>>
AbstractList
ListIterator List
<<interfaz>>
Set AbstractSet
<<interfaz>> <<interfaz>>
TreeSet
Comparator SortedSet Vector ArrayList
HashSet
<<interfaz>>
Map AbstractMap
LinkedHashSet AbstractSequentialList
Stack
TreeMap WeakHashMap
produce LinkedList
HashMap
<<interfaz>>
produce <<interfaz>> Enumeration
Map.Entry
HashTable Dictionary
109
Manual de Java
Clases fundamentales (II). Colecciones
interfaz Collection
La interfaz fundamental de trabajo con estructuras dinámicas es java.util.Collection.
Esta interfaz define métodos muy interesantes para trabajar con listas. Entre ellos:
método uso
boolean add(Object o) Añade el objeto a la colección. Devuelve true si se pudo
completar la operación. Si no cambió la colección como
resultado de la operación devuelve false
boolean remove(Object o) Elimina al objeto indicado de la colección.
int size() Devuelve el número de objetos almacenados en la
colección
boolean isEmpty() Indica si la colección está vacía
boolean contains(Object o) Devuelve true si la colección contiene a o
void clear() Elimina todos los elementos de la colección
boolean addAll( Collection Añade todos los elementos de la colección otra a la
otra) colección actual
boolean removeAll( Elimina todos los objetos de la colección actual que estén
Collection otra) en la colección otra
boolean retainAll( Elimina todos los elementos de la colección que no estén
Collection otra) en la otra
boolean containsAll( Indica si la colección contiene todos los elementos de otra
Collection otra)
Object[] toArray() Convierte la colección en un array de objetos.
Iterator iterator() Obtiene el objeto iterador de la colección
iteradores
La interfaz Iterator (también en java.util) define objetos que permiten recorrer los
elementos de una colección. Los métodos definidos por esta interfaz son:
método uso
Object next() Obtiene el siguiente objeto de la colección. Si se ha
llegado al final de la colección y se intenta seguir, da
lugar a una excepción de tipo:
NoSuchElementException (que deriva a su vez de
RunTimeException)
boolean hasNext() Indica si hay un elemento siguiente (y así evita la
excepción).
void remove() Elimina el último elemento devuelto por next
110
© Jorge Sánchez Asenjo’ 2004
Iterator it=colecciónString.iterator();
while(it.hasNext()){
String s=(String)it.next(); System.out.println(s);}
Listas enlazadas
interfaz List
List es una interfaz (de java.util) que se utiliza para definir listas enlazadas. Las listas
enlazadas son colecciones de datos en las que importa la posición de los objetos. Deriva
de la interfaz Collection por lo que hereda todos sus métodos. Pero los interesantes son
los que aporta esta interfaz:
método uso
void add(int índice, Object elemento) Añade el elemento indicado en la posición
índice de la lista
void remove(int índice) Elimina el elemento cuya posición en la
colección la da el parámetro índice
Object set(int índice, Object elemento) Sustituye el elemento número índice por
uno nuevo. Devuelve además el elemento
antiguo
int indexOf(Object elemento) Devuelve la posición del elemento. Si no lo
encuentra, devuelve -1
int lastIndexOf(Object elemento) Devuelve la posición del elemento
comenzando a buscarle por el final. Si no lo
encuentra, devuelve -1
void addAll(int índice, Collection Añade todos los elementos de una colección
elemento) a una posición dada.
ListIterator listIterator() Obtiene el iterador de lista que permite
recorrer los elementos de la lista
ListIterator listIterator(int índice) Obtiene el iterador de lista que permite
recorrer los elementos de la lista. El iterador
se coloca inicialmente apuntando al
elemento cuyo índice en la colección es el
indicado.
ListIterator
Es un interfaz que define clases de objetos para recorrer listas. Es heredera de la interfaz
Iterator. Aporta los siguientes métodos
método uso
void add(Object elemento) Añade el elemento delante de la posición
actual del iterador
void set(Object elemento) Sustituye el elemento señalado por el
iterador, por el elemento indicado
111
Manual de Java
Clases fundamentales (II). Colecciones
método uso
Object previous() Obtiene el elemento previo al actual. Si no lo
hay provoca excepción:
NoSuchElementException
boolean hasPrevious() Indica si hay elemento anterior al
actualmente señalado por el iterador
int nextIndex() Obtiene el índice del elemento siguiente
int previousIndex() Obtiene el índice del elemento anterior
List subList(int desde, int hasta) Obtiene una lista con los elementos que van
de la posición desde a la posición hasta
clase ArrayList
Implementa la interfaz List. Está pensada para crear listas en las cuales se aumenta el
final de la lista frecuentemente. Disponible desde la versión 1.2
Posee tres constructores:
clase LinkedList
Crea listas de adición doble (desde el principio y el final). Implementa la interfaz List.
Desde este clase es sencillo implantar estructuras en forma de pila o de cola. Añade los
métodos:
método uso
Object getFirst() Obtiene el primer elemento de la lista
Object getLast() Obtiene el último elemento de la lista
void addFirst(Object o) Añade el objeto al principio de la lista
void addLast(Object o) Añade el objeto al final de la lista
void removeFirst() Borra el primer elemento
void removeLast() Borra el último elemento
interfaz Set
Define métodos para crear listas dinámicas de elementos sin duplicados. Deriva de
Collection. Es el método equals el que se encarga de determinar si dos objetos son
duplicados en la lista (habrá que redefinir este método para que funcione
adecuadamente).
112
© Jorge Sánchez Asenjo’ 2004
Posee los mismos métodos que la interfaz Collection. La diferencia está en el uso
de duplicados.
clase HashSet
Implementa la interfaz anterior.
árboles. SortedSet
Un árbol es una colección ordenada de elementos. Al recorrer esta estructura, los datos
aparecen automáticamente en el orden correcto. La adición de elementos es más lenta,
pero su recorrido ordenado es mucho más eficiente.
La interfaz SortedSet es la encargada de definir esta estructura. Esta interfaz
deriva de Collection y añade estos métodos:
método uso
Object first() Obtiene el primer elemento del árbol (el más
pequeño)
Object last() Obtiene el último elemento del árbol (el más
grande)
SortedSet headSet(Object o) Obtiene un SortedSet que contendrá todos
los elementos menores que el objeto o.
SortedSet tailSet(Object o) Obtiene un SortedSet que contendrá todos
los elementos mayores que el objeto o.
SortedSet subSet(Object menor, Object Obtiene un SortedSet que contendrá todos
mayor) los elementos del árbol cuyos valores
ordenados estén entre el menor y mayor
objeto indicado
Comparator comparator() Obtiene el objeto comparador de la lista
El resto de métodos son los de la interfaz Collection (sobre todo add y remove). La
clase TreeSet implementa esta interfaz.
comparaciones
El problema es que los objetos tienen que poder ser comparados para determinar su
orden en el árbol. Esto implica implementar la interfaz Comparable de Java (está en
java.lang). Esta interfaz define el método compareTo que utiliza como argumento un
objeto a comparar y que devuelve 0 si los objetos son iguales, 1 si el primero es mayor
que el segundo y -1 en caso contrario.
Con lo cual los objetos a incluir en un TreeSet deben implementar Comparator y esto
les obliga a redefinir el método compareTo (recordando que su argumento es de tipo
Object).
Otra posibilidad es utilizar un objeto Comparator. Esta es otra interfaz que define
el método compare al que se le pasan dos objetos. Su resultado es como el de
compareTo (0 si son iguales, 1 si el primero es mayor y -1 si el segundo es mayor). Para
que un árbol utilice este tipo de objetos se les pasa como argumentos en su creación.
113
Manual de Java
Clases fundamentales (II). Colecciones
mapas
Permiten definir colecciones de elementos que poseen pares de datos clave-valor. Esto
se utiliza para localizar valores en función de la clave que poseen. Son muy interesantes
y rápidos. Es la nueva implementación de tablas hash (ver tablas hash, más adelante).
Métodos:
método uso
Object get(Object clave) Devuelve el objeto que posee la clave
indicada
Object put(Object clave, Object valor) Coloca el par clave-valor en el mapa. Si la
clave ya existiera, sobrescribe el anterior
valor y devuelve el objeto antiguo. Si esa
clave no aparecía en la lista, devuelve null
Object remove(Object clave) Elimina de la lista el valor asociado a esa
clave.
boolean containsKey(Object clave) Indica si el mapa posee la clave señalada
boolean containsValue(Object valor) Indica si el mapa posee el valor señalado
void putAll(Map mapa) Añade todo el mapa al mapa actual
Set keySet() Obtiene un objeto Set creado a partir de las
claves del mapa
Collection values() Obtiene la colección de valores del mapa
Set entrySet() Devuelve una lista formada por objetos
Map.Entry
método uso
Object getKey() Obtiene la clave del elemento actual
Map.Entry
Object getValue() Obtiene el valor
Object setValue(Object valor) Cambia el valor y devuelve el valor anterior
del objeto actual
114
© Jorge Sánchez Asenjo’ 2004
clase Vector
La clase Vector implementa la interfaz List. Es una clase veterana casi calcada a la
clase ArrayList. En las primeras versiones de Java era la única posibilidad de
implementar arrays dinámicos. Actualmente sólo se recomienda su uso si se utiliza una
estructura dinámica para usar con varios threads.
Esto se debe a que esta clase implementa todos los métodos con la opción
synchronized. Como esta opción hace que un método se ejecute más lentamente, se
recomienda suplantar su uso por la clase ArrayList en los casos en los que la estructura
dinámica no requiera ser sincronizada.
Otra diferencia es que permite utilizar la interfaz Enumeration para recorrer la
lista de vectores. Las variables Enumeration tienen dos métodos hasMoreElements
que indica si el vector posee más elementos y el método nextElement que devuelve el
siguiente el elemento del vector (si no existiera da lugar a la excepción
NoSuchElementException). La variable Enumeration de un vector se obtiene con el
método Elements que devuelve una variable Enumeration.
clase Stack
Es una clase derivada de la anterior usada para crear estructuras de pilas. Las pilas son
estructuras dinámicas en las que los elementos se añaden por arriba y se obtienen
primero los últimos elementos añadidos. Sus métodos son:
método uso
Object push(Object elemento) Coloca un nuevo elemento en la pila
Object pop() Retira el último elemento añadido a la pila.
Si la pila está vacía, causa una excepción de
tipo EmptyStackException (derivada de
RuntimeException).
Object peek() Obtiene el último elemento de la pila, pero
sin retirarlo.
tablas Hash
La clase HashTable implementa la clase anterior y proporciona métodos para manejar
la estructura de los datos. Actualmente está en desuso ya que hay clases más poderosas.
En estas tablas cada objeto posee un código hash que es procesado rápidamente. Los
elementos que tengan el mismo código hash, forman una lista enlazada. Con lo cual una
tabla hash es una lista de listas enlazadas, asociadas con el mismo código.
115
Manual de Java
Clases fundamentales (II). Colecciones
Cada elemento a añadir en una tabla hash posee una clave y un valor. Ambos
elementos son de tipo Object. La clave está pensada para ser buscada de forma rápida.
La idea es que a partir de la clave obtenemos el objeto.
En las tablas hash es fundamental la obtención del código hash de cada objeto. Esto
lo realiza el método hashCode que permite conocer el código hash de un objeto. Este
método está implementado en la clase Object, pero a veces hay que redefinirlo en las
clases de usuario para que funcione de manera conveniente. En cualquier caso, con los
mismos datos, el algoritmo hashCode, obtiene el mismo código.
No obstante el método hasCode se puede redefinir para calcular el código de la
forma que se estime conveniente.
Métodos de Hashtable:
método uso
Object put(Object clave, Object elemento) Asocia la clave indicada al elemento.
Devuelve excepción
NullPointerException si la clave es nula.
Devuelve el valor previo para esa misma
clave
Object get(Object key) Obtiene el valor asociado a esa clave.
Object remove(Object key) Elimina el valor asociado a la clave en la
tabla hash. Además devuelve ese valor
int size() Obtiene el número de claves de la tabla hash
Enumeration keys() Obtiene un objeto de enumeración para
recorrer las claves de la tabla
116
© Jorge Sánchez Asenjo’ 2004
método uso
Enumeration element() Obtiene un objeto de enumeración para
recorrer los valores de la tabla
Set keySet() (versión 1.2) Obtiene un objeto Set con los
valores de las claves
Set entrySet() (versión 1.2) Obtiene un objeto Set con los
valores de la tabla
boolean containsKey(Object key) true si la clave está en la tabla
boolean containsValue(Object valor) true si el valor está en la tabla
la clase Collections
Hay una clase llamada Collections (no confundir con la interfaz Collection) que
permite obtener fácilmente colecciones especiales, esto es lo que se conoce como
envoltorio. Sus métodos son:
método uso
static Collection Obtiene una colección con métodos
synchronizedCollection(Collection c) sincronizados a partir de la colección c
static List synchronizedList(List c) Obtiene una lista con métodos sincronizados
a partir de la lista c
static Set synchronizedSet(Set c) Obtiene una tabla hash sincronizada a partir
de la tabla hash c
static Set synchronizedSortedSet( Obtiene un árbol sincronizado a partir del
SortedSet c) árbol c
static Map synchronizedMap(Map c) Obtiene un mapa sincronizado a partir del
mapa c
static SortedMap Obtiene un mapa ordenado sincronizado a
synchronizedSortedMap(SortedMap c) partir de c
static Collection Obtiene una colección de sólo lectura a
unmodifiableCollection(Collection c) partir de la colección c
static List unmodifiableList(List c) Obtiene una lista de sólo lectura a partir de
la lista c
static Set unmodifiableSet(Set c) Obtiene una tabla hash de sólo lectura a
partir de la tabla hash c
static Set unmodifiableSortedSet( Obtiene un árbol de sólo lectura a partir de
SortedSet c) el árbol c
static Map unmodifiableMap(Map c) Obtiene un mapa de sólo lectura a partir del
mapa c
static SortedMap Obtiene un mapa ordenado de sólo lectura a
unmodifiableSortedMap(SortedMap c) partir de c
static void sort(List l) Ordena la lista
static void sort(List l, Comparator c) Ordena la lista basándose en el comparador
indicado
117
Manual de Java
Clases fundamentales (II). Colecciones
método uso
static int binarySearch(List l, Object o) Busca de forma binaria el objeto en la lista
(la lista tiene que estar ordenada en
ascendente)
static int binarySearch(List l, Object o, Busca de forma binaria el objeto en la lista.
Comparator c) La lista tiene que estar ordenada en
ascendente usando el objeto comparador c
118
clases fundamentales (y III)
números grandes
Cuando se manipulan números sobre los que se requiere gran precisión, los tipos
estándar de Java (int, long, double, etc.) se quedan cortos. Por ello en el paquete
java.math disponemos de dos clases dedicadas a la precisión de números.
clase BigInteger
Se utiliza para cuando se desean almacenar números que sobrepasan los 64 bits del tipo
long.
creación
constructor uso
BigInteger(String texto) Crea un objeto para enteros grandes usando
throws NumberFormatException el número representado por el texto. En el
caso de que el número no sea válido se lanza
una excepción
NumberFormatException
BigInteger(String texto, int base) Constructor idéntico al anterior, excepto en
throws NumberFormatException que se utiliza una base numérica
determinada por el parámetro base.
BigInteger(int tamaño, Random r) Genera un número entero largo aleatorio. El
número aleatorio abarca el tamaño indicado
por el primer parámetro, que se refiere al
número de bits que ocupará el número.
Otra forma de crear es mediante el método estático valueOf al cual se le puede pasar
un entero long a partir del cual se devuelve un BigInteger. Ejemplo:
BigInteger bi=BigInteger.valueOf(2500);
métodos
método uso
BigInteger abs() Obtiene el valor absoluto del número.
BigInteger add(BigInteger entero) Devuelve el resultado de sumar el entero
actual con el pasado como parámetro
int bitCount() Devuelve el número de bits necesarios para
representar el número.
int compareTo(BigInteger entero) Compara el entero actual con el utilizado
como parámetro. Devuelve -1 si el segundo
es mayor que el primero, 0 si son iguales y 1
si el primero era mayor.
BigInteger divide(BigInteger entero) Devuelve el resultado de dividir el entero
actual entre el parámetro
119
Manual de Java
Clases fundamentales (y III)
método uso
double doubleValue() Obtiene el valor del entero en forma de
número double.
boolean equals(Object o) Compara el objeto o con el entero actual y
devuelve true si son iguales
double floatValue() Obtiene el valor del entero en forma de
número float.
BigDecimal max(BigDecimal decimal) Devuelve el mayor de los dos números
BigDecimal min(BigDecimal decimal) Devuelve el menor de los dos números
BigInteger mod(BigInteger entero) Devuelve el resto que se obtiene de dividir el
número actual entre el que se pasa como
parámetro
BigInteger multiply(BigInteger entero) Multiplica los dos números y devuelve el
resultado.
BigInteger negate() Devuelve el número multiplicado por menos
uno.
BigInteger probablePrime(int bits, Calcula un número primo cuyo tamaño en
Random r) bits es el indicado y que es generado a partir
el objeto aleatorio r. La probabilidad de que
el número no sea primo es de 2-100
BigInteger subtract(BigInteger entero) Resta el entero actual menos el que se recibe
como par
BigInteger toBigInteger() Convierte el decimal en BigInteger
String toString() Obtiene el número en forma de cadena
String toString(int radio) Obtiene el número en forma de cadena
usando la base indicada
clase BigDecimal
Se utiliza con más frecuencia, representa números reales de gran precisión. Se usa una
escala de forma que el valor de la misma indica la precisión de los decimales. El
redondeo se realiza a través de una de estas constantes:
constante descripción
static int ROUND_CEILING Redondea hacia el infinito positivo
static int ROUND_DOWN Redondea hacia el número 0
static int ROUND_FLOOR Hacia el infinito negativo
static int ROUND_HALF_DOWN Redondea hacia el valor del dígito conexo o
cero si coinciden ambos
static int Redondea hacia el valor del dígito conexo o
ROUND_HALF_EVEN a un número par si coinciden
static int ROUND_HALF_UP Redondea hacia el valor del dígito conexo o
se alejan del cero si coinciden
static int Se presentan los valores sin redondeos
ROUND_UNNECESSARY
120
© Jorge Sánchez Asenjo’ 2004
constante descripción
static int ROUND_UP Se redondea alejándose del cero
constructores
constructor uso
BigDecimal(BigInteger enteroGrande) Crea un BigDecimal a partir de un
BigInteger
BigDecimal(BigInteger enteroGrande, int Crea un BigDecimal a partir de un
escala) BigInteger y coloca la escala indicada (la
escala determina la precisión de los
decimales)
BigDecimal(String texto) Crea un objeto para decimales grandes
throws NumberFormatException usando el número representado por el texto.
En el caso de que el número no sea válido se
lanza una excepción
NumberFormatException
El formato del número suele estar en
notación científica (2.3e+21)
BigDecimal (double n) Crea un decimal grande a partir del número
doble n
métodos
método uso
BigDecimal abs() Obtiene el valor absoluto del número.
BigDecimal add(BigDecimal decimal) Devuelve el resultado de sumar el decimal
actual con el pasado como parámetro
int bitCount() Devuelve el número de bits necesarios para
representar el número.
int compareTo(BigDecimal decimal) Compara el decimal actual con el utilizado
como parámetro. Devuelve -1 si el segundo
es mayor que el primero, 0 si son iguales y 1
si el primero era mayor.
BigDecimal divide(BigDecimal decimal, Devuelve el resultado de dividir el decimal
int redondeo) actual entre el decimal usado como
parámetro. Se le indica el modo de
redondeo que es una de las constantes
descritas anteriormente.
BigDecimal divide(BigDecimal decimal, Idéntica a la anterior, sólo que ahora
int escala, int redondeo) permite utilizar una escala concreta.
double doubleValue() Obtiene el valor del decimal en forma de
número double.
double floatValue() Obtiene el valor del decimal en forma de
número float.
boolean equals(Object o) Compara el objeto o con el entero actual y
devuelve true si son iguales
121
Manual de Java
Clases fundamentales (y III)
método uso
BigDecimal max(BigDecimal decimal) Devuelve el mayor de los dos números
BigDecimal min(BigDecimal decimal) Devuelve el menor de los dos números
BigDecimal multiply(BigDecimal Multiplica los dos números y devuelve el
decimal) resultado.
BigDecimal negate() Devuelve el número multiplicado por menos
uno.
BigDecimal subtract(BigDecimal Resta el entero actual menos el que se recibe
decimal) como par
int scale() Obtiene la escala del número
void setScale(int escala) Modifica la escala del número
String toString() Obtiene el número en forma de cadena
la clase Locale
Toda la cuestión de la portabilidad del código a diversas lenguas, gira en torno a la clase
Locale (en el paquete java.util). Un objeto Locale identifica una configuración
regional (código de país y código de idioma). En muchos casos los idiomas soportados
están definidos mediante una serie de constantes, que son:
constante significado
static Locale CANADA País Canadá
static Locale CANADA_FRENCH Idioma francés de Canadá
static Locale CHINA País China
static Locale CHINESE Idioma chino
static Locale ENGLISH Idioma inglés
static Locale FRANCE País Francia
static Locale FRENCH Idioma francés
static Locale GERMAN Idioma alemán
static Locale GERMANY País Alemania
static Locale ITALIAN Idioma italiano
static Locale ITALY País Italia
static Locale JAPAN País Japón
static Locale JAPANESH Idioma japonés
static Locale KOREA País corea del sur
static Locale KOREAN Idioma coreano
static Locale SIMPLIFIED_CHINESE Idioma chino simplificado
static Locale TAIWAN País Taiwán
static Locale TRADITIONAL_CHINESE Idioma chino tradicional
122
© Jorge Sánchez Asenjo’ 2004
constante significado
static Locale UK País Reino Unido
static Locale US País Estados Unidos
Locale l=Locale.ITALY;
constructor uso
Locale(String códigoLenguaje) Crea un objeto Locale utilizando el código
de lenguaje indicado
Locale(String códigoLenguaje, String Crea un objeto Locale utilizando los
códigoPaís) códigos de lenguaje y país indicados
indicado
métodos de Locale
método descripción
String getCountry() Escribe el código del país del objeto Locale
(ES para España)
String getDisplayCountry() Escribe el nombre del país del objeto Locale
(España)
String getLanguage() Escribe el código del idioma del objeto
Locale (es para Español)
String getDisplayLanguage() Escribe el nombre del idioma (español)
String getDisplayName() Obtiene el texto completo de descripción del
objeto local (español España)
métodos estáticos
Permiten obtener información sobre la configuración local de la máquina virtual de
Java en ejecución.
método descripción
static Locale[] getAvailableLocales() Devuelve un array de objetos Locales con la
lista completa de objetos locales disponibles
en la máquina virtual en ejecución.
static Locale getDefaultLocale() Obtiene el objeto local que se está utilizando
en la máquina actual
123
Manual de Java
Clases fundamentales (y III)
método descripción
static String[] getISOCountries() Obtiene una lista con los códigos de países
de dos letras disponibles en la máquina
actual
static String[] getISOLanguages Obtiene una lista con los códigos de idiomas
letras disponibles en la máquina actual
static void setDefault(Locale l) Modifica el objeto Locale por defecto de la
máquina actual
formatos numéricos
Ya se vio anteriormente la clase DateFormat (ver clase DateFormat, página 90). En
el mismo paquete de ésta (java.text) existen clases dedicadas también al formato de
números. Para ello disponemos de NumberFormat.
Los objetos NumberFormat sirven para formatear números, a fin de mostrarles
de forma conveniente. cada objeto NumberFormat representa un formato numérico.
creación
Hay tres formas de crear formatos numéricos. Las tres formas se realizan con métodos
estáticos (al estilo de DateFormat):
método descripción
static NumberFormat getInstance() Obtiene un objeto de formato numérico
según las preferencias locales actuales
static NumberFormat Obtiene un objeto de formato numérico
getInstance(Locale local) basado en las preferencias del objeto local
indicado como parámetro
static NumberFormat Obtiene un objeto de formato de moneda
getCurrencyInstance() según las preferencias locales actuales
static NumberFormat Obtiene un objeto de formato de moneda
geCurrencyInstance(Locale local) basado en las preferencias del objeto local
indicado como parámetro
static NumberFormat Obtiene un objeto de formato porcentaje
getPercentInstance() basado en las preferencias del objeto local
indicado como parámetro
static NumberFormat Obtiene un objeto de formato en porcentaje
getPercentInstance(Locale local) (0,2 se escribiría 20%) basado en las
preferencias del objeto local indicado como
parámetro
uso
El método format devuelve una cadena que utiliza el formato del objeto para mostrar
el número que recibe como parámetro. Ejemplo:
NumberFormat nf=NumberFormat.getCurrencyInstance();
System.out.println(nf.format(1248.32));
//sale 1.248,32 €
124
© Jorge Sánchez Asenjo’ 2004
métodos
Hay métodos que permiten variar el resultado del objeto NumberFormat:
método descripción
boolean getGroupingUsed() Indica si se está utilizando el separador de
miles
int getMinimumFractionDigit() Devuelve el número mínimo de decimales
con el que se formateará a los números
int getMaximumFractionDigit() Devuelve el número máximo de decimales
con el que se formateará a los números
int getMinimumIntegerDigit() Devuelve el número mínimo de números
enteros (los que están ala izquierda del
decimal) con el que se formateará a los
números.
Si hay menos números que el mínimo, se
rellenarán los que falta con ceros a la
izquierda
int getMaximumIntegerDigit() Devuelve el número máximo de números
enteros con el que se formateará a los
números
void setGroupingUsed(boolean uso) Modifica el hecho de que se muestren o no
los separadores de miles. Con valor true se
mostrarán
void setMinimumFractionDigit(int n) Establece el número mínimo de decimales
void setMaximumFractionDigit(int n) Establece el número máximo de decimales
void setMinimumIntegerDigit(int n) Establece el número mínimo de enteros
void setMaximumIntegerDigit(int n) Establece el número máximo de enteros
Propiedades
Las propiedades permiten cargar valores de entorno, esto es valores que se utilizan
durante la ejecución de los programas, pero que se almacenan de modo independiente a
estos.
Es la clase Properties (java.util) la encargada de implementar las propiedades.
Esta clase deriva de Hashtable con lo que los valores se almacenan en una lista de tipo
clave / valor. En el caso de Properties la lista se construye con pares nombre / valor;
donde el nombre es el nombre de la propiedad (que se puede agrupar mediante puntos,
como los paquetes de Java).
uso
El método para colocar elementos en la lista de propiedades es put. Desde la versión 1.2
se utiliza setProperty (equivalente a put, pero más coherente con el método de
obtención de propiedades getProperty). Este método usa dos cadenas, la primera es el
nombre de la propiedad y el segundo su valor:
125
Manual de Java
Clases fundamentales (y III)
prop1.setProperty(“MiPrograma.maxResult”,”134”);
prop1.setProperty(“MiPrograma.minResult”,”5”);
Para leer los valores se usa getProperty, que devuelve el valor del nombre de
propiedad buscado o null si no lo encuentra.
String mResult=prop1.getProperty(“MiPrograma.maxResult”);
grabar en disco
La ventaja de la clase Properties es que las tablas de propiedades se pueden almacenar
en discos mediante el método store. El método store (que sustituye al obsoleto, save)
posee dos parámetros, el primero es un objeto de tipo OutputStream (ver
InputStream/ OutputStream página 93) referido a una corriente de datos de salida en la
que se grabará (en forma de texto ASCII) el contenido de la tabla de propiedades. El
segundo parámetro es la cabecera de ese archivo. Por ejemplo:
prop1.save(System.out,”parámetros de programa”);
try{
File f=new File(“d:/propiedades.out”);
FileOutputStream fos=new FileOutputStream(f);
prop1,save(fos,”parámetros de programa”);
}
catch(FileNotFoundException fnf){
System.out.println(“No se encontró el archivo”);
}
/*en ambos casos la salida sería:
#parámetros de programa
#Wed Apr 28 00:01:30 CEST 2004
MiPrograma.maxResult=134
MiPrograma.minResult=5
*/
A su vez, el método load permite leer un archivo de propiedades. Este método devuelve
un objeto Properties a partir de una corriente InputStream.
En ambos métodos (load y store) hay que capturar las posibles excepciones de tipo
IOException que se produjeran.
126
© Jorge Sánchez Asenjo’ 2004
propiedad descripción
java.vendor Fabricante de la máquina Java en ejecución
java.vendor.url Dirección de la web del fabricante
java.version Versión de java en ejecución
java.home Directorio de instalación del kit de java en la
máquina actual (inaccesible desde un
applet)
os.arch Arquitectura de Sistema Operativo
os.version Versión de sistema operativo
os.name Nombre del sistema operativo
file.separator Separador de carpetas (\ en Windows)
path.separator Separador de rutas (; en Windows)
line.separator Separador de líneas (por ejemplo \n)
user.name Nombre de usuario (inaccesible desde un
applet)
user.home Ruta a la carpeta de usuario (inaccesible
desde un applet)
user.dir Carpeta actual de trabajo (inaccesible desde
un applet)
user.language Código del lenguaje que utiliza el usuario
(por ejemplo “es”)
user.country Código del país del usuario (por ejemplo
“ES”)
user.timezone Código de la zona horaria del usuario (por
ejemplo Europe/Paris)
temporizador
Desde la versión 1.3 de Java hay dos clases que permiten trabajar con temporizadores.
Son java.util.Timer y java.util.TimerTask.
clase TimerTask
Representa una tarea de temporizador; es decir una tarea que se asignará a un
determinado temporizador.
Se trata de una clase abstracta, por lo que hay que definir descendientes para poder
utilizarla. Cuando se redefine una subclase se debe definir el método abstracto run.
Este método es el que realiza la operación que luego se asignará a un temporizador.
El método cancel permite cancelar la ejecución de la tarea. Si la tarea ya había sido
ejecutada, devuelve false.
clase Timer
Es la que representa al temporizador. El temporizador se crea usando el constructor por
defecto. Mediante el método schedule se consigue programar el temporizador. Este
127
Manual de Java
Clases fundamentales (y III)
método tiene como primer parámetro un objeto TimerTask que tiene que haber
programado la acción a realizar cuando se cumpla el tiempo del temporizador.
El segundo parámetro de schedule es un objeto Date que sirve para indicar
cuándo se ejecutará el temporizador.
En este caso se ejecuta la tarea en cada segundo. El segundo parámetro en lugar de ser
un objeto Date() puede ser también un número de milisegundos desde el momento
actual. Así la última línea podía hacerse también con:
temp.schedule(tarea, 0, 1000);
Finalmente añadir que el método cancel, que no tiene argumentos, permite finalizar el
temporizador actual.
128
Swing
AWT y Swing
Swing es un conjunto de clases desarrolladas por primera vez para Java 1.2 (el llamado
Java2), para mejorar el anterior paquete que implementaba clases para fabricar
interfaces de usuario, el llamado AWT (Abstract Window Tools) que aún se usa
bastante en las aplicaciones Java.
Tanto Swing como AWT forman parte de una colección de clases llamada JFC (Java
Foundation Classes) que incluyen paquetes dedicados a la programación de interfaces
gráficos (así como a la producción multimedia).
Uno de los problemas frecuentes de la programación clásica era como programar
interfaces de usuario, ya que esto implicaba tener que utilizar las API propias del
Sistema Operativo y esto provocaba que el código no fuera transportable a otros
sistemas.
AWT fue la primera solución a este problema propuesta por Java. AWT está formada
por un conjunto de clases que no dependen del sistema operativo, pero que proponen
una serie de clases para la programación de GUIs (graphic users interfaces, interfaces
gráficos de usuario; cualquier entorno de comunicación entre el ordenador y el usuario).
AWT usa clases gráficas comunes a todos los sistemas operativos gráficos y luego la
máquina virtual traduce esa clase a la forma que tenga en el sistema concreto en el que
se ejecutó el programa, sin importar que dicho sistema sea un sistema X, McIntosh o
Windows. La popularidad de AWT desbordó las expectativas de la propia empresa Sun.
La clave de AWT era el uso de componentes iguales (peers). Los elementos de los
interfaces AWT dejaban al sistema la responsabilidad de generar realmente los
componentes. Eso aseguraba una vista coherente respecto al sistema en el que se
ejecutaba el programa. El problema es que ante la grandiosidad de la imagen en
Windows y Mac OS, otros sistemas quedaban peor ante la misma aplicación.
Por ello (y por otros problemas) aparece Swing en la versión 1.2 como parte del JFC
(Java Foundation Classes) que es el kit de clases más importante de Java para las
producciones gráficas.
Los problemas de AWT eran:
Swing aporta muchas más clases, consume menos recursos y construye mejor la
apariencia de los programas. En cualquier caso, AWT no desaparece; simplemente se
añade a las nuevas capacidades Swing
componentes
Los componentes son los elementos básicos de la programación con Swing. Todo lo que
se ve en un GUI de Java es un componente. Los componentes se colocan en otros
elementos llamados contenedores que sirven para agrupar componentes. Un
129
Manual de Java
Gráficos Java. Java 2D
JLabel JLabel
JText Field
JList JViewPort
Object
JPasswordField
JMenuBar JColorChooser
JEditorPane
JPanel JTextComponent
JEditorPane
Component JPopupMenu
JScrollBar
JToggleButton
JScrollPane
Containert
java.awt JTable JCheckBox
JTree JRadioButton
JComponent JInternalFrame
JTButton
JOptionPane
JTMeniItem
JProgressBar
JRootPane
JMenu
JSeparator
JRadioButtonMenuItem
javax.swing JSlider AbstractButton
JCheckButtonMenuItem
JScrollBar
JEditorPane
JSplitPane
JEditorPane
JTabbedPane
JToolBar
130
© Jorge Sánchez Asenjo’ 2004
La clase JComponent posee métodos para controlar la apariencia del objeto. Por
ejemplo: la visibilidad, tamaño, posición, tipo de letra, color,... Al dibujar un
componente, se le asigna un dispositivo de presentación.
Además posee métodos que controlan el comportamiento del componente. Cuando
el usuario ejecuta una acción sobre un componente, entonces se crea un objeto de
evento que describe el suceso. El objeto de evento se envía a objetos de control de
eventos (Listeners). Los eventos son uno de los pilares de la construcción de Interfaces
de usuario y una de las bases de la comunicación entre objetos.
pares
En AWT se usaban interfaces de pares. Esto significaba que cada componente creado
con AWT, creaba un par igual correspondiente al mundo real. Es decir al crear el botón,
existía el botón virtual creado en Java y el que realmente era dibujado en la pantalla (el
real). El programador no necesita saber de la existencia de ese par, la comunicación del
objeto creado con su par corría por cuenta de AWT.
Este modelo de componentes se elimina en Swing. En Swing se habla de
componentes de peso ligero. La clase JComponent que es la raíz de clases Swing, no
utiliza un par, cada componente es independiente del sistema de ventanas principal. Se
dibujan a sí mismos y responden a los eventos de usuario sin ayuda de un par.
La ventaja de este modelo es que requiere menos recursos y que su modificación
visual es más ágil y efectiva.
modelo/vista/controlador
Se trata del modelo fundamental del trabajo con interfaces de usuario por parte de
Swing. Consiste en tres formas de abstracción. Un mismo objeto se ve de esas tres
formas:
Por ejemplo un array de cadenas que contenga los meses del año, podría ser el modelo
de un cuadro combinado de Windows. Un cuadro combinado es un rectángulo con un
botón con una flecha que permite elegir una opción de una lista. La vista de ese cuadro
es el hecho de mostrar esas cadenas en ese rectángulo con flecha. Y el controlador es
la capa software que permite capturar el clic del ratón cuando apunta a la flecha del
control a fin de mostrar y seleccionar el contenido.
métodos de JComponent
La clase JComponent es abstracta, lo cual significa que no puede crear objetos, pero sí
es la superclase de todos los componentes visuales (botones, listas, paneles, applets,...) y
por ello la lista de métodos es interminable, ya que proporciona la funcionalidad de
todos los componentes. Además puesto que deriva de Component y Container tiene
los métodos de estos, por ello aún es más grande esta lista. Algunos son:
131
Manual de Java
Gráficos Java. Java 2D
métodos de información
método uso
String getName() Obtiene el nombre del componente
void setname(String nombre) cambia el nombre del componente
Container getParent() Devuelve el contenedor que sostiene a este
componente
132
© Jorge Sánchez Asenjo’ 2004
método uso
void putClientProperty(Object
clave, Object valor)
Color(int rojo, int verde, int azul). Construye un objeto color indicando los
niveles de rojo, verde y azul.
Color(int rgb). Crea un color usando un único entero que indica los niveles de
rojo, verde y azul. Se suele emplear con 6 dígitos en hexadecimal. Ejemplo:
0xFFCC33
Color(int rojo, int verde, int azul, int alfa). Construye un objeto color
indicando los niveles de rojo, verde y azul, y un valor de 0 a 255 indicando el valor
alfa (alfa indica la transparencia).
Color(int rgb). Crea un color usando un único entero que indica los niveles de
rojo, verde y azul. Se suele emplear con 6 dígitos en hexadecimal. Ejemplo:
0xFFCC33
métodos de dibujo
método uso
void paint(Graphics p) Pinta el componente y sus subcomponentes.
Delega sus funciones en los tres métodos
siguientes
133
Manual de Java
Gráficos Java. Java 2D
método uso
void paintComponent(Graphics p) Pinta sólo este componente. Este es el método
recomendado en Swing para dibujar en un
componente.
void paintComponents(Graphics p) Llama a los métodos de pintura de todos los
componentes de la ventana.
void paintChildren(Graphics p) Pinta los componentes hijo de este componente
void paintBorder(Graphics p) Pinta el borde del componente
protected Graphics Obtiene el objeto gráfico utilizado para dibujar
getComponentGraphics(Graphics g) el componente. El argumento es el objeto
gráfico original. El otro es el tratado por el
componente.
void update(Graphics g) Llama a paint
enfocar
Para que un componente sea al que van dirigidas las pulsaciones de las teclas o, dicho
de otra forma, el que recibe la interacción del usuario, debe poseer el enfoque (focus).
En muchos casos, el enfoque salta de un control al siguiente pulsando la tecla
tabulador. Varios métodos se encargan de controlar ese salto:
método uso
void requestFocus() Pide el foco para el componente. Será posible,
si este es visible, activado y enfocable
focusable). El contenedor del foco también
poseerá el foco.
boolean requestFocusInWindow() Pide el foco para el componente si la ventana
contenedora poseía el foco. Devuelve true si el
foco se puede dar sin problemas. Aunque sólo
el evento FOCUS_GAINED es el encargado de
indicar que el foco ha sido pasado.
Actualmente, debido a que el método anterior
es dependiente de la plataforma, se
recomienda este método siempre que sea
posible.
void transferFocus() Hace que el siguiente componente en la lista de
tabulaciones, obtenga el foco
134
© Jorge Sánchez Asenjo’ 2004
método uso
void transferFocusBackward() El foco pasa al anterior componente en la lista
de tabulaciones.
void setNextFocusableComponent( Hace que el componente c sea el siguiente en la
Component c) lista de tabulaciones.
Component Obtiene el siguiente componente de la lista de
getNextFocusableComponent() tabulaciones.
Contenedores
Son un tipo de componentes pensados para almacenar y manejar otros componentes.
Los objetos JComponent pueden ser contenedores al ser una clase que desciende de
Container que es la clase de los objetos contenedores de AWT.
Para hacer que un componente forme parte de un contenedor, se utiliza el método
add. Mientras que el método remove es el encargado de eliminar un componente.
Ambos métodos proceden de la clase java.awt.Container
Swing posee algunos contenedores especiales. Algunos son:
JFrame. Objeto que representa una ventana típica con bordes, botones de cerrar,
etc.
componentes de un contenedor
Estos métodos de la clase Container permiten obtener información sobre compo-
nentes de un contenedor:
método uso
Component[] getComponents() Devuelve un array de componentes con todos
los componentes correspondientes al conte-
nedor actual
void list(PrintWriter out) Escribe en el objeto PrintWriter indicado, la
lista de componentes.
Component getComponentAt(int x, Indica qué componente se encuentra en esas
int y) coordenadas (calculadas dentro del sistema de
coordenadas del contenedor).
JWindow
Este objeto deriva de la clase java.awt.Window que a su vez deriva de
java.awt.Container. Se trata de un objeto que representa un marco de ventana
135
Manual de Java
Gráficos Java. Java 2D
simple, sin borde, ni ningún elemento. Sin embargo son contenedores a los que se les
puede añadir información. Estos componentes suelen estar dentro de una ventana de
tipo Frame o, mejor, JFrame.
constructores
método uso
JWindow() Crea un marco de ventana típico e indepen-
diente
JWindow(Frame padre) Crea un marco de ventana dentro de la ventana
tipo Frame indicada.
JWindow(Window padre) Crea un marco de ventana dentro de la ventana
indicada.
JWindow(GraphicsConfiguration gc) Crea una nueva ventana usando la configu-
ración gráfica indicada
JWindow(Window padre, Crea una ventana dentro de la padre con la
GraphicsConfiguration gc) configuración de gráficos indicada
JFrame
Los objetos JFrame derivan de la clase Frame que, a su vez deriva, también de la clase
Window, por lo que muchos métodos de esta clase son comunes a la anterior. Los
objetos JFrame son ventanas completas.
constructores
constructor descripción
JFrame() Crea una ventana con la configuración normal.
JFrame(GraphicsConfiguration gc) Crea una nueva ventana usando la configu-
ración gráfica indicada
JFrame(String titulo) Crea una ventana con el título indicado.
JFrame(String titulo, Crea una ventana con el título y la
GraphicsConfiguration gc) configuración gráfica indicada.
métodos
método descripción
void setDefaultCloseOperation(int Indica el modo de cerrar la ventana, en
modo) sustitución del entero se puede utilizar los
siguientes valores:
JFrame.EXIT_ON_CLOSE. Al cerrar
la ventana, se acaba el programa (desde
versión 1.3 de Java)
JFrame.DO_NOTHING_ON_CLOSE.
No se hace nada al cerrar la ventana
JFrame.HIDE_ON_CLOSE. Esconde
la ventana al cerrarla.
JFrame.DISPOSE_ON_CLOSE.
Esconde la ventana y se deshace de ella.
136
© Jorge Sánchez Asenjo’ 2004
método descripción
El programa acaba cuando todas las
ventanas han sido cerradas con esta
opción.
voit setResizable(boolean b) Con true la ventana es cambiable de tamaño,
en false la ventana tiene tamaño fijo.
void setTitle(String t) Cambia el título de la ventana
void pack() Ajusta la ventana a tamaño suficiente para
ajustar sus contenidos
void setState(int estado) Coloca la ventana en uno de estos dos estados:
Frame.NORMAL
Frame.ICONOFIED. Minimizada
void setExtendedState(int estado) Idéntica a la anterior pero añade tres estados:
JFrame.MAXIMIZED_HOR.
Maximizada horizontal
JFrame.MAXIMIZED_VER.
Maximizada vertical
JFrame.MAXIMIZED_BOTH
Maximizada completa
JDialog
JDialog deriva de la clase AWT Dialog que es subclase de Window. Representa un
cuadro de diálogo que es una ventana especializada para realizar operaciones complejas.
constructores
método uso
JDialog() Crea una ventana con la configuración normal.
JDialog(Frame propietaria) Crea un nuevo cuadro de diálogo, indicando
como padre la ventana seleccionada
JDialog(Frame propietaria, boolean Crea un nuevo cuadro de diálogo, indicando
modal) como padre la ventana seleccionada.
Poniendo a true el segundo parámetro, el
cuadro de diálogo pasa a ser modal. Una
ventana modal obliga a el usuario a contestar
al cuadro antes de que pueda continuar
trabajando.
JDialog(Frame propietaria, String Crea un cuadro de diálogo perteneciente a la
título) ventana indicada y poniendo el título deseado
137
Manual de Java
Gráficos Java. Java 2D
método uso
JDialog(Frame propietaria, String Crea un cuadro de diálogo perteneciente a la
título, boolean modal) ventana indicada, poniendo el título deseado e
indicando si se desea el cuadro en forma
modal.
JDialog(Frame propietaria, String Lo mismo, pero además indicando una posible
título, boolean modal, configuración gráfica.
GraphicsConfiguration gc)
métodos interesantes
Hay varios métodos que se pueden usar con los objetos JDialog y JFrame
método uso
void toBack() Coloca la ventana al fondo, el resto de ventanas
aparecen por delante
void toFront() Envía la ventana al frente (además le dará el
foco).
void setTitle(String t) Cambia el título de la ventana
String getTitle() Obtiene el título de la página
voit setResizable(boolean b) Con true la ventana es cambiable de tamaño,
en false la ventana tiene tamaño fijo.
void pack() Ajusta la ventana a tamaño suficiente para
ajustar sus contenidos
Este código muestra una ventana ajustada al contenido de una ventana que pone Hola.
138
© Jorge Sánchez Asenjo’ 2004
eventos
En términos de Java, un evento es un objeto que es lanzado por un objeto y enviado a
otro objeto llamado escuchador (listener). Un evento se lanza (o se dispara, fire)
cuando ocurre una determinada situación (un clic de ratón, una pulsación de tecla,...).
La programación de eventos es una de las bases de Java y permite mecanismos de
diseño de programas orientados a las acciones del usuario. Es decir, son las acciones del
usuario las que desencadenan mensajes entre los objetos (el flujo del código del
programa se desvía en función del evento producido, alterando la ejecución normal).
Hay multitud de tipos de eventos, más adelante se señala una lista de los eventos
fundamentales. En su captura hay que tener en cuenta que hay tres objetos implicados:
El objeto fuente. Que es el objeto que lanza los eventos. Dependiendo del tipo
de objeto que sea, puede lanzar unos métodos u otros. Por ejemplo un objeto de
tipo JLabel (etiqueta) puede lanzar eventos de ratón (MouseEvent) pero no de
teclado (KeyEvent). El hecho de que dispare esos eventos no significa que el
programa tenga que, necesariamente, realizar una acción. Sólo se ejecuta una
acción si hay un objeto escuchando.
El objeto de evento. Se trata del objeto que es enviado desde el objeto fuente a
el escuchador. Según el tipo de evento que se haya producido se ejecutará uno u
otro método en el escuchador.
escuchadores de eventos
Cada tipo de evento tiene asociado un interfaz para manejar el evento. A esos interfaces
se les llama escuchadores (Listeners) ya que proporcionan métodos que están a la
espera de que el evento se produzca. Cuando el evento es disparado por el objeto fuente
al que se estaba escuchando, el método manejador del evento se dispara
automáticamente.
Por ejemplo, el método actionPerformed es el encargado de gestionar eventos del
tipo ActionEvent (eventos de acción, se producen, por ejemplo, al hacer clic en un
botón). Este método está implementado en la interfaz ActionListener (implementa
escuchadores de eventos de acción).
Cualquier clase que desee escuchar eventos (los suyos o los de otros objetos) debe
implementar la interfaz (o interfaces) pensada para capturar los eventos del tipo
deseado. Esta interfaz habilita a la clase para poder implementar métodos de gestión de
eventos.
Por ejemplo; un objeto que quiera escuchar eventos ActionEvent, debe
implementar la interfaz ActionListener. Esa interfaz obliga a definir el método ya
comentado actionPerformed. El código de ese método será invocado
automáticamente cuando el objeto fuente produzca un evento de acción.
139
Manual de Java
Gráficos Java. Java 2D
El objeto de evento que se dispara cuando ocurre un suceso. Por ejemplo para
capturar el ratón sería MouseEvent.
La interfaz que tiene que estar implementada en la clase que desea capturar ese
evento. En este ejemplo sería MouseListener, que es la que obliga a la clase del
escuchador a implementar los cuatro métodos de gestión comentados
anteriormente
Sin duda, el más complejo es este último, pero hay que entender que una interfaz lo
único que consigue es dar a una clase la facultad de escuchar (Listen) eventos.
fuentes de eventos
disparar eventos
El objeto fuente permite que un objeto tenga capacidad de enviar eventos. Esto se
consigue mediante un método que comienza por la palabra add seguida por el nombre
140
© Jorge Sánchez Asenjo’ 2004
de la interfaz que captura este tipo de eventos. Este método recibe como parámetro
el objeto escuchador de los eventos.
Esto es más fácil de lo que parece. Para que un objeto fuente, sea escuchado, hay que
indicar quién será el objeto que escuche (que obligadamente deberá implementar la
interfaz relacionada con el evento a escuchar). Cualquier componente puede lanzar
eventos, sólo hay que indicárselo, y eso es lo que hace el método add. Ejemplo:
En el ejemplo anterior se habilita al boton1 para que lance eventos mediante el método
addActionListener. Este método requiere un objeto escuchador que, en este caso,
será la ventana en la que está el botón. Esta ventana tiene que implementar la interfaz
ActionListener para poder escuchar eventos (de hecho el método addActionListener
sólo permite objetos de esta interfaz). Cuando se haga clic con el ratón se llamará al
método actionPerformed de la ventana, que es el método de gestión.
Hay que señalar que una misma fuente puede tener varios objetos escuchando los
eventos (si lanza varios métodos add). Si hay demasiados objetos escuchando eventos,
se produce una excepción del tipo TooManyListenersException
eliminar oyentes
Hay un método remove que sirve para que un oyente del objeto deje de escuchar los
eventos.
objeto de evento.
clase EventObject
Ya se ha comentado que cuando se produce un evento se crea un objeto llamado objeto
de evento. Este objeto es pasado al objeto que está escuchando los eventos.
141
Manual de Java
Gráficos Java. Java 2D
Todos los objetos de evento pertenecen a clases que derivan de EventObject . Esta
es la superclase de todos los objetos de evento. Representa un evento genérico y en la
práctica sólo sirve para definir los métodos comunes a todos los eventos que son:
método uso
Object getSource() Obtiene el objeto que lanzó el evento (método
muy importante)
String toString() Método toString redefinido para mostrar la
información del evento
EventObject
paquete java.beans
AWTEvent PropertyChangeEvent
Key Mouse
Event Event
MouseWheel
Event
clase AWTEvent
Se trata de una clase descendiente de EventObject y padre de todos los eventos para
componentes Swing y AWT. Se encuentra en el paquete java.awt.event. Proporciona
métodos comunes para todas sus clases hijas. Estos son (además de los ya comentados
getSource y toString):
método uso
void getID() Obtiene el identificador de tipo de evento
String paramString() Cadena de estado del evento (se usa sólo para
depuración)
void setSource(Object o) Redirige el evento al objeto o, haciendo que sea
éste el que lance de nuevo el evento.
142
© Jorge Sánchez Asenjo’ 2004
método uso
protected void consume() Consume el evento. Esto es, hace que el evento
deje de ser enviado. No todos los eventos lo
permiten
protected boolean isConsumed() Indica si el evento ya ha sido consumido
eventos InputEvent
Es una de las clases de eventos fundamentales, deriva de ComponentEvent. Hay
varios tipos de evento que derivan de éste. Se trata de los eventos KeyEvent y
MouseEvent. La clase InputEvent viene con unas serie de indicadores que permiten
determinar qué teclas y/o botones de ratón estaban pulsados en el momento del evento.
El método getModifiers devuelve un entero que permite enmascarar con esas
constantes para determinar las teclas y botones pulsados.
Este enmascaramiento se realiza con el operador lógico AND (&) en esta forma:
143
Manual de Java
Gráficos Java. Java 2D
método uso
long getWhen() Devuelve el momento exacto en el que ocurrió
el evento. Lo devuelve en formato long (que
representa los milisegundos acaecidos). Habría
que convertirlo a objeto Date (mediante el
constructor new Date(long)) para así poder
obtener hora, minutos y segundos (y también
la fecha)
boolean isAltDown() Devuelve true si la tecla Alt estaba pulsada
cuando se produjo el evento.
boolean isAltGraphDown() Devuelve true si la tecla Alt estaba pulsada
cuando se produjo el evento.
boolean isAltDown() Devuelve true si la tecla Alterno Gráfico (Alt
Gr) estaba pulsada cuando se produjo el
evento.
boolean isControlDown() Devuelve true si la tecla Control estaba
pulsada cuando se produjo el evento.
boolean isShiftDown() Devuelve true si la tecla Mayúsculas (Shift)
estaba pulsada cuando se produjo el evento.
144
© Jorge Sánchez Asenjo’ 2004
145
Manual de Java
Gráficos Java. Java 2D
146
© Jorge Sánchez Asenjo’ 2004
147
Manual de Java
Gráficos Java. Java 2D
148
© Jorge Sánchez Asenjo’ 2004
149
Manual de Java
Gráficos Java. Java 2D
150
© Jorge Sánchez Asenjo’ 2004
151
Manual de Java
Gráficos Java. Java 2D
152
© Jorge Sánchez Asenjo’ 2004
153
Manual de Java
Gráficos Java. Java 2D
Suponiendo que ventana sea una clase preparada para escuchar eventos de tipo
WindowsEvent, se crea el objeto de evento we. El envío del evento se realiza con el
método dispachEvent.
adaptadores
Para facilitar la gestión de eventos en ciertos casos, Java dispone de las llamadas clases
adaptadores. Gracias a ellas, en muchos casos se evita tener que crear clases sólo para
escuchar eventos. Estas clases son clases de contenido vacío pero que son muy
interesantes para capturas sencillas de eventos.
Todas poseen la palabra adapter en el nombre de clase. Por ejemplo esta es la
definición de la clase MouseAdapter:
Es una clase que implementa el interfaz MouseListener, pero que no define lo que
hace cada método de captura. Eso se suele indicar de manera dinámica:
154
© Jorge Sánchez Asenjo’ 2004
System.out.println(“Hola”);
}};
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
frame.pack();
frame.setVisible(true);
}
}
El evento windowClosing está capturado por una clase adaptadora, cuya efecto es finalizar el
programa cuando se cierra la ventana.
Clases adaptadoras:
ComponentAdapter
ContainerAdapter
FocusAdapter
InternalFrameAdapter
155
Manual de Java
Gráficos Java. Java 2D
KeyAdapter
MouseAdapter
MouseMotionAdapter
PrintJobAdapter
WindowAdapter
cuadros de información
Son cuadros de diálogo que sirven para informar al usuario de un determinado hecho.
Se construyen utilizando los siguientes métodos estáticos:
métodos uso
static void showMessageDialog( Muestra un cuadro de diálogo en el contenedor
Component padre, Object mensaje) padre indicado con un determinado mensaje
static void showMessageDialog( Muestra un cuadro de diálogo en el contenedor
Component padre, Object mensaje, padre indicado con un determinado mensaje,
String título, int tipo) título y tipo.
static void showMessageDialog( Igual que el anterior pero se permite indicar un
Component padre, Object mensaje, icono para acompañar el mensaje
String título, int tipo, Icon i)
Estos son los posibles creadores de este tipo de cuadro. El tipo puede ser una de estas
constantes:
JOptionPane.INFORMATION_MESSAGE.
JOptionPane.ERROR_MESSAGE.
JOptionPane.WARNING_MESSAGE. Aviso
JOptionPane.QUESTION_MESSAGE. Pregunta
Ejemplo:
156
© Jorge Sánchez Asenjo’ 2004
El resultado es:
cuadros de confirmación
La diferencia con los anteriores reside en que en estos hay que capturar la respuesta del
usuario para comprobar si acepta o declina el mensaje. Los métodos estáticos de
creación son:
métodos uso
static int showConfirmDialog( Muestra un cuadro de confirmación en el
Component padre, Object mensaje) componente padre con el mensaje indicado y
botones de Sí, No y Cancelar
static int showConfirmDialog( Muestra cuadro de confirmación con el título y
Component padre, Object mensaje, mensaje reseñados y las opciones indicadas (las
String título, int opciones) opciones se describen al final)
static int showConfirmDialog( Como el anterior pero indicando el tipo de
Component padre, Object mensaje, cuadro (los posibles valores son los indicados en
String título, int opciones, int tipo) la página anterior) y un icono
static int showConfirmDialog( Como el anterior pero indicando un icono.
Component padre, Object mensaje,
String título, int opciones, int tipo,
Icon icono)
Estas otras constantes facilitan el uso del parámetro opciones que sirve para modificar
la funcionalidad del cuadro. Son:
157
Manual de Java
Gráficos Java. Java 2D
Ejemplo:
if (res==JOptionPane.YES_OPTION) System.exit(0);
métodos uso
static String Muestra un cuadro de entrada con el mensaje
showInputDialog(Object mensaje) indicado
static String showInputDialog( Muestra un cuadro de entrada en el componente
Component padre, Object mensaje) padre con el mensaje indicado
static String showInputDialog( Muestra cuadro de entrada con el título y
Component padre, Object mensaje, mensaje reseñados y el tipo que se indica
String título, int tipo)
static Object showInputDialog( Indica además un icono, selecciones posibles y la
Component padre, Object mensaje, selección inicial. El valor devuelto es un objeto
String título, int tipo, Icono icono, Object.
Object[] selección, Object
selecciónInicial)
Todos los métodos devuelven un String en el que se almacena la respuesta del usuario.
En caso de que el usuario cancele el cuadro, devuelve null en la cadena a examinar.
Ejemplo:
El resultado es:
158
© Jorge Sánchez Asenjo’ 2004
159
Manual de Java
Gráficos Java. Java 2D
Programación de gráficos.
Java2D
Java2D
En el tema anterior se habló del conjunto de librerías JFC (Java Foundation Classes),
de la que formaba parte Swing. JFC es enorme y forma parte de ella también una API
para la programación de gráficos en dos dimensiones (2D).
Anteriormente a Swing, los gráficos se dibujaban utilizando las clases AWT. En este
tema se habla de clases Swing, pero se comentarán diferencias respecto a AWT.
paneles de dibujo
A la hora de hacer que los gráficos aparezcan en la pantalla, éstos se han de dibujar
sobre un lienzo. El lienzo es una forma abstracta de indicar que hace falta un marco de
trabajo. Lo malo es que normalmente una aplicación Java se sostiene sobre objetos que
se sostienen en marcos como JFrame por ejemplo. Estos marcos principales en Swing
implementan una interfaz llamada RootPaneContainer que coloca varios paneles
superpuestos a fin de organizar la información. Las clases que implementan esta
estructura son las siguientes:
java.awt
Window Applet
Frame Dialog
javax.swing
RootPaneContainer
Esta interfaz (RootPaneContainer) hace que las clases que la implementan utilicen un
panel raíz para los contenidos llamado JRootPane. Es el nombre de una clase que
implementa una estructura de paneles a todos los marcos ya comentados. La estructura
que propone es la indica en las siguientes ilustraciones:
160
© Jorge Sánchez Asenjo’ 2004
JRootPane
Glass Pane Layered Pane
Component JLayeredPane
Content Pane
Component JMenuBar
Ilustración 23, Estructura de los paneles de los marcos contenedores
RootPaneContainer
(JFrame, JWindow, JDialog, JApplet, JInternalFrame)
JRootPane
JLayeredPane
JMenuBar
(opcional) ContentPane
panel de contenido
Contenidos
GlassPane
Ilustración 24, Apariencia visual de los paneles de los marcos contenedores
Por encima se sitúa el JRootPane que está compuesto de dos grandes paneles:
161
Manual de Java
Gráficos Java. Java 2D
162
© Jorge Sánchez Asenjo’ 2004
clase JPanel
Esta clase es la clase básica contenedora de Swing. Se puede preparar para dibujar
componentes y elementos, y luego asignarla por ejemplo al content pane de una
aplicación mediante el método add.
Ejemplo:
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Container;
import java.awt.Color;
163
Manual de Java
Gráficos Java. Java 2D
El resultado anterior es una ventana de 300 por 300 píxeles con fondo de color rojo. Lo
bueno es que como JPanel es un Container se le pueden añadir componentes con el
método add.
Normalmente se suele generar una clase que deriva de JPanel y en ella redefinir el
método paintComponent para definir la forma de dibujar objetos en el panel.
constructores
constructor descripción
JPanel() Construye un nuevo JPanel con doble búfer y
composición flow layout
JPanel(boolean dobleBúfer) Crea un JPanel e indica si se desea un doble
búfer para él y composición flow layout
JPanel(LayoutManager l) Construye un nuevo JPanel con doble búfer y la
composición marcada por l
JPanel(boolean dobleBúfer, Construye un panel indicando si se desea doble
LayoutManager l) búfer y el tipo de composición
métodos
método descripción
PanelUI getUI() Devuelve el tipo de UI que se está usando en
forma de objeto PanelUI
String getUIClassID() Devuelve el tipo de UI que se está usando en
forma de cadena
void setUI(PanelUI ui) Asigna un nuevo UI al panel
void updateUI() Hace que el panel acepte los cambios de UI
realizados normalmente con
UIManager.setLookAndFeel
164
© Jorge Sánchez Asenjo’ 2004
Utilizar desde clases AWT o Swing como resultado de una petición de dibujo. Esta
petición se pasa al método paint, paintComponent o update que reciben un
objeto Graphics que mediante cast puede convertirse a Graphics2D (ya que la
clase Graphics2D es hija de Graphics).
165
Manual de Java
Gráficos Java. Java 2D
1> Para los componentes Swing, paint() se invoca siempre como resultado tanto
de las órdenes del sistema como de la aplicación; el método update() no se
invoca jamás sobre componentes Swing
2> Jamás se debe utilizar una llamada a paint, este método es llamado
automáticamente. Los programas pueden provocar una llamada futura a
paint() invocando a repaint().
3> En componentes con salida gráfica compleja, repaint() debería ser invocado
con argumentos para definir el rectángulo que necesita actualización, en
lugar de invocar la versión sin argumentos, que hace que sea repintado el
componente completo
4> La implementación que hace Swing de paint(), reparte esa llamada en tres
llamadas separadas: paintComponent(), paintBorder() y paintChildren().
Los componentes internos Swing que deseen implementar su propio código
de repintado, deberían colocar ese código dentro del ámbito del método
paintComponent(); no dentro de paint(). paint se utiliza sólo en
componentes grandes (como JFrame).
Establecer reglas de composición para indicar como se combinan los píxeles que
se quieren pintar sobre píxeles previos ya pintados
1> Obtener un objeto Graphics2D una forma común de hacerlo es a través del
método paint o paintComponent.
166
© Jorge Sánchez Asenjo’ 2004
3> Establecer el trazo de dibujo mediante setStroke que poseen los objetos
Stroke:
Stroke trazo=....;
g2.setStroke(trazo);
4> Establecer la pintura (indica cómo se rellenan las formas, color, textura, etc.)
creando objetos Paint y utilizando el método setPaint
Paint pintura=...;
g2.setPaint(pintura);
5> Usar objetos de recorte (clase Shape) para delimitar la imagen a través del
método setClip.
Shape forma=...;
g2.clip(forma);
g2.setTransform(AffineTransform.getRotationInstance(0.9);
8> Establecer reglas de composición para indicar como se mezclan los nuevos
píxeles con los ya existentes.
Composite mezcla=...;
g2.setComposite(mezcla);
Shape forma=....
formas 2D
En Java 1.0 la clase Graphics definía métodos para dibujar formas. Desde 1.2, es decir
desde la aparición de Java 2D, se utilizan clases que representan estas formas. Todas las
167
Manual de Java
Gráficos Java. Java 2D
clases implementan la interfaz Shape que define unos cuantos métodos relacionados
con formas gráficas bidimensionales.
formas rectangulares
La superclase RectangularShape es la clase padre de Rectangle2D (que representa
rectángulos), RoundRectangle (rectángulo de esquinas redondeadas), Ellipse2d
(elipses) y Arc2D (Arcos).
Todas las formas descritas se puede entender que están inscritas en un rectángulo,
lo que facilita su uso. De hecho, todas derivan de la clase RectangularShape que
proporciona métodos interesantes para manipular las formas. Son (además de los
heredados de la clase Shape):
constructor uso
double getCenterX() Devuelve la coordenada X del centro del
rectángulo
168
© Jorge Sánchez Asenjo’ 2004
constructor uso
double getCenterY() Devuelve la coordenada Y del centro del
rectángulo
double getHeight() Altura del rectángulo
double getMaxX() Coordenada X más larga del rectángulo
double getMaxY() Coordenada Y más larga del rectángulo
double getMinX() Coordenada X más corta del rectángulo
double getMinY() Coordenada Y más corta del rectángulo
double getWidth() Anchura del rectángulo
double getX() Coordenada X de la esquina superior
izquierda del rectángulo
double getY() Coordenada Y de la esquina superior
izquierda del rectángulo
void setFrame(double x, double y, Establece nueva medida y posición para el
double anchura, double altura) rectángulo
void setFrameFromCenter(double x, Modifica el rectángulo para que obtenga su
double y, double esquinaX, double posición a partir del centro marcado por x, y
esquinaY) por las dos coordenadas de una de las
esquinas
void setFrameFromDiagonal(double Modifica el rectángulo para que obtenga su
x1, double y1, double x2, double y2) posición a partir de las coordenadas de los
dos vértices indicados
constructores
Sólo se comenta la versión Double pero hay que recordar que hay versión Float (que
sería igual pero cambiando los tipos double por float)
constructor uso
Rectangle2D.Double(double x, double Crea un rectángulo con la esquina superior
y, double ancho, double alto) izquierda en la coordenada x,y y una anchura
y altura dadas
Ellipse2D.Double(double x, double y, Crea una elipse inscrita en el rectángulo
double ancho, double alto) definido por esos cuatro parámetros
169
Manual de Java
Gráficos Java. Java 2D
constructor uso
Arc2D.Double(double x, double y, Crea un arco inscrito en el rectángulo
double ancho, double alto, double inicio, definido por los cuatro primeros parámetros.
double longitud, int tipoDeCierre) El arco se empezará a dibujar desde el inicio
marcado en ángulos (0, 90, etc.) y durante los
grados marcados por longitud (inicio 90 y
longitud 90, significa un ángulo de 90 a 180
grados).
El tipo de cierre puede ser alguna de estas
constantes:
puntos
La clase Point2D crea puntos en el espacio que se pueden aplicar a diversas funciones.
Su construcción requiere también elegir entre su clase interna Float o Double.
Ejemplo:
curvas
Java 2D proporciona curvas cuadráticas y cúbicas. Las cuadráticas son complejas de
manipular para crearlas se utilizan dos coordenadas iniciales y dos finales más dos
coordenadas de control de curva. Ejemplo:
170
© Jorge Sánchez Asenjo’ 2004
(20,30
(50,50)
(50,80)
El resultado es:
Las cúbicas representan curvas de Bezier que permiten dibujar curvas más
eficientemente a través de puntos de tangente. El formato es:
Ejemplo:
CubicCurve2D c=new
CubicCurve2D.Double(20,30,30,60,60,40,50,80)
El resultado es:
(20,30)
(60,40)
(30,60)
(50,80)
Hay una forma mucho más compleja que es el objeto GeneralPath. Este objeto
permite encadenar curvas y rectas. Se construye con el constructor sin argumentos o
utilizando un constructor que usa como argumento una forma ya hecha que será la
primera curva de la secuencia.
Después el método moveTo se especifica la primera coordenada. El método lineTo
permite dibujar una línea desde el punto anterior a uno nuevo. El método curveTo
permite dibujar una curva de Bezier al siguiente punto. Los parámetros son:
171
Manual de Java
Gráficos Java. Java 2D
curveTo(controlPuntoAnteriorX,
controlPuntoAnteriorY,controlX, controlY, X, Y);
El método moveTo no sirve sólo para empezar la ruta, marca también otro segmento
de ruta.
áreas
Una técnica muy interesante para construir formas consiste en usar operaciones lógicas
para unir, restar o intersectar figuras. Hay cuatro operaciones de este tipo:
add substract
intersect
exclusiveOr
Para realizar estas operaciones se requiere un objeto de área (clase Area). Es esta clase
la que posee estos métodos. La clase Area posee un constructor en el que se pueden
172
© Jorge Sánchez Asenjo’ 2004
colocar objetos Shape. De esta forma se pueden combinar figuras como se muestra en
el ejemplo:
Graphics2D g2=(Graphics2D) g;
Area a1=new Area(new Rectangle2D.Double(100, 100, 400,200));
Area a2=new Area(new Ellipse2D.Double(150,50, 250, 400));
a1.add(a2);
g2.draw(a1);
trazos
Define los bordes de las formas dibujadas. Por defecto es una línea de un píxel que
contornea a la forma. La interfaz Stroke es el que permite definir trazos. En Java2D
sólo la clase BasicStroke (dentro de java.awt)implementa dicho interfaz. Esta
interfaz devuelve sólo un método llamado createdStrokedShape que recibe una
forma Shape y devuelve otra forma cuyo contenido es el trazo definido alrededor del
Shape utilizado como argumento. El método setStroke de la clase Graphics2D es el
que establece el trazo actual. Ejemplo:
//si g2 es un Graphics2D
g2.setStroke(new BasicStroke(10.0F));
g2.draw(new Ellipse2D.Double(15,40,140,310));
constructores de BasicStroke
constructor uso
BasicStroke(float ancho) Crea un trazo con la anchura dada
BasicStroke(float ancho, int finalLínea, Crea un trazo con la anchura dada. Especifica
int unión) también un final de línea que puede ser:
BasicStroke.CAP_BUTT. Final de
línea en recto
BasicStroke.CAP_ROUND. Final
de línea redondeada
BasicStroke.CAP_SQUARE. Final
de línea en cuadrado (el borde se
extiende más que en recto),
Se especifica también la forma de esquinas de
las líneas
BasicStroke.JOIN_BEVEL. Vértice
de línea en bisel
BasicStroke.JOIN_MITER. Vértice
de línea en inglete
BasicStroke.JOIN_ROUND.
Vértice de línea en redondeado
173
Manual de Java
Gráficos Java. Java 2D
constructor uso
BasicStroke(float ancho, int finalLínea, Igual que la anterior pero con otros tres
int unión, float límite, float[] trazoLínea, parámetros más: el máximo desfase de las
float faseDeTrazo) esquinas y extremos de la línea, un array de
números float para indicar el grosor de los
trazos de la línea y un último parámetro para
indicar en que posición comienzan esos
trazos.
Ejemplo:
pintura
La interfaz Paint permite indicar de qué forma se rellenarán las formas dibujadas. El
método setPaint de la clase Graphics2D es el que permite indicar la forma de la
pintura. Este método requiere una clase que implemente la interfaz Paint.
Tras utilizar el método setPaint, los métodos fill (pintar el relleno) y draw (pintar
el contorno) rellenan con el color elegido.
g2.setPaint(Color.RED);
Esta instrucción coloca rojo como el color de pintura. Este será el color que utilice el
método fill que es el que realmente rellena una forma (que se debe pasar como
argumento) con la pintura elegida. Si tras setPaint, el método que se usa es draw,
entonces es el borde el que aparecerá de color rojo.
174
© Jorge Sánchez Asenjo’ 2004
constructores de GradientPaint
constructor uso
GradientPaint(float x1, float y1, Color Gradiente que va desde la coordenada (x1,y1)
color1, float x2, float y2, Color color2) a la coordenada (x2,y2) y que hace transición
de colores (gradiente) desde el color1 hasta el
color2
GradientPaint(float x1, float y1, Color Igual que el anterior, sólo que permite que el
color1, float x2, float y2, Color color2, gradiente sea cíclico (se repite continua-
boolean cíclico) mente)
GradientPaint(Point2D p1, Color Versiones de los constructores anteriores
color1, Point2D p1, Color color2) usando objetos Point2D para especificar las
GradientPaint(Point2D p1, Color coordenadas
color1, Point2D p1, boolean cíclico)
Ejemplo:
Y esto es equivalente a:
g2.setPaint(new GradientPaint(80,100,Color.BLACK,
200,200,Color.YELLOW);
g2.fill(a1);
Otra posibilidad es pintar utilizando una textura, como se verá más adelante.
transformaciones
Son operaciones que se realizan sobre el contexto gráfico, de forma que los siguientes
dibujos aparecen distorsionados en la dirección marcada por la transformación. Lo que
se transforma es el contexto gráfico.
Esta transformación se ejecuta con algunos de estos métodos:
175
Manual de Java
Gráficos Java. Java 2D
rotación distorsión
rotate shear
Son los métodos scale, translate, rotate y shear los encargados de realizarlas. Se
pueden realizar todas las transformaciones deseadas.
Hay que tener en cuenta que lo que se gira es el contexto gráfico, es decir, se gira el
contexto y se dibuja la forma.
transformaciones afines
La clase AffineTransform permite realizar todo tipo de transformaciones. Se basa en
el uso de una matriz de transformaciones. Y permite ejecutar todo tipo de operaciones
sobre el contexto gráfico.
El método setTransform de la clase Graphics2D permite aplicar una
transformación afín sobre el contexto gráfico para que al dibujar el siguiente objeto se
utilice.
Una posibilidad rápida es usar los métodos estáticos de esta clase que permiten
rotar, escalar, distorsionar, etc. Ejemplo:
g2.setTransform(AffineTransform.getRotateInstance(45));
g2.draw(rectangulo);
176
© Jorge Sánchez Asenjo’ 2004
Métodos de AffineTransform:
método uso
static AffineTransform Rota el contexto el número de radianes
getRotateInstance(double theta) indicado por theta
static AffineTransform Rota el contexto el número de radianes
getRotateInstance(double theta, indicado por theta girando desde el centro
double x, double y) marcado por x,y
static AffineTransform Escala el contexto según los valores de escala
getScaleInstance(double escalaX, indicados
double escala)
static AffineTransform Distorsiona la figura según los valores
getShearInstance(double escalaX, indicados
double escala)
static AffineTransform Mueve el contexto según los valores indicados
getTranslateInstance(double x, de desplazamiento.
double y)
recorte
Permite recortar la siguiente forma a dibujar. Tres métodos de Graphics2D permiten
realizar estos recortes:
void setClip(Shape s). Establece la forma s como forma de recorte del contexto
gráfico. Lo siguiente que se dibuje quedará recortado según esta forma.
Shape clipAntiguo=g2.getClip();
g2.clip(formaDeRecorte);
g2.setClip(clipAntiguo);//Se recupera el recorte inicial
composición
La composición permite indicar la manera en la que los dibujos de formas se mezclan.
Es decir, cuando se dibuja un objeto en el contexto gráfico (fuente), los colores de éste
se mezclan con los ya existentes (destino). Sin indicar nada, el objeto fuente reemplaza
a los destinos.
Para ello se utilizan objetos de tipo AlphaComposite que permiten indicar un
grado de transparencia y una forma de mezcla. Estos objetos se crean a través del
método estático getInstance.
177
Manual de Java
Gráficos Java. Java 2D
Este método posee dos versiones, en la primera se establece la regla de fusión (un
entero que se usa a través de las constantes especificadas en las siguientes tablas). La
segunda versión añade un número float que es un valor entre 0 y 1, que indica el
porcentaje. Un alpha 1 significa que la imagen se escribe al 100% de tinta, 0% es
absolutamente transparente. Las constantes estáticas para la regla son:
constante uso
AlphaComposite.CLEAR Se eliminan los píxeles del objeto destino y
del fuente.
AlphaComposite.DST Dibuja sólo el objeto destino
AlphaComposite.DST_ATOP Dibuja el objeto fuente, pero en la parte
donde se solapa con el destino dibuja el
destino
AlphaComposite.DST_IN Se dibuja la parte del destino que se
superpone al fuente.
AlphaComposite.DST_OUT Se dibuja la parte del destino que no se solapa
al fuente
AlphaComposite.DST_OVER Se dibuja la parte del destino que se
superpone al fuente.
AlphaComposite.SRC Dibuja el color y el valor de transparencia del
objeto fuente
AlphaComposite.SRC_ATOP Dibuja el destino, pero en las partes en las
que se solapan, dibuja el fuente
AlphaComposite.SRC_IN Dibuja la parte del objeto fuente que se solapa
con el destino
AlphaComposite.SRC_OUT Dibuja la parte del objeto fuente que no se
solapa con el destino
AlphaComposite.SRC_OVER Dibuja el objeto fuente sobre el objeto de
destino
AlphaComposite.XOR Dibuja el objeto fuente y el destino en
aquellas zonas donde no hay solapamiento
fuentes
Se puede dibujar también texto en los contextos gráficos. El texto queda determinado
fundamentalmente por su tamaño y tipo de letra. respecto al tipo de letra conviene
utilizar tipos genéricos: Dialog, DialogInput, Monospaced, Serif, o SansSerif.
Suele haber otras cincuenta o sesenta fuentes más. Hay una clase llamada
GraphicsEnvironment que se refiere a las propiedades del entorno gráfico en
ejecución. Posee un método estático llamado getLocalGraphicsEnvironment que
devuelve un objeto del mismo tipo referido al entorno local. Este objeto permite saca
múltiples propiedades sobre las posibilidades gráficas de la máquina en ejecución, entre
ellas getAvailableFontFamilyNames, devuelve los tipos de letra disponibles:
178
© Jorge Sánchez Asenjo’ 2004
String lista[]=GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
for(int i=0;i<lista.length;i++){
System.out.println(lista[i]);
}
Lógicamente utilizar esas fuentes es un riesgo ya que no estarán presentes en todos los
sistemas; de ahí que sea más recomendable el uso de fuentes genéricas.
clase Font
La clase Font sirve para establecer una determinada fuente. Se crea una fuente de esta
forma:
En el futuro puede que se soporten otros formatos de fuente (como por ejemplo
PostScript Tipo 1).
Para dibujar texto en el panel de dibujo, se debe usar el objeto Graphics
correspondiente. Este objeto posee un método setPaint que permite utilizar un objeto
de fuente Font concreto. El método drawString es el encargado de dibujar el texto, lo
hace a partir de unas coordenadas que indicarán la posición de la línea base a la
izquierda del texto:
179
Manual de Java
Gráficos Java. Java 2D
métodos de font
método uso
boolean canDisplay(char c) Indica si la fuente posee el símbolo
perteneciente al carácter indicado
int canDisplayUpTo(String s) Indica si la fuente puede mostrar los
caracteres integrados en la cadena s. De no
ser así devuelve la posición dentro de la
cadena del primer carácter que no se puede
mostrar. Si puede mostrar toda la cadena,
devuelve -1
static Font createFont(int Obtiene una fuente a partir de un archivo de
formatoFuente, InputStream stream) fuentes (ver ejemplo más arriba)
Font deriveFont(AffinTransform at) Crea un nuevo objeto Font duplicando la
fuente actual y transformándola según las
características del objeto de transformación
indicado.
Font deriveFont(float tamaño) Devuelve un nuevo objeto Font resultado de
ampliar o reducir el tamaño de la fuente. Para
ampliar tamaño debe ser mayor que uno;
para reducir debe ser menor que uno
Font deriveFont(int estilo) Obtiene un objeto Font resultado de añadir
el estilo indicado (FONT.Bold, FONT.Italic,
...) en la fuente.
Font deriveFont(Afint estilo, float Aplica el estilo y el tamaño indicado en una
tamaño) nueva fuente creada a partir de la actual.
byte getBaselineFor(char c) Devuelve la línea de base apropiada para esta
fuente
String getFamily() Devuelve el nombre de familia de la fuente
String getFamily(Locale l) Devuelve el nombre de familia de la fuente,
para la configuración local indicada
String getFontName() Devuelve el nombre de la fuente
String getFontName (Locale l) Devuelve el nombre de la fuente, para la
configuración local indicada
float getItalicAngle() Devuelve el ángulo que utiliza la letra en
cursiva
LineMetrics getLineMetrics(String s, Obtiene un objeto de tipo LineMetrics
FontRenderContext frc) configurado para la cadena s y el objeto de
relleno de fuentes frc. Los objetos
LineMetrics permiten obtener todas las
medidas de dibujo de una fuente
(descendente, interlineado)
String getName() Devuelve el nombre lógico de la fuente
String getPSName() Devuelve el nombre PostScript de la fuente
int getSize() Devuelve el tamaño en puntos de la fuente
180
© Jorge Sánchez Asenjo’ 2004
método uso
float getSize2D() Devuelve el tamaño en puntos de la fuente,
ahora en formato float
Rectangle2D Devuelve la forma rectangular mínima que
getStringBounds(String s, contiene los caracteres de la cadena s
FontRenderContext frc) utilizando el objeto FontRenderContext
indicado (sirve para indicar como pintar las
fuentes)
int getStyle() Devuelve el estilo de la fuente (Font.BOLD,
Font.ITALIC, ...)
boolean isBold() Devuelve true si la fuente está en negrita
boolean isItalic() Devuelve true si la fuente está en cursiva
String toString() Devuelve el texto de la fuente
LineMetrics
La clase LineMetrics (en java.awt.font) se utiliza para obtener información sobre las
medidas de una fuente en concreto. Se utiliza para dibujar elementos que se ajusten
correctamente sobre un texto concreto. La forma de hacerlo es:
El objeto frc es un FontRenderContext que se utiliza para saber cómo se pintan las
fuentes en el contexto gráfico.
plagio
Ascendente
Principal
Línea de base
Descendente
Interlineado
Ilustración 25, medidas de las fuentes. LineMetrics
método uso
float getAscent() Medida del ascendente de la línea
float getBaselineIndex() Devuelve el tipo de línea base del texto. Puede
ser una de estas constantes:
ROMAN_BASELINE, CENTER_BASELINE,
HANGING_BASELINE.
181
Manual de Java
Gráficos Java. Java 2D
método uso
float getBaselineOffsets() Devuelve el desplazamiento de las letras a
partir de la línea base
float getDescent() Medida del descendente de la línea
float getHeight() Altura del texto
float getLeading() Medida del interlineado
float getNumChars() Número de caracteres máximos a los que este
objeto puede incluir
float getStrikeThroughOffset() Posición de la línea de tachado a partir de la
línea base del texto
float getStrikeThroughThickness() Grosor de la línea de tachado
float getUnderlineOffset() Posición de la línea de subrayado a partir de
la línea base del texto
float getUnderlineThickness() Grosor de la línea de subrayado
clase TextLayout
Es una potentísima clase (dentro de java.awt.font) que permite usar texto como si
fuera una forma, lo que permite operaciones muy avanzadas.
Se crean objetos de este tipo mediante un String que representa el texto, un objeto
Font con las características de la fuente y un objeto FontRenderContext. Esta última
clase sirve para saber como dibujar el texto. Se puede obtener un objeto de este tipo con
el método getFontRenderContext de la clase Graphics2D. Ejemplo de creación:
FontRenderContext frc=g2.getFontRenderContext();
TextLayout tl=new TextLayout(“Dialog”, new Font(“SansSerif”,
Font.BOLD, 12),frc);
El método draw permite dibujar el texto en la zona deseada de pantalla. Para usar este
método se debe pasar el objeto Graphics2D en el que deseamos dibujar y las
coordenadas x e y que marcan la posición del texto.
Por otro lado esta clase posee numerosos métodos para realizar todo tipo de
transformaciones sobre el texto. Por ejemplo getOutline permite obtener una forma
Shape del texto aplicando además una determinada transformación (mediante un
objeto AffineTransform).
métodos
método uso
void draw(Graphics2D g2, float x, Dibuja el texto en el contexto gráfico indicado
float y) a partir de las coordenadas x, y
float getAscent() Medida del ascendente de la línea
float getDescent() Medida del descendiente de la línea
182
© Jorge Sánchez Asenjo’ 2004
método uso
float getBaseline() Devuelve el tipo de línea base del texto. Puede
ser una de estas constantes:
Font.ROMAN_BASELINE,
Font.CENTER_BASELINE,
Font.HANGING_BASELINE.
float getBaselineOffsets() Devuelve el desplazamiento de las letras a
partir de la línea base
Rectangle2D getBounds() Obtiene un rectángulo delimitador que
engloba al texto
int getCharacterCount() Número de caracteres del texto
float getLeading() Obtiene la posición de la cabecera del texto
Shape getOutline(AffineTransform Obtiene la forma del contorno del texto
trans) usando la transformación indicada. La forma
posee coordenadas en 0,0. Teniendo en
cuenta que en el caso del texto la referencia
se toma sobre la línea base, con lo que la
forma del texto se dibujará fuera de la
pantalla si no se transforma.
Image imagen=getToolkit().getImage(url);
Image imagen=getToolkit().getImage(getClass().getResource
(“dibujo1.gif”));
Tras estas funciones, Java no habrá cargado la imagen. Se cargará cuando se intente
mostrar. Hay una interfaz llamada ImageObserver que sirve para examinar la imagen
a cargar y así conocer sus condiciones a priori (anchura, altura, tamaño, etc.).
183
Manual de Java
Gráficos Java. Java 2D
g2.drawImage(image,50,50,this);
método uso
boolean drawImage(Image imagen, int x, Dibuja la imagen en la posición x, y usando el
int y, ImageObserver observador) observador indicado
boolean drawImage(Image imagen, int x, Dibuja la imagen en la posición x, y usando el
int y, Color fondo, ImageObserver observador indicado y el color de fondo que se
observador) indica
boolean drawImage(Image imagen, int x, Dibuja la imagen en la posición x, y con la altura
int y, int ancho, int alto, Color fondo, y anchura indicada y el Color de fondo
ImageObserver observador) señalado.
boolean drawImage(Image imagen, int x, Dibuja la imagen en la posición x, y con la altura
int y, int ancho, int alto, ImageObserver y anchura indicada
observador)
boolean drawImage(Image imagen, int Dibuja la imagen en el rectángulo definido por
x1, int y1, int x2, int y2, ImageObserver las coordenadas x1,y1 y x2,y2 Ajustando la
observador) imagen si es necesario.
boolean drawImage(Image imagen, int Dibuja la imagen en el rectángulo definido por
x1, int y1, int x2, int y2, Color fondo, las coordenadas x1,y1 y x2,y2 Ajustando la
ImageObserver observador) imagen si es necesario.
boolean drawImage(Image imagen, int Dibuja una imagen en el rectángulo definido por
x1, int y1, int x2, int y2, int ox1, int oy1, int las coordenadas x1,y1 y x2,y2 La imagen a
ox2, int oy2, ImageObserver observador) dibujar se toma del archivo de origen usando el
rectángulo en esa imagen que va de ox1, oy1 a
ox2,oy2 La imagen se transformará si es
necesario.
boolean drawImage(Image imagen, int Igual que el anterior pero usando un color de
x1, int y1, int x2, int y2, int ox1, int oy1, int fondo para los píxeles transparentes.
ox2, int oy2, Color fondo, ImageObserver
observador)
184
Threads
Introducción
En informática, se conoce como multitarea, la posibilidad de que una computadora
realice varias tareas a la vez. En realidad es una impresión (salvo en un equipo con
varios procesadores) que se consigue repartiendo tiempo y recursos entre distintos
procesos.
La palabra thread hace referencia a un flujo de control dentro de un programa
(también se le llama hilo). La capacidad que permiten los threads a un programa
estriba en que se pueden ejecutar más de un hilo a la vez.
Los hilos comparten los datos del programa (además pueden tener datos propios) y
esto hace que el control sea más dificultoso. Como ejemplo de thread, está el recolector
de basura de Java que elimina los datos no deseados mientras el programa continúa con
su ejecución normal.
El uso de hilos es muy variado: animación, creación de servidores, tareas de segundo
plano, programación paralela,...
métodos de Thread
métodos uso
void interrupt () Solicita la interrupción del hilo
static boolean interrupted() true si se ha solicitado interrumpir el hilo
actual de programa
static void sleep(int milisegundos) Duerme el Thread actual durante un cierto
número de milisegundos.
185
Manual de Java
Threads
métodos uso
static void sleep(int milisegundos, int Duerme el Thread actual durante un cierto
nanos) número de milisegundos y nanosegundos.
boolean isAlive() Devuelve verdadero si el thread está vivo
boolean isInterrupted() true si se ha pedido interrumpir el hilo
static Thread currentThread() Obtiene un objeto Thread que representa al
hilo actual
void setDaemon(boolean b) Establece (en caso de que b sea verdadero) el
Thread como servidor. Un thread servidor
puede pasar servicios a otros hilos.
Cuando sólo quedan hilos de este tipo, el
programa finaliza
void setPriority(int prioridad) Establece el nivel de prioridad del Thread.
Estos niveles son del 1 al 10. Se pueden
utilizar estas constantes también:
Thread.NORMAL_PRIORITY.
Prioridad normal (5).
Thread.MAX_PRIORITY. Prioridad
alta (10).
Thread.MIN_PRIORITY. Prioridad
mínima (1).
void start() Lanza la ejecución de este hilo, para ello
ejecuta el código del método run asociado al
Thread
static void yield() Hace que el Thread actual deje ejecutarse a
Threads con niveles menores o iguales al
actual.
creación de threads
2> Definir el método run y en él las acciones que tomará el hilo de programa
4> Ejecutar el método start del Thread para ejecutar el método run
186
© Jorge Sánchez Asenjo’ 2004
El código de run se ejecuta hasta que es parado el Thread. La clase Thread dispone de el
método stop() para definitivamente la ejecución del thread. Sin embargo no es
recomendable su utilización ya que puede frenar inadecuadamente la ejecución del hilo
de programa. De hecho este método se considera obsoleto y no debe utilizarse
jamás. La interrupción de un hilo de programa se tratará más adelante. Ejemplo de
creación de hilos:
Se considera una solución más adaptada al uso de objetos en Java hacer que la propia
clase que implementa Runnable, lance el Thread en el propio constructor.
187
Manual de Java
Threads
Ejemplo:
control de Threads
sleep
Para conseguir que un thread se pare durante un cierto tiempo sin consumir CPU, se
utiliza o el método sleep de los objetos thread o el método estático Thread.sleep. En
ambos casos se indica como parámetro el número de milisegundos que se detendrá la
ejecución del thread. Se dice que el Thread duerme durante ese tiempo. Tras consumir
el tiempo continuará la ejecución.
Este método detiene el hilo actual de programa, sea el que sea. Este método se debe
realizar de vez en cuando en un hilo de ejecución continua ya que, de otro modo, se
paraliza la ejecución del resto de Threads.
Es decir, dormir un Thread es obligatorio para la programación multitarea, mientras
un hilo duerme, los otros se pueden ejecutar.
interrupción de la ejecución
Un detalle importante en el control de Threads es que los hilos no tienen ningún
método de interrupción. Un thread finaliza cuando se abandona el método run. Pero,
por supuesto, se debe dar la posibilidad de finalizar un thread y, por ello existe un
método llamado interrupt que está relacionado con este hecho.
El método interrupt de la clase Thread, no interrumpe un hilo, pero sí solicita su
finalización. Esta solicitud es realizada (se interrumpe el hilo) si el hilo está en
ejecución, pero no si está dormido (con el método sleep o con el método wait). Cuando
un hilo está dormido, lo que ocurre es una excepción del tipo InterruptedException.
Es decir si está dormido se produce una excepción, si el hilo está despierto queda
marcado para su interrupción (el método interrupted, detecta si el hilo debe ser
interrumpido). Visto así el cuerpo de un método run para que pueda ser parado, sería:
188
© Jorge Sánchez Asenjo’ 2004
}
}catch(InterruptedException ie){
//Se paro el hilo mientras estaba dormido
}
finalize{
//Instrucciones de limpieza, si procede
}
}
estados de un thread
Los hilos de programa pueden estar en diferentes estados. Cada uno de ellos permite
una serie de tareas.
estado nuevo
Es el estado en el que se encuentra un thread en cuanto se crea. En ese estado el thread
no se está ejecutando. En ese estado sólo se ha ejecutado el código del constructor del
Thread.
estado de ejecución
Ocurre cuando se llama al método start. No tiene por qué estar ejecutándose el thread,
eso ya depende del propio sistema operativo. Es muy conveniente que salga de ese
estado a menudo (al estado de bloqueado), de otra forma se trataría de un hilo egoísta
que impide la ejecución del resto de threads.
La otra posibilidad de abandonar este estado es debido a la muerte del thread
estado bloqueado
Un thread está bloqueado cuando:
189
Manual de Java
Threads
estado muerto
Significa que el método finalizó. Esto puede ocurrir si:
Se puede comprobar si un thread no está muerto con el método isAlive que devuelve
true si el thread no está muerto.
finalizó bloqueado
sleep
bloqueo
sleep
haciendo disponible
wait
notify E/S
new
nuevo
start terminó
E/S
en espera
de bloqueo
muerto ejecución
finalizó
run
Ilustración 26, Diagrama de estados de un Thread o hilo
sincronización
Hay serios problemas cuando dos threads diferentes acceden a los mismos datos.
190
© Jorge Sánchez Asenjo’ 2004
métodos sincronizados
Un primer problema ocurre cuando un método de un thread es interrumpido por otro
thread de mayor prioridad. Por ejemplo: supongamos que poseemos un thread que usa
un valor x y le multiplica por dos. Si desde que lee x hasta que la multiplica por dos, otro
thread cambia el valor de x, entonces la multiplicación puede no ser correcta.
Para evitar que un método sea interrumpido por un thread externo, se utiliza la
palabra clave synchronized:
Estos métodos tienen como inconveniente, que consumen mucho tiempo de CPU, lo que
aletargará mucho el programa si se utilizan muy a menudo.
método wait
Los métodos sincronizados impiden que otros threads les utilicen, por lo que bloquean a
estos threads. Esto puede provocar que un programa quede paralizado si se está
buscando una determinada condición que depende de otros threads.
Por ejemplo imaginemos esta situación: disponemos de threads que controlan
cuentas de banco. Estos threads utilizan un método sacarDinero que sólo puede sacar
dinero si efectivamente lo hay. Si este método está marcado con synchronized
entonces el resto no pueden añadir dinero mientras se ejecute el anterior.
Esto lo soluciona el método wait. Este método bloquea al thread actual para que el
resto pueda ejecutarse. Este método puede provocar una excepción del tipo
InterruptedException si el thread es interrumpido durante la espera. Por eso los
métodos syncronized deben propagar esta excepción a el thread que lo llamó mediante
la palabra throws (Véase throws, página 74). Ejemplo (método sacarDinero de una
supuesta clase Banco, este método puede ser utilizado por un thread):
El método wait pertenece a la clase Object por lo que puede ser llamado por cualquier
clase.
191
Manual de Java
Threads
192
componentes Swing
introducción
Durante los temas anteriores se han tratado elementos de la construcción de interfaces
de usuario. No obstante los paquetes de Java proporcionan clases especiales para crear
interfaces de usuario. Además discutiremos también los principales apartados sobre la
apariencia.
La colocación de componentes se realiza con el método add del contenedor en elque
va el panel. En el caso de un elemento con RootPane (véase paneles de dibujo, página
160) como JFrame, JApplet y JDialog, se suele coolocar en el panel de contexto (que se
obtiene con getRootPane).
administración de diseño
introducción
Los administradores de diseño son una parte esencial de la creación de interfaces de
usuario, ya que determinan las posiciones de los controles en un contenedor. En
lenguajes orientados a una sola plataforma, el problema es menor ya que el aspecto es
más fácilmente controlable. Pero la filosofía Java está orientada a la portabilidad del
código. Por eso este es uno de los apartados más complejos de la creación de interfaces,
ya que las medidas y posiciones dependen de la máquina en concreto.
En otros entornos los componentes se colocan con coordenadas absolutas. En Java
se desaconseja esa práctica porque en muchos casos es imposible prever el tamaño de
un componente.
En su lugar se utilizan administradores de diseño que permiten realizar colocaciones
y maquetaciones de forma independiente de las coordenadas.
El método setLayout dentro de la clase Container es el encargado de
proporcionar un administrador de diseño a un determinado panel. Este método tiene
como único parámetro un objeto de tipo LayoutManager. LayoutManager, es una
interfaz implementada en diversas clases que sirven para maquetar (FlowLayout,
GridLayout, BoxLayout, BorderLayout, GridBagLayout, ...)
La adición de elementos se puede crear con el método add. A veces no se muestran
los cambios en el contenedor, para forzarles hay que utilizar el método validate que
poseen los contenedores.
Flow Layout
Distribuye los componentes del contenedor de izquierda a derecha y de arriba abajo. Es
la distribución más fácil y una de las más efectivas.
constructores
constructor uso
FlowLayout() Construye el administrador de tipo
flow con alineación centrada y
márgenes a 5 píxeles
193
Manual de Java
componentes Swing
constructor uso
FlowLayout(int alineación) Permite indicar la alineación que
puede indicarse por alguna de estas
constantes de clase: LEFT, RIGHT o
CENTER
FlowLayout(int alineación, int sepH, int Permite indicar alineación y la
sepV) separación horizontal y vertical
entre los componentes.
Grid Layout
Crea distribuciones en forma de malla que posee una serie de columnas y filas. Estas
filas y columnas crean celdas de exactamente el mismo tamaño. Los componentes se
distribuyen desde la primer celda a la izquierda de la primera fila; y van rellenando fila a
fila toda la malla hasta la celda más a la derecha de la última fila.
constructores
constructor uso
GridLayout() Crea una malla de una sola celda
GridLayout(int nFilas, int nColumnas) Crea una malla de las filas y
columnas indicadas
GridLayout(int nFilas, int nColumnas, int Crea una malla con las filas y
sepH, int sepV) columnas indicadas y con los
espacios entre botones que se
especifican.
El método add de los contenedores admite un segundo parámetro con el que se puede
indicar el número de la celda donde se coloca el componente. Ejemplo:
194
© Jorge Sánchez Asenjo’ 2004
getContentPane().add(boton15,5);
En el ejemplo el botón llamado boton15 se coloca en la sexta casilla (la primera es la 0).
Si había más casillas detrás de esa, entonces se mueven al siguiente hueco.
Si se añaden más componentes que casillas tenga el contenedor, entonces se amplía
el Grid en consecuencia automáticamente.
Border Layout
Permite colocar componentes alrededor de los bordes de un contenedor. Por defecto es
lo que utiliza AWT (y por lo tanto las clases Swing). Los bordes son NORTH. SOUTH,
EAST, WEST y CENTER. Se suele utilizar estas formas en el método add para
colocar componentes en el panel deseado:
Cualquiera de esas dos formas es válida. El panel central se come los paneles adyacentes
si detecta que están vacíos.
constructores
constructor uso
BorderLayout() Crea un nuevo Border Layout
BorderLayout(int espH, int espV) Crea un nuevo Border Layout con
los espacios señalados
BoxLayout
Permite distribuir componentes en una fila o columna. Pensado para filas y columnas
de botones, pertenece al paquete javax.swing (las anteriores están en java.awt).
Para facilitar su manejo, Swing incluye un contenedor llamado Box que está pensado
para manipular los componentes insertados en el contenedor. Box es una clase que
posee diversos métodos estáticos que manipular internamente el administrador
BoxLayout. Para crear un contenedor Box:
Box horizontal=Box.createHorizontalBox();
Boxvertical=Box.createVerticalBox();
195
Manual de Java
componentes Swing
constructor uso
static Box createVerticalBox() Obtiene un contenedor Box vertical
para añadir componentes.
static Component createHorizontalGlue() Crea un componente horizontal para
ajustar automáticamente la distancia
horizontal entre los componentes.
Devuelve el componente creado.
static Component createHorizontalStrut(int Crea un componente horizontal con
ancho) la anchura dada. Devuelve dicho
componente.
static Component createRigidArea( Crea un componente invisible con las
Dimension d) dimensiones dadas.
static Component createVerticalGlue() Crea un componente vertical para
ajustar automáticamente la distancia
vertical entre los componentes.
Devuelve el componente creado.
static Component createVerticalStrut(int Crea un componente vertical con la
ancho) anchura dada. Devuelve dicho
componente.
Ejemplo:
public static void main(String args[]){
JFrame v=new JFrame();
v.setLocation(50,50);
v.setSize(400,300);
v.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton boton1=new JButton("Hola"),
boton2=new JButton("Texto largo"),
boton3=new JButton("Hola"),
boton4=new JButton("Qué hay");
Box b=Box.createHorizontalBox();
b.add(boton1);
b.add(b.createHorizontalGlue());
b.add(boton2);
b.add(b.createHorizontalGlue());
b.add(boton3);
b.add(b.createHorizontalGlue());
b.add(boton4);
v.getContentPane().add(b);
v.setVisible(true);
}
El resultado es:
196
© Jorge Sánchez Asenjo’ 2004
GridBagLayout
Es el administrador de diseño más flexible, pero es más complicado de manipular.
Coloca los componentes en relación a sí mismos y gracias a ello se consigue (con
paciencia y algo de suerte) cualquier diseño.
Los componentes se distribuyen en un espacio de coordenadas lógicas, que no
reales, que son posiciones en el espacio de filas y columnas formadas por el grupo de
componentes. De este modo tendremos una cuadrícula imaginaria que se estira
formando tamaños basados en los componentes que se acomodan en ella.
Es mucho más cómodo trabajar con este diseño mediante programas de diseño
WYSIWYG (como el editor de componentes de NetBeans).
Esta clase tiene un constructor sin parámetros y métodos para obtener información
y configurar la malla de componentes creada.
GridBagConstraints
Esta es una clase totalmente asociada a este tipo de diseños, de hecho con esta clase se
controla la posición y propiedades de los componentes añadidos a contenedores
GridBagLayout. Posee una serie de propiedades
propiedades uso
int gridx, gridy Controla la posición del componente en la malla
int weightx, weighty Indica como añadir el espacio sobrante a un
componente
int fill Controla la expansión del componente para
llenar el espacio que sobre de su asignación
int gridheight, gridwidth Controla el número de filas o columnas sobre las
que se extiende el componente
int anchor Posición del componente si hay espacio adicional
sobrante
int ipadx,ipady Relleno entre el componente y los bordes de su
área
Insets insets Configura el relleno de los bordes
197
Manual de Java
componentes Swing
colocación de componentes
En un GridBagLayout no se puede especificar el tamaño de la malla. Ésta se calcula
según las necesidades. Esto se basa en lo siguiente: si un componente es añadido, por
ejemplo, en la posición 25,25; entonces se crea una malla de esas dimensiones. Si el
siguiente dice estar en la columna 30, la malla se amplía para que esto sea posible.
La colocación de componentes se basa en el método add de los contenedores. A este
método se le pasa el componente a añadir y el conjunto de restricciones con que queda
afectado. Esas restricciones se construyen con la clase GridBagConstraints. Las
propiedades gridx y gridy permiten colocar al componente en una celda concreta de la
malla.
Ejemplo:
Resultado:
198
© Jorge Sánchez Asenjo’ 2004
fill permite indicar en que dirección se desplaza el componente para llenar el hueco.
Se pueden utilizar las siguientes constantes estáticas:
restricciones.fill.GridBagConstraints.BOTH
restricciones.weightx=1.0;
restricciones.weighty=1.0;
Esto expande las celdas ocupadas, hasta llenar la ventana. El valor 0.0 no expande,
cualquier valor distinto de 0, sí lo hace.
199
Manual de Java
componentes Swing
añadeGrid(new JButton("Cuatro"),1,1);
añadeGrid(new JButton("Cinco"),1,2);
restricciones.gridwidth=1;
}
private void añadeGrid(Component c, int x, int y){
restricciones.gridx=x;
restricciones.gridy=y;
getContentPane().add(c,restricciones);
}}
Resultado:
pesos
Sin duda es de los elementos más difíciles de controlar. Son las propiedades de
GridBagConstraints weigthx y weighty las que permiten su control. Sólo valen de
forma relativa. Es decir si a un componente le asignamos 0.5 y a otro 1.0, esto significa
que el segundo es el doble de grande. El valor 0, significa que se mantendrá como estaba
originalmente.
ubicación absoluta
Es una forma de trabajo que Sun desaconseja, aunque bien es cierto que nos libra de la
tiranía de los administradores de diseño y nos permite una colocación libre, mediante
píxeles. Es idónea cuando el tamaño en píxeles de la ventana es fijo.
Consiste en indicar null como valor para el método setLayout del contenedor en el
que deseamos colocar nuestros componentes.
Tras esa acción, los componentes a colocar indicarán su posición y tamaño antes de
añadirse al contenedor. Si no se desea calcular el tamaño, el método de la clase
Component getPreferredSize() devuelve el tamaño predeterminado que se calculará
automáticamente en función de su contenido.
200
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
public class pruebaLayoutAbsoluto {
public static void main(String args[]){
JButton boton=new JButton("Absoluto");
JFrame v=new JFrame();
v.setLocation(50,50);
v.setSize(400,300);
v.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
v.getContentPane().setLayout(null);
boton.setLocation(230,90);
boton.setSize(100, 80);
v.getContentPane().add(boton);
v.setVisible(true);
}
}
Resultado:
apariencia
clase UIManager
Una de las principales mejoras de Swing fue el administrador de apariencia, conocido
como UIManager y para el cual se creó la clase con el mismo nombre (integrada en
javax.swing). Esta clase permite cambiar la apariencia según varios esquemas
preestablecidos. La idea es que un programa Java se visualice igual independientemente
de la plataforma.
Los tres esquemas son: Metal (que es el que funciona por defecto), Motif (parecida
a la estética X-Windows) y Windows (parecida a la estética Windows).
Para cambiar la apariencia se utiliza el método estático (UIManager es una clase
estática) setLookAndFeel al cual se le pasa una cadena con este texto según el
formato deseado:
javax.swing.plaf.metal.MetalLookAndFeel
201
Manual de Java
componentes Swing
com.sun.java.swing.plaf.motif.MotifLookAndFeel
com.sun.java.swing.plaf.windows.WindowsLookAndFeel
El método setLookAndFeel lanza diversos tipos de eventos que hay que capturar, son:
ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException. Normalmente se capturan todos a la vez
usando la superclase Exception.
Para obligar a un elemento a actualizar la apariencia se utiliza la función de las
clases JComponent, updateUI sin argumentos. Ejemplo de cambio de apariencia:
import javax.swing.*;
public class pruebaTiposApariencia {
public static void main(String args[]){
JFrame ventana1=new JFrame();
JPanel jp=new JPanel();
jp.setSize(300,300);
ventana1.setLocation(100,100);
ventana1.setSize(300,300);
jp.add(new JTextField("Texto de prueba"));
jp.add(new JButton("Botón de prueba"));
ventana1.setContentPane(jp);
ventana1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try{
UIManager.setLookAndFeel("com.sun.java.swing.”+
“plaf.windows.WindowsLookAndFeel");
}
catch(Exception e) {System.out.println(“Error”);
System.out.println("");
}
jp.updateUI();
ventana1.setVisible(true);
}
Siempre hay una estética actual para la apariencia de la ventana. Es lo que se llama el
LookAndFeel actual. LookAndFeel es una clase Swing preparada para gestionar
apariencias. Se puede modificar esta clase estática para conseguir una apariencia
personal (indicando tipos de letra, bordes, etc. etc.)
Para averiguar los sistemas LookAndFeel instalados se puede usar el método
estático UIManager.getInstalledLookAndFeels, devuelve un array de objetos
UIManager.LookAndFeelInfo. Esta clase posee un método getClassName que
devuelve el nombre completo de los sistemas instalados.
202
© Jorge Sánchez Asenjo’ 2004
etiquetas
Son textos de una línea que sirven para dar información textual a las ventanas y applets.
También se pueden utilizar para mostrar imágenes estáticas.
creación de etiquetas
constructor uso
JLabel() Crea una etiqueta normal y sin imagen
JLabel(String texto) Construye un objeto JLabel con el texto
indicado
JLabel(String texto, int Construye un objeto JLabel con el texto y
alineaciónHorizontal)) alineación indicados
JLabel(Icon imagen) Construye un objeto JLabel con esa imagen
JLabel(Icon imagen, int Construye un objeto JLabel con esa imagen y
alineaciónHorizontal) alineación
JLabel(String texto, Icon imagen, int Construye un objeto JLabel con ese texto,
alineaciónHorizontal) imagen y alineación
Se pueden crear etiquetas de todo tipo, con texto e imágenes. La posición de la etiqueta
dependerá del tipo de maquetación del contenedor (se verá más adelante). Un etiqueta
normal se colocaría:
jp.add(l1);
jp.add(l2);
ventana1.setLocation(100,100);
ventana1.setSize(300,300);
ventana1.setContentPane(jp);
ventana1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ventana1.setVisible(true);
Como se observa en el ejemplo, las etiquetas se pueden colocar con el método add de
los paneles contenedores.
etiquetas HTML
En Java se permite que el texto de una etiqueta incluya comandos HTML. Basta utilizar
estos comandos en el texto de la etiqueta. Se puede utilizar cualquier comando, pero
sólo se aconseja usar comandos formato de texto y párrafo.
Para que el texto de una etiqueta se considere texto HTML y no texto normal, se
debe comenzar el texto de la etiqueta con <html>.
203
Manual de Java
componentes Swing
Ejemplo:
En el ejemplo anterior el texto Este es el texto, saldría de color rojo, tamaño 7 (el
máximo) y letra Arial o Helvética o sans-serif.
etiquetas gráficas
Hay una modalidad de etiqueta que permite colocar imágenes. Las imágenes que se
pueden colocar se basan en el interfaz Icon. Este interfaz define los siguientes métodos:
getIconHeight (altura del icono), getIconWidth (anchura del icono) y paintIcon
que pinta el icono dentro de componente especificado, indicando su apartado gráfico y
sus posiciones x e y.
Para facilitar la tarea de usar iconos, se puede hacer uso de los objetos ImageIcon.
clase ImageIcon
Se trata de una clase pensada para utilizar iconos a partir de ficheros de imagen.
Constructores:
constructor uso
ImageIcon() Crea un icono de imagen (vacío)
ImageIcon(String rutaArchivo) Crea un icono de imagen a partir del archivo
cuya ruta se indica
ImageIcon(String rutaArchivo, String Crea un icono de imagen a partir del archivo
descripción) cuya ruta se indica y utilizando la descripción
especificada.
La descripción sirve para indicar qué archivo se
abre, no es obligatorio, es una formalidad.
ImageIcon(Image i) Crea un icono imagen a partir del objeto de
imagen i
ImageIcon(Image i, String Crea un icono imagen a partir del objeto de
descripción) imagen i utilizando la descripción especificada.
ImageIcono(URL url) Crea un icono imagen desde la URL indicada
ImageIcon(URL url, String Crea un icono imagen desde la URL indicada
descripción) utilizando la descripción especificada.
ImageIcono(byte[] datos) Crea un icono imagen desde un array de bytes
ImageIcon(byte[] datos, String Crea un icono imagen utilizando la descripción
descripción) especificada.
204
© Jorge Sánchez Asenjo’ 2004
alineación
método uso
void setHorizontalAlignment(int Fija la alineación horizontal de la etiqueta. Esta
alineación) es la posición de la etiqueta (texto e imagen)
con respecto a la horizontal del panel en el que
está inscrita la etiqueta.
Puede ser:
JLabel.LEFT (izquierda)
JLabel.RIGHT (derecha)
JLabel.CENTER (centro)
void setVerticalAlignment(int Fija la alineación vertical de la etiqueta con
alineación) respecto al panel en el que está inscrita
Puede ser:
JLabel.TOP (arriba)
JLabel.BOTTOM(abajo)
JLabel.CENTER (centro)
205
Manual de Java
componentes Swing
método uso
void setHorizontalTextPosition(int Fija la posición del texto respecto a la imagen
posición) de la etiqueta, referido a la horizontal. Las
posibilidades son las mismas que con
setHorizontalAlignment
void setVerticalTextPosition(int Igual que la anterior, pero referida a la vertical.
posición) Las opciones son las mismas que las de
setVerticalAlignment
Todos estos métodos tienen versión get (getVerticalAlignment, por ejemplo) que
permiten obtener las alineaciones actuales.
método uso
Component getLabelFor() Obtiene el componente que éste objeto etiqueta
o null so no lo hay.
char getDisplayedMnemonic() Obtiene el carácter fijado como mnemónico de
la etiqueta.
void setLabelFor(Component c) Hace que el componente c sea etiquetado por
esta etiqueta
void setDisplayedMnemonic(char c) Hace que el carácter c actúe como mnemónico
de la etiqueta.
void setDisplayedMnemonicIndex( Indica qué posición de letra es la que se
int i) remarcará (empezando por 0, que es la
throws IllegalArgumentException primera)
Sirve para el caso en el que queramos resaltar
una determinada letra como mnemónica y
resulte que se repita varias veces.
Lanza evento del tipo:
IllegalArgumentException si esa posición
no existe.
Este método se incorporó en la versión 1.4
cuadros de texto
Son controles que permiten al usuario especificar un texto para que el programa lo
examine. Es uno de los controles más usados.
206
© Jorge Sánchez Asenjo’ 2004
constructores
constructor uso
JTextField() Crea un nuevo cuadro de texto con la
configuración predeterminada
JTextField(int columnas) Anchura del cuadro en caracteres
JTextField(String texto) Crea un cuadro de texto conteniendo el texto
que se pasa como argumento
JTextField(String texto, int columnas) Crea un cuadro de texto conteniendo el texto
que se pasa como argumento e indicando su
anchura.
métodos
método uso
int getColumns() Devuelve la anchura actual del cuadro
void setColumns(int ancho) Establece la anchura del cuadro
String getSelectedText() Obtiene el texto seleccionado del cuadro
int getSelectionStart() Devuelve la posición del texto del cuadro donde
comienza el texto seleccionado
int getSelectionEnd() Devuelve la posición del texto del cuadro donde
finaliza el texto seleccionado
Color Obtiene el color del texto seleccionado
getSelectionTextColor()
Color getSelectionColor() Obtiene el color de fondo del texto
seleccionado
void setSelectionStart(int pos) Hace que en la posición de texto indicada
comience el texto seleccionado
void setSelectionEnd(int pos) Hace que en la posición de texto indicada
finalice el texto seleccionado
void Asigna el color indicado como nuevo color del
setSelectionTextColor(Color texto seleccionado del cuadro
color)
void setSelectionColor(Color Asigna el color indicado como nuevo color del
color) fondo seleccionado del cuadro
void setAlignmentX(float alineación) Establece la alineación horizontal del texto
respecto al cuadro. Puede ser:
JTextField.LEFT_ALIGNMENT
(izquierda)
JTextField.RIGHT_ALIGNMENT
(derecha)
JTextField.CENTER_ALIGNMENT
(centro)
207
Manual de Java
componentes Swing
método uso
void setAlignmentY(float alineación) Establece la alineación vertical del texto
respecto al cuadro. Puede ser:
JTextField.TOP_ALIGNMENT
(arriba)
JTextField.BOTTOM_ALIGNMENT
(abajo)
JTextField.CENTER_ALIGNMENT
(centro)
float getAlignmentX() Obtiene la alineación horizontal del texto
float getAlignmentY() Obtiene la alineación vertical del texto
eventos
Se producen eventos del tipo ActionEvent cuando se pulsa Intro en un cuadro de
texto.
cuadros de contraseña
Se corresponden a la clase JPasswordField que es una subclase de JTextField, con
lo que lo dicho para ésta vale para los cuadros de contraseña. La diferencia está en que
en los cuadros de contraseña, el texto que se escribe queda oculto (normalmente por
asteriscos) siendo ideal para introducir contraseñas en un programa.
métodos
Posee los mismos que los de JTextField ya que deriva de éste. Sin embargo están
obsoletos los métodos getText y setText:
método uso
char getEchoChar() Obtiene el carácter que usa el cuadro de
contraseña para ocultar el contenido.
char[] getPassword() Obtiene el texto del cuadro de contraseña en
forma de array de caracteres. Sustituye a
getText
void setEchoChar(char c) Configura el carácter c para que sea el carácter
con el que se substituya el texto escrito en el
cuadro
botones
Son elementos fundamentales en el trabajo de las aplicaciones. Cuando son pulsados,
generan un evento ActionEvent que, capturado permite crear una acción para el botón.
La clase fundamental es JButton que toma la mayor parte de su funcionalidad de
AbstractButton clase abstracta con la que se definen casi todos los botones.
208
© Jorge Sánchez Asenjo’ 2004
constructores de JButton
constructor uso
JButton() Crea un nuevo botón
209
Manual de Java
componentes Swing
constructor uso
JButton(String texto) Crea un nuevo botón con el texto indicado
JButton(Icon icono) Crea un nuevo botón con el icono indicado
JButton(String texto, Icon icono) Crea un nuevo botón con el texto e icono
indicado
eventos ActionEvent
Los botones y los cuadros de texto (y otros controles) generan eventos del tipo
ActionEvent. Para manipular estos eventos, se debe llamar en cada control que
queramos que lance eventos, al método addActionListener y se indicará el objeto que
manipulará estos eventos (este objeto debe pertenecer a alguna clase que implemente el
interfaz ActionListener). Será el método actionPerformed el que se encargará de
manipular el evento.
Un problema típico consiste en que, a menudo, se necesita averiguar qué botón o
cuadro de texto lanzó el evento. Una forma fácil de saberlo es mediante la cadena
ActionCommand. Esta cadena es un texto que describe al objeto que lanzó el evento.
Se usa de la siguiente forma:
1> El objeto que lanza eventos de este tipo rellena su cadena ActionCommand
usando su método setActionCommand
Ejemplo:
210
© Jorge Sánchez Asenjo’ 2004
if (e.getActionCommand().equals(“bt1”)==true){
System.out.println(“Se pulsó Aceptar”);
}
casillas de activación
Se trata de controles que permiten su activación y desactivación a fin de elegir una serie
de opciones independientes. En Swing es la clase JCheckBox la encargada de
representarlas. Esta clase deriva de JToggleButton, que, a su vez, deriva de
AbstractButton
constructores
constructor uso
JCheckBox() Construye una nueva casilla de verificación
JCheckBox(String texto) Crea una nueva casilla con el texto indicado
JCheckBox(Icon icon) Crea una nueva casilla con el icono indicado
JCheckBox(String texto, boolean Crea una nueva casilla con el texto indicado y
activado) permite elegir si está activada o no
inicialmente
JCheckBox(String texto, Icon icono) Crea una nueva casilla con el texto indicado y el
icono que se elija
JCheckBox(String texto, Icon icono, Crea una nueva casilla con el texto indicado y el
boolean seleccionado) icono que se elija
imágenes
Se pueden crear distintas imágenes de una casilla de verificación al igual que ocurría
con los botones. Los métodos de la clase AbstractButton (véase más arriba), permiten
este hecho.
En el caso de las casillas de verificación, suele bastar con poner un icono inicial en el
constructor o con el método setIcon y después asignar el icono que corresponde al
estado de seleccionado de la casilla con setSelectedIcon.
eventos
Las casillas de verificación lanzan (al ser herederas de los botones), eventos
ActionEvent cuando son seleccionadas. Pero disponen de un evento propio llamado
ItemEvent que se lanza cuando se cambia el estado de una casilla (véase eventos
InputEvent, página 140).
El interfaz relacionado es ItemListener y el método de captura es
itemStateChanged que captura el evento cuando el estado de la casilla ha cambiado.
El método getItemSelectable devuelve la casilla que produjo el evento, mientras que
getStateChanged permite saber qué tipo de cambio ocurrió (ItemEvent.SELECTED o
ItemEvent.DESELECTED)
211
Manual de Java
componentes Swing
Ejemplo:
public VentanaCasillaAct(){
Container conpane = getContentPane();
conpane.setLayout(new FlowLayout());
deportes = new JCheckBox(“Deportes”);
cultura = new JCheckBox(“Cultura”);
descripción = new JLabel(“Tiene elegido: ”);
deportes.addItemListener(this);
deportes.addItemListener(this);
conpane.add(deportes);
conpane.add(cultura);
conpane.add(descripción);
}
public void itemStateChanged(ItemEvent e){
if (e.getItemSelectable()==deportes){
if (e.getStateChange()==ItemEvent.SELECTED)
sdeportes=" deportes";
else sdeportes="";
}
else { //sólo puede haberlo provocado el evento "Cultura"
if (e.getStateChange()==ItemEvent.SELECTED)
scultura=", cultura";
else scultura="";
}
descripción.setText("Tiene elegido:"+sdeportes+scultura);
}
}
botones de opción
Son casi iguales a los anteriores. Sólo que se utilizan para elegir una opción de entre un
grupo de opciones. Como las casillas de verificación, la clase JRadioButton encargada
de crear botones de radio, desciende de JToggleButton.constructores
constructor uso
JRadioButton() Construye un nuevo botón de radio
212
© Jorge Sánchez Asenjo’ 2004
constructor uso
JRadioButton(String texto) Crea un nuevo botón de radio con el texto
indicado
JRadioButton(Icon icon) Crea un nuevo botón de radio con el icono
indicado
JRadioButton(String texto, boolean Crea un nuevo botón de radio con el texto
activado) indicado y permite elegir si está activada o no
inicialmente
JRadioButton(String texto, Icon Crea un nuevo botón de radio con el texto
icono) indicado y el icono que se elija
JRadioButton(String texto, Icon Crea un nuevo botón de radio con el texto
icono, boolean seleccionado) indicado y el icono que se elija
métodos
método uso
void add(AbstractButton boton) Añade el botón al grupo
void remove(AbstractButton boton) Quita el botón del grupo
Con estos dos métodos se añaden botones de radio a un grupo y así sólo se podrá
seleccionar una de las opciones.
eventos
Se manejan los mismos eventos que en las casillas de verificación. Ejemplo (igual que el
de las casillas de activación):
public VentanaBtRadio(){
Container conpane = getContentPane();
conpane.setLayout(new FlowLayout());
deportes = new JRadioButton("Deportes");
cultura = new JRadioButton("Cultura");
descripción = new JLabel("Tiene elegido:");
213
Manual de Java
componentes Swing
ocio.add(deportes);
ocio.add(cultura);
deportes.addItemListener(this);
cultura.addItemListener(this);
conpane.add(deportes);
conpane.add(cultura);
conpane.add(descripción);
}
public void itemStateChanged(ItemEvent e){
if (e.getItemSelectable()==deportes){
descripción.setText("Tiene elegido: deportes");
}
else { //sólo puede haber provocado el evento "Cultura"
descripción.setText("Tiene elegido: cultura");
}
}
}
viewport
Se trata de la clase madre de las clases que permiten desplazamientos (scrolls). Un
viewport es una ventana dentro de la vista actual que muestra una sección de los datos
y que permite desplazar la vista hacia el resto de datos.
La clase que representa los viewports es JViewport.
construcción
Un viewport se construye mediante un constructor por defecto. Una vez creado el
objeto, necesitamos asignarle el componente ligero (un panel normalmente) sobre el
que actuará el viewport. Esa asignación se realiza mediante el método setView al cual
se le pasa el componente a visualizar mediante el viewport.
métodos interesantes
La clase JViewport posee una gran cantidad de métodos. Entre ellos destacan:
método uso
void reshape(int x, int y, int ancho, int Asigna los límites del viewport
alto)
void setBorder(Border borde) Asigna un borde al viewport
void setExtendSize(Dimension nueva) Asigna el tamaño visible de la vista
utilizando coordenadas de vista
void setView(Component panel) Componente ligero sobre el que se
aplica el viewport
214
© Jorge Sánchez Asenjo’ 2004
método uso
void setViewPosition(Point p) Asigna las coordenadas de vista que
aparecen en la esquina superior
izquierda del viewport
void setViewSize(Dimension nueva) Asigna como coordenadas de vista la
esquina superior izquierda y el
tamaño indicado
Dimension Convierte tamaño en formato de
toViewCoordinates(Dimension tamaño) coordenadas de puntos a tamaño en
forma de coordenadas de vista
Point toViewCoordinates(Point tamaño) Convierte punto en coordenadas de
punto a coordenadas de vista
Todos los métodos set indicados en la tabla tienen versión get para obtener valores en
lugar de asignar.
JScrollPane
Se trata de una clase espectacular que permite colocar barras de desplazamiento a
cualquier componente. Usa, al igual que Viewport, la interfaz Scrollable que permite
realizar desplazamientos.
constructores
constructor uso
JScrollPane() Construye un panel de desplazamiento vacío.
JScrollPane(Component c) Construye un panel de desplazamiento para el
componente c
JScrollPane(Component c, int Construye un panel para mostrar el componente c
políticaVertical, int utilizando barras de desplazamiento. Las barras se
políticaHorizontal) configuran a través de los dos argumentos siguientes
usando estas constantes estáticas:
VERTICAL_SCROLLBAR_ALLWAYS. Barra
vertical obligatoria
VERTICAL_SCROLLBAR_AS_NEEDED.
Saldrá la barra vertical cuando se necesite
VERTICAL_SCROLLBAR_NEVER. Nunca
saldrá la barra vertical
HORIZONTAL_SCROLLBAR_ALLWAYS
Barra horizontal obligatoria
HORIZONTAL_SCROLLBAR_AS_NEEDED
Saldrá la barra horizontal cuando se necesite
HORIZONTAL_SCROLLBAR_NEVER.
Nunca saldrá la barra horizontal.
215
Manual de Java
componentes Swing
métodos interesantes
método uso
JScrollBar getHorizontalScrollBar() Devuelve la barra de desplazamiento
horizontal del panel
JScrollBar getVerticalScrollBar() Devuelve la barra de desplazamiento vertical
del panel.
Viewport getViewport() Obtiene el viewport actual de la barra
void setHorizontalScrollBar( Añade la barra que se pasa como argumento
JScrollBar barraHorizontal) para que sea la barra horizontal del panel
void setHVerticalScrollBar( JScrollBar Añade la barra que se pasa como argumento
barraVertical) para que sea la barra vertical del panel
void setVerticalScrollBarPolicy(int Modifica el comportamiento de la barra
políticaVertical) vertical.
void setHorizontalScrollBarPolicy (int Modifica el comportamiento de la barra
políticaHorizontal) horizontal.
Barras de desplazamiento
La clase JScrollBar representa objetos de barra de desplazamiento. Normalmente es
más que suficiente la clase anterior para controlar un componente. No obstante, es
posible utilizar las barras de desplazamiento para acciones interesantes o para
modificar las propiedades de las barras de un JScrollPane.
Las barras tienen estas propiedades:
máximo. El máximo valor que puede representar la barra. Es lo que vale la barra
si la guía está al final.
construcción
constructor uso
JScrollBar() Construye una barra de desplazamiento vacía.
JScrollBar(int orientación) Construye una barra de desplazamiento en la
orientación indicada que puede ser:
JScrollBar.HORIZONTAL
JScrollBar.VERTICAL
216
© Jorge Sánchez Asenjo’ 2004
constructor uso
JScrollBar(int orientación, int Crea una barra de desplazamiento con la orientación,
valor, int extensión, int mínimo, valor, extensión, valor
int máximo)
métodos interesantes
método uso
void setMaximum(int máximo) Ajusta el valor máximo de la barra
void setMinimum(int mínimo) Ajusta el valor mínimo de la barra
void setOrientation(int orientación) Cambiar la orientación de la barra
void setValue(int valor) Ajusta el valor de la barra
void setValues(int valor, int extensión, int Asigna las cuatro propiedades de la barra
mínimo, int máximo)
void setVisibleAmount(int extensión) Asigna la propiedad extensión del modelo
eventos
Las barras generan eventos del tipo AdjustmentEvent cuando se modifican los
valores de las barras. El método que controla estos eventos es AdjustmentEvent.
Por su parte el evento AdjustmentEvent posee dos métodos muy interesantes:
getValue() que devuelve el valor de la barra y getAdjustmentType() que devuelve
un entero que indica el tipo de cambio que se produjo en la barra. Este puede ser:
AdjustmentEvent. UNIT_INCREMENT. Se pulsó en el botón de subir.
AdjustmentEvent. UNIT_DECREMENT Se pulsó en el botón de bajar.
AdjustmentEvent. BLOCK_INCREMENT. Se pulsó entre la guía y el botón
de subir.
AdjustmentEvent. BLOCK_DECREMENT Se pulsó entre la guía y el botón
de bajar.
AdjustmentEvent. TRACK. Se cambió la posición de la guía.
Ejemplo:
217
Manual de Java
componentes Swing
getContentPane().add(etiqueta,BorderLayout.SOUTH);
barra.addAdjustmentListener(this);
}
El resultado es:
deslizadores
La clase JSlider representa un tipo de objeto similar a las barras de desplazamiento
pero pensado únicamente para elegir un valor numérico (al modo del ejemplo expuesto
en las barras de desplazamiento).
construcción
constructor uso
JSlider() Crea un deslizador
JSlider(int orientación) Crea un deslizador en la orientación indicada
(JSlider.HORIZONTAL o JSlider.VERTICAL)
JSlider(int orientación, int Crea el deslizador en la orientación señalada y con el
mínimo, int máximo, int valor) mínimo, máximo y valor inicial señalados.
métodos
método uso
void setMaximum(int máximo) Ajusta el valor máximo de la barra
void setMinimum(int mínimo) Ajusta el valor mínimo de la barra
void setOrientation(int orientación) Cambiar la orientación de la barra
void setValue(int valor) Ajusta el valor de la barra
void setExtent(int valor) Cambia la extensión. La extensión es el
rango de valores máximos a los que el
deslizador no puede llegar. Si el valor
máximo es 100 y la extensión es 40, el
deslizador no podrá pasar de 60.
218
© Jorge Sánchez Asenjo’ 2004
método uso
void setInverted(boolean b) Con valor true hace que los valores vayan
desde el más alto al más bajo (al revés de lo
habitual).
void setPaintLabels(boolean b) Indica si se mostrarán las etiquetas del
deslizador.
void setLabeltable(Dictionary etiquetas) Permite especificar las etiquetas que se
mostrarán en el deslizador.
void setPaintTicks(boolean b) Indica si se mostrarán las marcas del
deslizador.
void setPaintTrack(boolean b) Indica si se pinta la guía del deslizador
void setSnapToTicks(boolean b) Hace que la guía se ajuste automáticamente
a las marcas.
void setMajorTickSpacing(int n) Modifica el espaciamiento entre las marcas
mayores del deslizador
void setMinorTickSpacing(int n) Modifica el espaciamiento entre las marcas
menores del deslizador
Hay métodos get que permiten obtener algunos de los valores ajustados (getValue(),
getOrientation(), getMajorTickSpacing(), getLabelTable(), getPaintTicks(),
etc.).
marcas y rellenos
Los deslizadores permiten mostrar marcas para facilitar al usuario la selección del valor
requerido. Eso lo hace el método setPaintTicks, mientras que otros métodos permiten
especificar el espacio entre las marcas y otras propiedades.
A su vez se puede utilizar esta sintaxis:
slider.putClientProperty(“JSlider.isFilled”, Boolean.TRUE);
Esto permite cambiar la propiedad cliente isFilled de los objetos JSlider y hacer así que
se muestre un relleno
eventos
JSlider puede provocar eventos ChangeEvent, que deben ser capturados en clases que
implementen la interfaz ChangeListener. Para que un deslizador lance eventos, debe
usar el método addChangeListener. El método de captura de estos eventos es
stateChanged que se producirá cuando se cambie de posición al deslizador. Ejemplo:
public VentanaSlider() {
barra=new JSlider(JSlider.HORIZONTAL,0,100,0);
219
Manual de Java
componentes Swing
getContentPane().add(barra,BorderLayout.NORTH);
getContentPane().add(etiqueta,BorderLayout.SOUTH);
}
listas
Son controles que permiten elegir entre un conjunto de alternativas. Al principio de
muestra sólo un pequeño grupo de opciones. Se puede elegir una sola opción o varias si
se hace clic manteniendo pulsada la tecla Control.
La clase de creación de listas es JList, heredera de JComponent.
construcción
constructor uso
JList() Crea un objeto de lista
JList(Object[] listData) Crea una lista con el contenido del
array de objetos, normalmente un
array de Strings
220
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
métodos
método uso
void clearSelection(int máximo) Borra la selección actual
void ensureIndexIsVisible(int índice) Desplaza el viewport de la lista para
asegurar que se muestra el elemento
número índice
int getFirstVisibleIndex() Devuelve el primer número de índice visible
en la lista
int getLastVisibleIndex() Devuelve el último número de índice visible
en la lista
int getMaxSelectionIndex() Devuelve el índice máximo de la selección
actual
int getMinSelectionIndex() Devuelve el índice mínimo de la selección
actual
Dimension Calcula el tamaño del viewport necesario
getPreferredScrollableViewportSize() para mostrar visualizar visibleRowCount
filas
int getSelectionMode() Indica si se puede seleccionar de forma
múltiple o simple
int getSelectedIndex() Obtiene el número del primer índice
seleccionado
int[] getSelectedIndices() Devuelve un array de números de índice de
cada valor seleccionado.
Object getSelectedValue() Obtiene el primer valor seleccionado
Object[] getSelectedValues() Devuelve un array de valores para las celdas
seleccionadas
boolean isSelectionEmpty() true si no hay nada seleccionado
boolean is SelectedIndex(int i) true si el índice señalado está seleccionado
void setFixedCellHeight(int alto) Define la altura de cada celda de la lista
void setFixedCellWidth(int ancho) Define la anchura de cada celda de la lista
void setSelectedIndex(int i) Selecciona sólo la celda número i
void setSelectedIndices(int[] indices) Selecciona los índices señalados
void setSelectionBackground(Color c) Asigna color de fondo a las celdas
seleccionadas
void setSelectionForeground(Color c) Asigna color de texto a las celdas
seleccionadas
221
Manual de Java
componentes Swing
método uso
void setSelectionMode(int modo) Permite cambiar el modo de selección de la
lista. Puede tener como valores, constantes
de la clase ListSelectionModel:
SINGLE_SELECTION. Selección
de una única opción
SINGLE_INTERVAL_SELECTION
Selecciona varias celdas, pero sólo si
están seguidas
MULTIPLE_INTERVAL_SELECTION
Permite seleccionar cualquier número
de celdas en cualquier orden
void setVisibleRowCount(int n) Indica el número preferido de componentes
que se pueden mostrar en la lista
eventos
Las listas se controlan con eventos ListSelectionEvent, en el paquete
javax.swing.event, que se lanzan en listas que hagan uso del método
addListSelectionListener. Las clases que deseen controlar esos eventos deben
implementar la interfaz ListSelectionListener el cual obliga a definir el método
valueChanged que será llamado cuando se modifique el valor de una lista.
Ejemplo:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public VentanaLista() {
JScrollPane sp=new JScrollPane(lista)
p1.setLayout(new FlowLayout());
p2.setLayout(new FlowLayout());
222
© Jorge Sánchez Asenjo’ 2004
lista.setVisibleRowCount(5);
lista.addListSelectionListener(this);
p1.add(sp);
p2.add(etiqueta);
getContentPane().add(p1,BorderLayout.CENTER);
getContentPane().add(p2,BorderLayout.SOUTH);
}
public void valueChanged(ListSelectionEvent e) {
String texto="Seleccionado: ";
int i;
//Se añade el texto del siguiente elemento
//seleccionado hasta el último
for(i=0;i<=lista.getSelectedValues().length-1;i++){
texto+=(String) lista.getSelectedValues()[i]+" ";
}
etiqueta.setText(texto);
}
cuadros combinados
Son listas especiales que combinan las capacidades de una lista y un cuadro de texto. En
apariencia son cuadros de texto. Pero un botón con una flecha permite abrirles para
seleccionar una (y sólo una) opción. Es uno de los controles más populares de Windows.
constructores
constructor uso
JComboBox() Crea un objeto de lista
JComboBox(Object[] o) Crea el cuadro combinado con el
contenido del array de objetos,
normalmente un array de Strings
métodos
método uso
void addItem(Object o) Añade el objeto o al final de la lista
Object getItemAt(int index) Obtiene el elemento número index
de la lista
int getItemCount() Devuelve el número de elementos de
la lista
Object getSelectedItem() Obtiene el elemento seleccionado
actual
void hidePopup() Esconde la ventana emergente del
cuadro
223
Manual de Java
componentes Swing
método uso
void insertItemAt(Object objeto, int Añade el objeto en la posición
posición) indicada
boolean isEditable() true si el cuadro de texto es editable
boolean isPopupVisible() true si se puede ver la ventana
emergente
void removeAllItems() Borra todos los elementos de la lista
combinada
void removeItemAt(int posición) Quita el elemento situado en la
posición indicada
boolean selectWithKeyChar(char c) Selecciona el primer elemento de la
lista que comienza con la letra
indicada
void setEditable(boolean b) Permite (o no) que el cuadro de texto
sea editable
void setMaximumRowCount(int n) Ajusta el número máximo de filas
visibles. Si hay más filas, aparecerá
una barra de desplazamiento.
void setPopupVisible(boolean b) Despliega el cuadro de la lista
void setSelectedIndex(int i) Selecciona el elemento de la lista
número i
void setSelectedItem(Object o) Selecciona el objeto marcado
eventos
Al ser un control mixto, que puede editar texto o seleccionar un elemento de la lista,
envía dos tipos de eventos: ActionEvent (comentado anteriormente, en especial en el
apartado de los botones) al pulsar Intro, e ItemEvent cuando se cambia un elemento
de la lista (visto anteriormente en los controles de casilla de verificación).
El método de evento getStateChange() permite saber si el cambio fue para
seleccionar (ItemEvent.SELECTED) o para deseleccionar (ItemEvent.DESELECTED).
No suele ser necesario usarles ya que la clase JComboBox tiene elementos de sobra
para trabajar.
Ejemplo(equivalente al ejemplo de la lista).
public VentanaListaCombinada() {
p1.setLayout(new FlowLayout());
p2.setLayout(new FlowLayout());
224
© Jorge Sánchez Asenjo’ 2004
lista.setMaximumRowCount(5);
lista.addItemListener(this);
p1.add(lista);
p2.add(etiqueta);
getContentPane().add(p1,BorderLayout.CENTER);
getContentPane().add(p2,BorderLayout.SOUTH);
}
public void itemStateChanged(ItemEvent e) {
etiqueta.setText("Seleccionado:"+
(String)lista.getSelectedItem());
}
}
selectores de color
La clase JColorChoose permite obtener cuadros de selección de colores.
constructores
constructores uso
JColorChooser() Muestra un cuadro genérico de selección de
colores
JColorChooser(Color c) Muestra un cuadro genérico de selección de
colores usando como color preseleccionado,
el que define el parámetro.
JColorChooser(ColorSelectionModel Muestra un cuadro genérico de selección de
csm) colores usando el modelo de selección de
colores indicado.
225
Manual de Java
componentes Swing
métodos
métodos uso
AbstractColorChooserPanel[] Obtiene un array con todos los paneles de
getChooserPanels() selección de colores del cuadro.
static JDialog Crea un cuadro de diálogo en el componente
createDialog(Component padre, String padre especificado con botones de Ok y
título, boolean modal, JColorChooser Cancelar. Tendrá especificado por
jcc, ActionListenerk botónOK, parámetros: el título, una indicación de si se
ActionListener botónCancelar desea que sea modal o no, el JColorChooser
que irá incluido en el cuadro y dos
indicaciones sobre qué objetos escucharán al
botón Ok y al botón Cancelar.
Color getColor() Obtiene el color actualmente seleccionado en
el cuadro
JComponent getPreviewPanel() Obtiene el panel de previsualización del color
elegido.
ColorSelectionModel Obtiene el modelo de selección de colores
getSelectionModel() actual
AbstractColorChooserPanel Elimina el panel especificado del cuadro
removeChooserPanel(
AbstractColorChooserPanel panel)
226
© Jorge Sánchez Asenjo’ 2004
métodos uso
void setChooserPanel( Establece los paneles de selección a través del
AbstractColorChooserPanel[] paneles) array que se pasa como parámetro.
void setColor(Color color) Establece el color indicado como color actual
del cuadro.
void setPreviewPanel(JComponent Establece el panel indicado como de
panel) previsualización de colores del cuadro.
void setSelectionModel( Establece el modo de selección de colores del
ColorSelectionModel csm) cuadro.
static Color Muestra un cuadro de selección de colores
showDialog(Component padre, para el componente padre, con el título
String título, Color colorInicial) indicado y un primer color seleccionado.
Si el usuario acepta el cuadro, devolverá el
color elegido y si no devuelve null.
Selección de archivos
La clase JFileChooser se utiliza para seleccionar archivos. Su funcionamiento es muy
parecido al cuadro anterior
constructores
constructores uso
JFileChooser() Crea un nuevo cuadro de selección de
archivos
JFileChooser(String ruta) Utiliza una cadena de texto como ruta
predeterminada para el cuadro de selección
de archivos
JFileChooser(String ruta, Igual que el anterior pero utiliza además el
FileSystemView vista) objeto de vista de archivos indicada
JFileChooser(File ruta) Utiliza la ruta dada en forma de objeto File
como ruta predeterminada para el cuadro de
selección de archivos
JFileChooser(File ruta, Igual que el anterior pero utiliza además el
FileSystemView vista) objeto de vista de archivos indicada
JFileChooser(FileSystemView vista) Crea un selector de archivos con el objeto de
vista de archivos indicado
métodos
métodos uso
void addActionListener Aplica un objeto oyente de eventos de acción
(ActionListener al) al cuadro
227
Manual de Java
componentes Swing
métodos uso
void addChosableFileFilter(FileFilter Añade un filtro de archivos al cuadro de
ff) selección de archivos
void approveSelection() Es llamado automáticamente cuando el
usuario acepta el cuadro pulsando “Abrir” o
“Guardar”
void cancelSelection() Es llamado automáticamente cuando el
usuario cancela el cuadro
void ChangeToParentDirectory() Hace que el cuadro cambie al directorio padre
del que mostraba hasta ese momento
void ensureFileIsVisible(File f) Asegura que el archivo f sea visible en el
cuadro
FileFilter getAcceptAllFileFilter() Obtiene el filtro de sistema referido a todos
los archivos de la carpeta (en Windows es la
expresión *.*)
FileFilter[] getChoosableFileFilters() Obtiene la lista de todos los filtros que el
usuario puede escoger en el cuadro
File getCurrentDirectory() Obtiene el directorio actual en forma de
objeto File
String getDescription(File f) Obtiene la cadena que describe el tipo de
archivo al que pertenece f
int getDialogType() Indica el tipo de cuadro que es el selector de
archivo, puede ser:
JFileChooser.SAVE_DIALOG
JFileChooser.OPEN_DIALOG
JFileChooser.CUSTOM_DIALOG
FileFilter getFileFilter() Obtiene el filtro de archivos que se aplica
actualmente al cuadro
FileSystemView getFileSystemView() Obtiene el objeto de vista de archivos de
sistema actual
FileView getFileView() Obtiene el objeto de vista de archivos actual
Icon getIcon(File f) Devuelve el icono del archivo
Icon getName(File f) Obtiene el nombre del archivo
File getSelectedFile() Obtiene el archivo seleccionado
File[] getSelectedFiles() Devuelve la lista de archivos seleccionados
String getTypeDescription(File f) Obtiene una cadena descriptiva del tipo de
archivos al que pertenece f
boolean isAcceptAllFileFilter() true si el filtro general (*.* en Windows) está
seleccionado
boolean isDirectorySelectionEnabled() true si se permiten seleccionar carpetas
boolean isFileHiddingEnabled() true si no se muestran los archivos ocultos en
el cuadro
boolean isFileSelectionEnabled() Indica si se permite seleccionar archivos
228
© Jorge Sánchez Asenjo’ 2004
métodos uso
boolean isMultiSelectionEnabled() Indica si se permite la selección múltiple de
elementos en el cuadro
boolean isTraversable(File f) true si se puede entrar en el directorio
representado por el objeto f
void rescanCurrentDirectory() Refresca el contenido de la carpeta actual
void resetChoosableFileFilter() Restaura el filtro de archivos a su posición
inicial
void setApproveButtonMnemonic( Activa el carácter c para que sea la tecla de
char c) acceso rápido al botón de aceptar
void setApproveButtonText(String Establece el texto del botón de aprobación del
texto) cuadro
void setControlButtonsAreShown( Establece si se muestran los botones de
boolean b) aprobación y cancelación en el cuadro
void setCurrentDirectory(File f) Hace que la carpeta representada por el
objeto f se considere la carpeta actual del
cuadro
void setDialogTitle(String título) Establece el título del cuadro de diálogo
void setDialogType(int tipo) Establece el tipo de cuadro de diálogo. Las
posibilidades son:
JFileChooser.SAVE_DIALOG
Guardar
JFileChooser.OPEN_DIALOG
Abrir
JFileChooser.CUSTOM_DIALOG
Personal
void setFileFilter(FileFilter ff) Establece el filtro de archivos actual
void setFileHiddingEnabled(boolean Indica si se muestran los archivos ocultos
b)
void setFileSelectionEnabled(boolean Indica si se permite selección de archivos en
b) el cuadro
void setFileSelectionMode(int modo) Indica el modo de selección de archivos del
cuadro. Puede ser:
JFileChooser.FILES_ONLY Sólo
archivos
JFileChooser.DIRECTORIES_ONLY.
Sólo directorios
JFileChooser.FILES_AND_DIRECTORIES.
Ambas cosas
229
Manual de Java
componentes Swing
métodos uso
void setFileView(FileView fv) Establece el tipo de vista de. Lo cual indica
qué tipo de iconos se ven por ejemplo
void Establece la selección múltiple de archivos (si
setMultiSelectionEnabled(boolean b) se indica un true como argumento)
void setSelectedFile(File f) Establece f como el archivo o carpeta
actualmente seleccionado en el cuadro
void setSelectedFiles(File[] lista) Hace que la lista de archivos se muestre como
conjunto de archivos seleccionados en el
cuadro
int showDialog(Component c, String Crea un cuadro de diálogo asociado al selector
texto) de archivos preparado con un botón de
aprobación que posee el texto indicado.
int showOpenDialog(Component padre) Crea un cuadro de apertura de archivo
int showSaveDialog(Component padre) Crea un cuadro de guardado de archivo
Ejemplo:
Para que este ejemplo funcione, hay que definir la clase ImgFileFilter. Gracias a esa
clase se consigue (como se verá en el siguiente ejemplo), que se muestren sólo archivos
JPG y GIF.
Eso es lo que se llama un filtro. Los filtros se crean usando clases derivadas de la
clase abstracta javax.swing.filechooser.FileFilter. Las clases de filtros deben
incluir un método accept que devuelva verdadero para todos los archivos que se deben
mostrar en el cuadro y un método getDescription que devuelva una cadena indicando
el tipo de archivos que el filtro es capaz de mostrar. La definición sería:
230
© Jorge Sánchez Asenjo’ 2004
El cuadro sería:
231
applets
introducción
Sin duda uno de los pilares de Java es su dedicación a la programación en red. Esta
programación crea aplicaciones distribuida desde una red. La lentitud de Internet ha
propiciado que las aplicaciones distribuidas sigan siendo problemáticas. Por ello se
tiende a que la aplicación que se distribuye sea sencilla para conseguir que llegue lo
antes posible.
De hecho la idea es que un usuario no perciba diferencia alguna entre una página
con Java y una página sin Java. Esta idea no está conseguida del todo, pero sí se ha
realizado un importante avanza mediante esos subprogramas Java incrustados dentro
del código normal (HTML) de una página web. Estos subprogramas son llamados
applets.
Un applet es un programa Java que tiene ciertas particularidades derivadas de la
idea de tener que colocar ese programa en una página web. Para que la página web
HTML muestre el subprograma Java, tiene que incluir una etiqueta applet que es la
encargada de asociar el archivo class que representa al subprograma con la página. Esa
misma etiqueta determina la posición y el tamaño del applet
Probar la applet implicar abrir la página, aunque el JDK de Java incluye un programa
llamado appletviewer para mostrar el applet.
En definitiva los pasos para crear un applet son:
1> Crear una página web dejando hueco para colocar el applet de Java.
4> Al publicar la página se deben enviar los archivos HTML junto con los
archivos class y los archivos necesarios para que la página y applet se vean
correctamente (imágenes, hojas de estilo,...).
c:\ejemplos>appletviewer applet.java
233
Manual de Java
applets
El resultado es una ventana que muestra el applet sin usar página web.
En el código java se puede incluir la etiqueta applet de HTML a fin de probar el
applet con un tamaño y configuración concretos. Para ello se coloca la etiqueta en los
comentarios del archivo:
import javax.swing.JApplet;
/* <applet code=”applet.class”
width=200 height=200>
</applet>
*/
public class applet extends JApplet{
...
compatibilidad
Un problema inherente a la naturaleza de la web, es que no podemos saber el software
que posee el usuario. Si la red utilizada para acceder a la página con applet es Internet,
entonces no tendremos ninguna seguridad de qué versión de intérprete de Java posee el
usuario. De hecho ni siquiera sabremos si puede ejecutar subprogramas Java.
Esto provoca los siguientes problemas:
234
© Jorge Sánchez Asenjo’ 2004
Aún avisando a nuestros usuarios de que deben poseer una versión más moderna
de Java, éstos no se animarán a descargar un entorno de ejecución más moderno,
bien porque consideran que el problema es ajeno a ellos, bien porque las
descargas siguen siendo muy lentas desde Internet, o bien por que tienen miedo
de que sea un truco de un pirata que intenta introducirnos software dañino.
Sun proporciona dos soluciones para instalar plataformas de ejecución Java (JRE, Java
Runtime Environment) y evitar estos problemas en lo posible.
http://www.java.com/es/download (español)
http://www.java.com/en/download (inglés).
De todas las formas cuando un usuario llega a una página con applets creadas con
versiones superiores al plugin de Java instalado, entonces se le pedirá descargar el
plugin. Otra cuestión es que el usuario permita esa descarga.
235
Manual de Java
applets
método init
Este método es llamado automática tras crearse el applet. Aquí se prepara el programa,
los recursos necesarios, los elementos GUI, etc. No se deben realizar estas
operaciones en el constructor. El constructor de un applet no está pensado para
esto, sólo está para crear en sí el applet.
método start
Es llamado cuando el programa se hace visible. Se le llama tantas veces sea necesario.
método stop
Es llamado cuando el programa se hace invisible. Es decir, cuando se cierra la ventana
de la página web en la que está incrustado el applet o cuando el usuario acude a otra
página.
Es este método se debería detener las actividades innecesarias. Son innecesarias
aquellas que no debería seguir realizándose cuando el programa está detenido; de otro
modo se consumen recursos inútiles en el ordenador del usuario.
Por otro lado hay veces en las que puede interesar que el programa continúe
realizando operaciones en segundo plano.
método destroy
Es el método que se implementa para eliminar todos los objetos antes de que el applet
desaparezca. Cerrar sockets, eliminar objetos GUI, … son tareas habituales de este
método. El método es llamado cuando se elimina del todo; esto es difícil de establecer
ya que cada navegador realiza esta tarea en diferentes momentos.
otros métodos
métodos uso
Container getContentPane() Obtiene el content pane de la applet. Es ahí
donde se suelen colocar el resto de los elementos
de las applets
Component getGlassPane() Devuelve el glass pane
JMenuBar getJMenuBar() Devuelve la barra de menús
JLayeredPane getLayeredPane() Devuelve el layered pane de la applet
JRootPane getRootPane() Obtiene el root pane
void remove(Component c) Elimina el componente c de la applet
void setContentPane(Container c) Hace que el contenedor c sea el actual content
pane de la applet
void setGlassPane(Component c) Hace que el componente c sea asignado como
glass pane de la applet
void setJMenuBar(JMenuBar Asigna un menú a la ventana
menu)
void Cambia el layered pane de la applet
setLayeredPane(JLayeredPane l)
236
© Jorge Sánchez Asenjo’ 2004
métodos uso
void setLayout(LayoutManager l) Cambia la disposición de la página por la
indicada por l (véase administración de diseño,
página 193)
void repaint() Llama al método paint
la etiqueta applet
La etiqueta applet tiene diversos atributos para controlar adecuadamente la carga y
ejecución del applet. Los atributos (se usan así:
atributo significado
ARCHIVE Indica el archivo JAR en el que está colocado el archivo class
indicado en el atributo code
237
Manual de Java
applets
atributo significado
CODEBASE URL que indica el directorio en el que se busca el código de la
applet. Si la applet está en un paquete o en un archivo JAR,
este atributo hace referencia al directorio que contiene a ese
paquete o archivo JAR.
CODE Ruta a la applet desde el directorio anterior, o desde el
directorio en el que está la página (si no se indicó codebase).
Debe incluir la extensión class
ALT Texto alternativa a mostrar por los navegadores que no han
cargado el applet por alguna razón
NAME Nombre del la applet en el navegador. Esto permite que las
applets interactúen entre sí
WIDTH Anchura de la applet en la página
HEIGHT Altura de la applet en la página
ALIGN Alineación de la applet respecto a los elementos que la siguen.
Los valores que más se usan para este atributo son left y right
VSPACE Espacio se deja en vertical alrededor de la applet
HSPACE Espacio se deja en horizontal alrededor de la applet
parámetros
Desde la etiqueta applet se pueden pasar parámetros a una applet. Eso facilita
reutilizar applets de una aplicación a otra, ya que se puede personalizar el resultado de
la misma gracias a estos parámetros.
De esto se encarga una etiqueta HTML que debe ir en el interior de las etiquetas
applet. La etiqueta en cuestión se llama param y tiene dos atributos: name que
indica el nombre del parámetro y value que posee el valor inicial de ese parámetro.
Desde el programa Java, es la función getParameter procedente de la clase
Applet la que captura el valor de un parámetro. Devuelve siempre un String, por lo que
se deben convertir los valores dentro del código Java si queremos usar número o fechas
por ejemplo. Ejemplo:
Código en la página web:
Código Java
238
© Jorge Sánchez Asenjo’ 2004
El resultado será una página que muestra el texto Esto es una prueba.
línea de estado
La línea de estado del navegador es la franja gris inferior de la ventana en la que el
navegador dispone diversa información. La applet puede requerir escribir en esa barra
haciendo uso del método showStatus, al cual se le pasa el texto que se desea escribir.
try{
URL u=new URL("http://www.jorgesanchez.net");
getAppletContext().showDocument(u);
}
catch(MalformedURLException mfe){
JOptionPane.showConfirmDialog(this,"Error al cambiar ruta");
}
Applet ap2=getAppletContext().getApplet(“applet2”);
239
Manual de Java
applets
paquetes
Las clases se agrupan en paquetes como ya se ha comentado anteriormente en este
manual. Eso se suele respetar también cuando el applet es publicado en la red. Pero hay
que tener en cuenta que se tiene que respetar esa estructura.
Se utiliza el atributo codebase de la etiqueta applet para indicar desde donde
comienza la ruta del archivo class. El inicio debe ser la raíz de las applets. A partir de ahí
se indica el nombre completo (incluyendo el nombre del paquete) de clase en el atributo
code. Ejemplo:
<applet codebase=”http://www.jorgesanchez.net/”
code=”utiles.relojes.analogico.class”>
También es válido:
<applet codebase=”http://www.jorgesanchez.net/”
code=”utiles/relojes/analogico.class”>
archivos JAR
El SDK de Java posee una utilidad muy poderosa que permite comprimir varias clases
en un archivo que posee extensión JAR. Normalmente se empaqueta toda una
aplicación en cada archivo JAR.
En el caso de las applets, se recomienda mucho su uso ya que reducen notablemente
la carga al ser archivos comprimidos. Además facilitan el mantenimiento de las applets
ya que hay que publicar un solo archivo.
Su uso es sencillo, hay que entender que un archivo JAR es un tipo especial de
paquete. El compilador del SDK entiende su uso, las applets no tienen problema
tampoco en usarlo e incluso se pueden colocar otros tipos de archivos (como imágenes,
audio,...) dentro del conjunto comprimido JAR y se pueden recuperar utilizando
getClass().getResource(rutaArchivo)
El programa jar del SDK es el que realiza esta operación. Pero casi todos los
entornos de programación poseen facilidades de creación de archivos JAR. Ejemplos:
manifesto
Los archivos JAR incluso pueden incluir carpetas dentro e incluir un texto descriptivo
llamado manifiesto (manifesto). Gracias a este texto se le pueden dar funcionalidades
avanzadas a los archivos JAR. El manifiesto es un archivo de texto que contiene líneas
240
© Jorge Sánchez Asenjo’ 2004
Manifest-Version: 1.0
Created-By: NetBeans IDE
Specified-By: pruebaJAR/a1.jarContent
Name:H41.class
RevisionNumber:4.0
Name:H42.class
RevisionNumber:2.0
Ejemplos:
e páginaHTML
applets
Applet1.class
ap.jar
241
Manual de Java
applets
2> La misma situación, pero ahora hay una carpeta llamada jars en la que se
encuentra el archivo jar:
e páginaHTML
applets
jars
Applet1.class
ap.jar
3> Ahora la página web se encuentra en una carpeta y el archivo jar en otra que
parten de la misma raíz
webs
e páginaHTML
applets
jars
Applet1.class
ap.jar
el administrador de seguridad
Se trata del programa conocido como Security Manager. Está incluido dentro de los
navegadores y sirve para controlar las operaciones realizadas por las applets. Los
usuarios pueden configurarlo con lo que es imposible saber la configuración de
seguridad en cada ordenador.
En un caso normal este administrador prohíbe:
Abrir conexiones de red hacia un ordenador distinto del que se utilizó para crear
el applet
242
© Jorge Sánchez Asenjo’ 2004
applets firmados
Se trata de una posibilidad interesante. Permite autentificar al autor del applet y de esta
forma conseguir que haya applets de confianza a los que se les permite un acceso menos
restringido al sistema.
Esta autentificación se consigue por medio de una firma digital. Esta firma
autentifica al creador de la applet y así, si el usuario confía en ese creador, se podrán
realizar operaciones que, de otro modo, prohibiría el administrador de seguridad.
Las firmas se basan en un sistema de codificación de datos por clave pública, lo cual
significa que la firma en realidad es una clave pública que sirva para codificar nuestros
datos. Una segunda clave (privada) permitirá decodificarlos. El usuario receptor de la
applet recibe esa clave pública.
La cuestión es ¿cómo podemos estar seguros de que esa clave pertenece a quien dice
pertenecer? Es decir, cómo se verifica la autenticidad de la clave. Eso se realiza
mediante certificados de autenticidad (CA). Estos certificados son emitidos por una
entidad emisora que verifica que esa clave pertenece realmente a quien dice pertenecer.
Esto significa que la confianza se traspasa ahora a esta entidad.
Podría surgir la duda de si esa entidad es válida. El problema puede continuar
intentando verificar la identidad del CA, pero ahora las posibilidades son cada vez más
reducidas al haber menos claves a verificar.
certificados de sitio
Estos certificados se envían al navegador para validar las firmas de los archivos JAR.
Estos certificados se pueden guardar para asignarles más o menos privilegios cuando
utilicemos sus applets.
certificados de usuario
Estos se envían en dirección contraria, es decir al sitio. Son los certificados que verifican
la identidad del usuario.
firmar archivos
keytool es una utilidad que viene con el SDK de Java que permite manejar toda una
base de datos de identidades. Mientras que jarsigner permite firmar los archivos jar
que se deseen. Se puede obtener información de:
http://java.sun.com/products/jdk/1.2/docs/tooldocs/win32/keytool.html
http://java.sun.com/products/jdk/1.2/docs/tooldocs/win32/jarsigner.html
243
Manual de Java
applets
Esto importa el certificado y lo almacena en el almacén por defecto con el alias jorge y la
contraseña mimono. Después nos pregunta la utilidad keytool si podemos confiar en el
certificado, al decir que sí, estamos diciendo que confiamos en el certificado.
Sin embargo este certificado instalado sólo vale para confiar en él. Si deseamos
incorporar nuestro certificado para firmar, entonces necesitamos conseguir un
certificado que incluya la clave privada (keytool tiene capacidad para conseguir claves
privadas). Con nuestra clave pública y privada deberemos acudir a un organismo emisor
y así tendremos un archivo que valida nuestras contraseñas. Para añadirle:
En el archivo META-INF del archivo JAR se añadirá una entrada Jorge.SF (firma del
archivo) y Jorge.DSA (firma binaria real).
Para probar se puede crear una autofirma (aunque sin validez al no haber
certificador), de esta forma:
244
programación en red
introducción
Sin duda la red es el contexto de trabajo fundamental de java. Lo mejor de Java está
creado para la red. El paquete java.net es el encargado de almacenar clases que
permitan generar aplicaciones para redes. En él podremos encontrar clases orientadas a
la programación de sockets y herramientas de trabajo con URLs.
También se utiliza mucho el paquete java.io (visto en el tema dedicado a la entrada
y salida, página 93). Esto se debe a que la comunicación entre clientes y servidores se
realiza intercambiando flujos de datos, por lo que las clases para controlar estos flujos
son las mismas que las vistas en el tema citado.
sockets
Son la base de la programación en red. Se trata de el conjunto de una dirección de
servidor y un número de puerto. Esto posibilita la comunicación entre un cliente y un
servidor a través del puerto del socket. Para ello el servidor tiene que estar escuchando
por ese puerto.
Para ello habrá al menos dos aplicaciones en ejecución: una en el servidor que es la
que abre el socket a la escucha, y otra en el cliente que hace las peticiones en el socket.
Normalmente la aplicación de servidor ejecuta varias instancias de sí misma para
permitir la comunicación con varios clientes a la vez.
Socket
Socket
et
ck
Programa Cliente
So
Socket
er
rv
Socket
Se
Socket
Programa Cliente
Socket
clientes
Las aplicaciones clientes son las que se comunican con servidores mediante un socket.
Se abre un puerto de comunicación en ordenador del cliente hacia un servidor cuya
dirección ha de ser conocida.
La clase que permite esta comunicación es la clase java.net.Socket.
245
Manual de Java
programación en red
construcción de sockets
constructor uso
Socket(String servidor, int puerto) Crea un nuevo socket hacia el servidor
throws IOException, utilizando el puerto indicado
UnknownHostException
Socket(InetAdress servidor, int puerto) Como el anterior, sólo que el servidor se
throws IOException establece con un objeto InetAddress
Socket(InetAdress servidor, int puerto, Crea un socket hacia el servidor y puerto
InetAdress dirLocal, int puertoLocal) indicados, pero la lectura la realiza la dirección
throws IOException local y puerto local establecidos.
Socket(String servidor, int puerto, Crea un socket hacia el servidor y puerto
InetAdress dirLocal, int puertoLocal) indicados, pero la lectura la realiza la dirección
throws IOException, local y puerto local establecidos.
UnknownHostException
Ejemplo:
try{
Socket s=new Socket(“time-a.mist.gov”,13);
}
catch(UnknownHostException une){
System.out.println(“No se encuentra el servidor”);
}
catch(IOException une){
System.out.println(“Error en la comunicación”);
}
método uso
InputStream getInputStream() Obtiene la corriente de entrada de datos para el
throws IOException socket
OutputStream getOutputStream() Obtiene la corriente de salida de datos para el
throws IOException socket
246
© Jorge Sánchez Asenjo’ 2004
try{
Socket socket=new Socket(“servidor.dementiras.com”,7633);
BufferedReader in=new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out=new PrintWriter(
socket.getOutputStream(),true)); // el parámetro
//true sirve para volcar la salida al
//dispositivo de salida (autoflush)
boolean salir=false;
do {
s=in.readLine();
if(s!=null) System.out.println(s);
else salir=true;
}while(!salir);
}
catch(UnknownHostException une){
System.out.println(“No se encuentra el servidor”);
}
catch(IOException une){
System.out.println(“Error en la comunicación”);
}
servidores
En el caso de un programa de servidor, éste se ha de ocupar de recibir el fuljo de datos
que procede del socket del cliente (además tiene que procurar servir a varios clientes a
la vez).
Para que un programa abra un socket de servidor. Se usa la clase ServerSocket
cuyo constructor permite indicar el puerto que se abre:
Después se tiene que crear un socket para atender a los clientes. Para ello hay un
método llamado accept que espera que el servidor atienda a los clientes. El
funcionamiento es el siguiente, cuando el socket de servidorEste método obtiene un
objeto Socket para comunicarse con el cliente. Ejemplo:
try{
ServerSocket s=new ServerSocket(8189);
Socket recepcion=s.accept();
//El servidor espera hasta que llegue un cliente
247
Manual de Java
programación en red
Este es un servidor que acepta texto de entrada y lo repite hasta que el usuario escribe
ADIOS. Al final la conexión del cliente se cierra con el método close de la clase Socket.
248
© Jorge Sánchez Asenjo’ 2004
out.println("Bienvenido”);
boolean salir=false;//controla la salida
while (!salir){
resp=in.readLine();//lectura
...//proceso de datos de lectura
out.println(“....”);//datos de salida
if(...) salir=true;//condición de salida
}
out.println("ADIOOOOOOS");
socket.close();
}catch(Exception e){}
métodos de Socket
método uso
void setSoTimeout(int tiempo) Establece el tiempo máximo de bloqueo cuando
se está esperando entrada de datos por parte
del socket. Si se cumple el tiempo, se genera
una interrupción del tipo:
InterruptedIOException.
249
Manual de Java
programación en red
método uso
void shutdownOutput() Cierra el flujo de salida de datos para que el
servidor (en aquellos que funcionan de esta
forma) sepa que se terminó el envío de datos.
Disponible desde la versión 1.3
void shutdownInput() Cierra el flujo de entrada de datos. Si se intenta
leer desde el socket, se leerá el fin de archivo.
Desde la versión 1.3
clase InetAddress
Obtiene un objeto que representa una dirección de Internet. Para ello se puede emplear
este código:
InetAddress dirección=InetAddress.getByName(
“time-a.nist.gov”);
System.out.println(dirección.getHostAddress());//129.6.15.28
InetAddress[] nombres =
InetAddress.getAllByName("www.elpais.es");
System.out.println(nombres.length);
for(int i=0;i<nombres.length;i++) {
System.out.println(nombres[i].getHostAddress());
}
//Escribe:
//195.176.255.171
//195.176.255.172
lista de métodos
método uso
static InetAddress getByName(String Obtiene el objeto InetAddress que corresponde
servidor) al servidor indicado
static InetAddress getAllByName(String Obtiene todos los objetos InetAddress asociados
servidor) al servidor indicado
static InetAddress getByAddress(byte[] Obtiene el objeto InetAddress asociado a esa
direcciónIP) dirección IP
static InetAddress getLocalHostName() Obtiene el objeto InetAddress que corresponde
al servidor actual
String getHostAddress() Obtiene la dirección IP en forma de cadena
byte[] getAddress() Obtiene la dirección IP en forma de array de
bytes
String getHostName() Obtiene el nombre del servidor
250
© Jorge Sánchez Asenjo’ 2004
método uso
String getCanonicalHostName() Obtiene el nombre canónigo completo (suele ser
la dirección real del host
conexiones URL
Realizar conexiones mediante sockets tiene el inconveniente de que hay que conocer las
conexiones a un nivel de funcionamiento bastante bajo. Por eso se utilizan también
conexiones a nivel más alto mediante objetos URL
objetos URL
Un objeto URL representa una dirección alcanzable de una red TCP/IP. Su construcción
se suele realizar de esta forma:
try{
URL url=new URL("http://www.elpais.es");
BufferedReader in=new BufferedReader(
new InputStreamReader(url.openStream()));
String linea;
while((linea=in.readLine())!=null)
{
System.out.println(linea);
}
}
catch(MalformedURLException mue){
System.out.println("URL no válida");
}
catch(IOException ioe){
System.out.println("Error en la comunicación");
}
251
Manual de Java
programación en red
constructores
constructor uso
URL(String url) Construye un objeto URL a partir de la ruta
throws MalformedURLException dada
URL(String protocolo, String servidor, Construye un objeto URL con los parámetros
String archivo) desglosados que se observan
throws MalformedURLException
URL(String protocolo, String servidor, int Construye un objeto URL con los parámetros
puerto, String archivo) desglosados que se observan
throws MalformedURLException
métodos
constructor uso
int getDefaultPort() Devuelve el puerto asociado por defecto para
la URL del objeto
int getPort() Devuelve el puerto que utiliza realmente el
objeto URL
String getHost() Devuelve el nombre del servidor
String getQuery() Devuelve la cadena que se envía al archivo
para ser procesado por el (es lo que sigue al
signo ? de una dirección URL)
String getPath() Obtiene una cadena con la ruta hacia el
archivo desde el servidor y el nombre
completo del archivo
String getFile() Igual que la anterior, pero además añade lo
que devuelve getQuery.
String getUserInfo() Devuelve la parte con los datos del usuario de
la dirección URL
URLConnection openConnection() Obtiene un objeto de tipo URLConnection
que permite establecer una conexión
completa con el servidor (véase capítulo
siguiente)
InputStream openStream() Permite establecer una corriente de entrada
para recibir el recurso
boolean sameFile(URL url2) Compara la URL del argumento con la
original y devuelve true si se refieren al
mismo archivo
String toExternalForm() Devuelve una cadena que representa al objeto
URL
objetos URI
Hay una distinción entre URL y URI. Los URI definen recursos sintácticos de Internet.
Esos recursos no tienen necesidad de poseer datos para localizar, esa es su diferencia.
De hecho una URL es una caso de URI en el que los datos son localizables (es decir una
252
© Jorge Sánchez Asenjo’ 2004
URL hace referencia a datos que existen, una URI es teórica, los datos podrían no
existir).
Por esta razón la clase URL de Java sólo usa recursos FTP o HTTP. La clase URI
permite examinar direcciones y desglosarlas en sus distintos apartados (servidor,
puerto, etc.).
Los métodos de esta clase son muy similares a los de URL (la mayoría son los
mismos). Además un método llamado toURL convierte el URI en URL.
JEditorPane
Se trata de una clase que permite mostrar páginas web de forma muy fácil. Es una clase
heredera de JTextComponent y sirve para mostrar documentos HTML (hasta versión
3.2), RTF y de texto plano.
construcción
construcción uso
JEditorPane() Crea un nuevo panel de edición
JEditorPane(String url) Crea un nuevo panel que muestra el
documento contenido en la cadena (que debe
implementar una URL)
JEditor(URL url) Crea y muestra un panel con el contenido de
la URL indicada
JEditor(String mime, String texto) Establece el editor para incluir documentos
del tipo MIME indicado y que contendrán el
texto inicial marcado
métodos
construcción uso
addHyperlinkListener( Indica qué objeto escuchará los eventos de
HyperlinkListener oyente) tipo HiperlinkEvent que esta función
creará
String getContentType() Devuelve una cadena con el tipo de contenido
que el editor es capaz de mostrar.
HiperlinkListeners[] Obtiene una lista, en forma de array, de los
getHyperlinkListeners() objetos que actualmente actúan de oyentes de
eventos Hiperlink del editor
URL getPage() Devuelve el objeto URL que está mostrando
ahora el editor
void scrollsToReference(String marca) Mueve la pantalla hasta colocarse en la
posición de la marca indicada (es el mismo
tipo de elemento que el devuelto por el
método getReference de la clase URL)
253
Manual de Java
programación en red
construcción uso
void setContentType(String tipo) Establece el tipo de contenido que el editor
visualizará. Puede valer una de estas cadenas:
text/plain. Permite mostrar texto
plano (sin formato).
text/html. Permite mostrar texto
HTML 3.2
text/rtf. Permite mostrar documentos
RTF
void setEditable(boolean siONo) Indica si el contenido del panel podrá ser
editado. Para mostrar texto HTML (y sólo
mostrar) es necesario el valor false.
void setPage(URL url) Hace que el editor muestre la página
throws IOException contenida en la URL indicada
void setPage(String url) Igual que el anterior, sólo que la URL se pasa
throws IOException en forma de cadena (menos recomendable)
void setText(String texto) Hace que el editor muestre el texto indicado
eventos Hyperlink
Son eventos asociados a esta clase (véase también eventos InputEvent, página 143).
Ocurren cuando el usaurio realiza cualquier acción sobre uno de los enlaces de la página
que muestra el editor. Es el interfaz HyperlinkListener el que implementa el método
hyperlinkUpdate encargado de manejar estos eventos.
Los eventos HyperlinkEvent son lanzados cuando el usuario realiza cualquier
operación sobre ellos. Hay que utilizar el método getEventType de la clase
HyperlinkEvent para saber que tipo de evento fue el producido. El objeto devuelto
por getEventType es un objeto de tipo HyperlinkEvent.EventType que es de una
subclase dentro de la HyperlinkEvent.
Para determinar el valor de este objeto, se ha de comparar con las constantes:
Por otro lado, el método getURL devuelve la URL del enlace usado. Este es el método
más utilizado de este tipo de eventos. Por último, getDescription devuelve el texto del
enlace.
Ejemplo de uso de eventos de enlace:
254
© Jorge Sánchez Asenjo’ 2004
objetoEditorPane.setCursor(
Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
else if (tipo== HyperlinkEvent.EventType.EXITED)
objetoEditorPane.setCursor(Cursor.getDefaultCursor());
else objetoEditorPane.setPage(e.getURL());
}
catch(IOException e){}
}
conexiones URLConnection
Los objetos URLConnection permiten establecer comunicaciones más detalladas con
los servidores. Los pasos son:
método uso
void setDoInput(boolean b) Permite que el usuario reciba datos desde la
URL si b es true (por defecto está establecido
a true)
void setDoOutput(boolean b) Permite que el usuario envíe datos si b es
true (éste no está establecido al principio)
void setIfModifiedSince(long tiempo) Sólo muestra recursos con fecha posterior a la
dada (la fecha se da en milisegundos a partir
de 1970, el método getTime de la clase Date
consigue este dato).
void setUseCaches(boolean b) Permite recuperar datos desde un caché
void Permite solicitar contraseña al usuario. Esto
setAllowUserInteraction(boolean b) lo debe realizar un programa externo, lo cuál
no tiene efecto fuera de un applet (en un
navegador, el navegador se encarga de sacar
el cuadro).
void RequestProperty(String clave, Establece un campo de cabecera
String valor)
método uso
void connect() Conecta con el recurso remoto y recupera
información de la cabecera de respuesta
255
Manual de Java
programación en red
método uso
String getHeaderFieldKey(int n) Obtiene el campo clave número n de la
cabecera de respuesta
String getHeaderField(int n) Obtiene el valor de la clave número n
int getContentLength() Recupera el tamaño del contenido
String getContentType Recupera una cadena que indica el tipo de
contenido
long getDate() Fecha del recurso
long getExpiration() Obtiene la fecha de expiración del recurso
long getLastModifier() Fecha de última modificación
método uso
InputStream openInputStream() Obtiene un flujo para recibir datos desde el
servidor (es igual que el método
openStream de la clase URL)
OutputStream openOutputStream() Abre un canal de salida hacia el servidor
Gracias a esto las conexiones son más poderosas y se permiten muchas más
operaciones.
Ejemplo:
try{
URL url=new URL("http://www.elpais.es");
URLConnection conexión=url.openConnection();
conexión.setDoOutput(true);
conexión.connect();
256
© Jorge Sánchez Asenjo’ 2004
while(s!=null){
System.out.println(s);
s=in.readLine();
}
}
catch(Exception e){
System.out.println("Fallo");
}
257
JDBC
introducción
SGBD
Una de las principales aplicaciones de cualquier lenguaje moderno es la posibilidad de
utilizar datos pertenecientes a un sistema de base de datos. La dificultad del manejo de
archivos y las facilidades de manejo de datos que ofrecen los sistemas gestores de base
de datos (SGBDs) son los causantes de esta necesidad.
En el mercado hay gran cantidad de bases de datos y cada una de ellas se maneja de
un modo diferente. Esto está en contra del planteamiento fundamental de Java que
intenta que la programación sea independiente de la plataforma.
Hoy en día hay que tener en cuenta que la inmensa mayoría de los SGBD
administran bases de datos relacionales. Éstas son bases de datos que permiten
organizar los datos en tablas que después se relacionan mediante campos clave y que
trabajan con el lenguaje estándar conocido como SQL.
Cada tabla es una serie de filas y columnas, en la que cada fila es un registro y cada
columna un campo. Cada campo representa un dato de los elementos almacenados en
la tabla (nombre, DNI,...). Cada registro representa un elemento de la tabla (la señora
Eva Jiménez , el señor Andrés Gutiérrez,...). No puede aparecer dos veces el mismo
registro, por lo que uno o más campos forman lo que se conoce como clave principal.
La clave principal no se puede repetir en dos registros y permite que los datos se
relacionen.
En cualquier caso en este manual no se pretende revisar cómo funcionan las bases
de datos relacionales, sólo se explica cómo acceder desde Java a este tipo de bases de
datos.
La idea de Sun era desarrollar una sola API (application programming interfaces,
interfaz de programación de aplicaciones) para el acceso a bases de datos, esta interfaz
se conoce como JDBC (java data base connect). Los requisitos eran:
JDBC sería una API a nivel SQL (el lenguaje SQL sería el que realizaría la
conexión con las bases de datos), independiente por tanto de la plataforma
JDBC debía ser similar al funcionamiento de las API para acceso a bases de datos
existentes (en especial a la ya entonces famosa ODBC de Microsoft)
ODBC
Es imposible en este apartado no dedicar un pequeño espacio a ODBC (open data base
connectivity). Se trata del interfaz diseñado por Microsoft como estándar para el
manejo de datos de diversas bases de datos.
Esta API de bases de datos se ha convertido en la más popular. Su éxito se basa en la
facilidad de instalación y configuración en Windows y en que casi todos los gestores de
bases de datos la utilizan. Pero tiene varios inconvenientes:
259
Manual de Java
JDBC
Está creada en C, con los problemas que supone eso para la programación en
otros lenguajes (punteros void por ejemplo). Esto, en definitiva, supone que si no
se utiliza C se han de diseñar librerías intermedias de acceso a ODBC; con lo que
se multiplican las capas dificultando la programación.
Es compleja su programación.
No obstante, debido a la popularidad de ODBC, existen puentes ODBC para JDBC que
permiten comunicar bases de datos con controladores ODBC con aplicaciones
programadas para JDBC.
estructura JDBC
En el diagrama siguiente se puede apreciar como la idea es que las aplicaciones sólo se
tengan que comunicar con el interfaz JDBC. Éste es el encargada de comunicarse con
los sistemas de base de datos.
aplicación aplicación
JDBC
controladores
Una vez instalado, configurado y puesto en funcionamiento nuestro sistema gestor de
base de datos favorito, si queremos que las bases de datos creadas por él sean accesibles
desde los programas Java, necesitamos el controlador JDBC de ese sistema.
Hay cuatro tipos de controladores:
260
© Jorge Sánchez Asenjo’ 2004
Cliente Servidor
Puente
JDBC-ODBC
Controlador
ODBC
Librería del
vendedor SGBD
Cliente Servidor
Controlador JDBC
con parte nativa
Librería del
vendedor SGBD
261
Manual de Java
JDBC
Cliente Servidor
JDBC
puro Java
Tipo 4. Paquetes de Java puro que traducen peticiones JDBC a protocolo de base
de datos específico. No requieren intermediarios entre el software JDBC y la base
de datos
Cliente Servidor
JDBC
puro Java SGBD
Normalmente las distribuciones JDBC que suministran los fabricantes son de tipo 3 o 4.
para adquirir estos controladores es necesario ponerse en contacto con el fabricante o
dirigirse a su página web y después descargarlo. Las instrucciones de instalación las da
el fabricante, pero en caso de ser controladores de tipo 3 o 4 habrá que instalar los
paquetes del API JDBC en la ruta Classpath para que sean accesibles por los
compiladores Java.
Para saber si existe controlador JDBC para nuestra base de datos de trabajo, se
puede comprobar en la dirección:
http://servlet.java.sun.com/products/jdbc/drivers/index.html
conexión
Para conseguir conectar una base de datos con una aplicación, nuestra aplicación
requiere el URL de la base de datos y las propiedades que establezca nuestro
controlador JDBC. Las clases necesarias para usar JDBC están en el paquete java.sql.
El primer paso es instalar el controlador (driver) de la base de datos. Hay varias
posibilidades una es colocar el controlador en el atributo jdbcdrivers de la máquina
virtual al ejecutar el programa:
java -Djdbc.drivers=com.mysql.jdbc.Driver
262
© Jorge Sánchez Asenjo’ 2004
Pero el método que más se usa es lanzar el controlador en la propia aplicación mediante
el método estático forName de la clase Class. Para evitar problemas con la máquina
virtual se utiliza newInstance. Es decir, el formato es:
Class.forName(rutaDelDriver).newInstance();
La ruta del driver tiene que venir en las instrucciones del fabricante. Por ejemplo en el
caso del controlador MySQL el formato es:
Class.forName("com.mysql.jdbc.Driver").newInstance();
Esa instrucción puede dar lugar a las excepciones ClassNotFoundException (si no se
encontró la clase en el drivr JDBC), InstantiationException (si no se puede crear el
driver para la base de datos) e IllegalAccessException (si el acceso a la base de datos
no fue correcto). Se suelen capturar todas ellas con una Exception genérica.
Una vez que el controlador se ha registrado, entonces se abre la URL a la base de
datos. cuyo formato suele ser:
jdbc:sgbd://servidor/basedatos:puerto
jdbc:sgbd://localhost/prueba:3306
Connection con=DriverManager.getConnection(
"jdbc:mysql://localhost/almacen:3306","root","mimono");
Statement st=con.createStatement();
//con es un objeto Connection
executeUpdate
Este es un método Statement que permite ejecutar instrucciones SQL de tipo
UPDATE, INSERT o DELETE y también CREATE TABLE , DROP TABLE y otros de
definición de tablas. Devuelve un entero que indica el número de filas implicadas.
263
Manual de Java
JDBC
Ejemplo:
try{
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection con=DriverManager.getConnection(
"jdbc:mysql://localhost/personas:3306","root","");
Statement st=con.createStatement();
System.out.println(st.executeUpdate("UPDATE clientes SET”+
“sexo='V' WHERE sexo='H'"));
}
catch (SQLException e){
System.out.println(e.getMessage());
}
excuteQuery
Este método permite ejecutar una consulta SELECT. Este tipo de consultas devuelven
una tabla, que en Java se representa con objetos de clase ResultSet. El método next
de esta clase permite avanzar de fila, mientras que hay varios métodos get que permiten
obtener el valor de una columna. En el caso típico, el recorrido por una consulta se hace:
try{
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection con=DriverManager.getConnection(
"jdbc:mysql://localhost/personas:3306","root","");
Statement st=con.createStatement();
ResultSet rs=st.executeQuery(“SELECT * FROM empleados”);
while(rs.next()){
System.out.println(rs.getString(“Nombre”)+
rs.getInt(“Edad”));
}
}
catch (SQLException e){
do {e.printStackTrace
} while (e.getNextException());
}
264
© Jorge Sánchez Asenjo’ 2004
wasNull
Al leer un determinado campo con alguna función get puede ocurrir que el valor leído
sea nulo. En ese caso se puede comprobar mediante la función booleana wasNull. Esa
función devuelve true si la última función get obtuvo un valor nulo de la base de datos.
Ejemplo:
int n=rs.getInt("Valor");
if (rs.wasNull()){
System.out.println("Se leyó un valor nulo");
}
SQLException
En el paquete java.sql se encuentra la clase SQLException que captura las
excepciones ocurridas en el manejo de la base de datos. Su uso no difiere del resto de
excepciones, pero incorpora nuevos métodos interesantes:
getMessage. El método típico de excepciones, salvo que éste recoge el texto que
envía el controlador JDBC según lo informado por el gestor de bases de datos, lo
que le hace muy efectivo.
265
Manual de Java
JDBC
Ejemplo:
try{
//instrucciones de manejo de la base de datos
}
catch(SQLException sqle){
while(e!=null){
System.err.println("Estado: "+e.getSQLState());
System.err.println("Código: "+e.getErrorCode());
System.err.println("Mensaje: "+e.getMessage());
e.getNextException();
}
}
SQLWarning
Otras veces, ocurre que la base de datos provoca excepciones, sino advertencias
(warnings). Los objetos que las provocan (sobre todo los ResultSet) las van
almacenando en objetos de tipo SQLWarning. Para ver los errores basta con llamar
repetidamente al método getSQLWarning. En cada llamada obtendremos un nuevo
objeto SQLWarning hasta que, finalmente no haya más (devolverá entonces el valor
null).
Los métodos que permiten observar el contenido de la advertencia son los mismos
que los de la clase SQLException, ya que esta clase es heredera suya. Añade el método
getNextWarning que funciona de forma similar a getNextException, pero que en
esta clase no se suele utilizar ya que las sucesivas llamadas al método
getSQLWarnings provocan el mismo resultado.
El método clearWarnings de la clase ResultSet permite borrar las advertencias
almacenadas hasta ese momento.
266
© Jorge Sánchez Asenjo’ 2004
Para la concurrencia:
método uso
boolean next() Avanza el puntero de registros del conjunto
de resultados al siguiente registro. Devuelve
true si existe registro siguiente.
boolean previous() Coloca el puntero de registros en el registro
anterior si lo hay, si no lo hay devuelve
false
boolean absolute(int registro) Coloca el puntero de registros en la fila
indicada. Si esa fila no existe, devuelve
false. Si el número de fila se indica con un
número negativo, la fila se cuenta desde el
final.
267
Manual de Java
JDBC
método uso
boolean relative(int fila) Coloca el puntero de registros en la fila
indicada a partir de la posición actual del
puntero. Si esa fila no existe, devuelve
false.
El número de fila se puede indicar de forma
negativa y en ese caso el puntero se mueve
hacia el primer registro (si es positivo se
mueve hacia el final).
boolean first() Coloca el puntero en el primer registro. Si
no hay primer registro, devuelve false
boolean last() Coloca el puntero en el último registro. Si no
hay último registro, devuelve false
void beforeFirst() Coloca el puntero delante del primer
registro. El método next se movería al
primer registro si se utiliza tras esta orden.
void afterLast() Coloca el puntero detrás del último registro.
El método previous se movería al último
registro si se utiliza tras esta orden.
boolean isFirst() Devuelve true si el puntero está situado en
el primer registro.
boolean isLast() Devuelve true si el puntero está situado en
el último registro.
boolean isBeforeFirst() Devuelve true si el puntero está situado
delante del primer registro.
boolean isAfterLast() Devuelve true si el puntero está situado
detrás del último registro.
int getRow() Obtiene el número de registro actual
modificación de datos
Los conjuntos de resultados se pueden utilizar también para modificar los datos
obtenidos por la consulta SELECT (siempre y cuando sea posible). Para ello se necesitan
utilizar los métodos update de la clase ResultSet que permiten modificar el contenido
de un campo en la posición actual del puntero de registros.
Se trata de un conjunto de métodos que comienzan con la palabra update seguida
del tipo de datos Java del campo y un segundo parámetro que indica el nuevo valor para
el campo. Ejemplo:
268
© Jorge Sánchez Asenjo’ 2004
El método updateRow es el que permite actualizar la base de datos con los nuevos
cambios. Se debe utilizar cuando estamos seguros de que los cambios son los correctos.
Si queremos anular los cambios se debe utilizar el método cancelRowUpdates
adición de datos
Para añadir un nuevo registro (una nueva fila) en el conjunto de resultados obtenido.
Hay que emplear los métodos anteriores de modificación de datos (métodos update)
sobre una fila especial conocida como fila de inserción de registros.
Para ello los pasos son:
2> Actualizar los datos de los campos de ese nuevo registros usando los métodos
update (updateString, updateInt, updateBigDecimal,...).
3> Añadir el registro en la base de datos con insertRow (se puede cancelar con
cancelRowUpdates)
borrar registros
Se puede borrar el registro actual del conjunto de resultados utilizando el método
deleteRow.
actualizar registro
El método refreshRow del ResultSet actualiza el valor del registro actual, según lo
que valga ahora en la base de datos. Se usa por si acaso se ha modificado el valor de los
datos desde otro cliente de la base de datos.
metadatos
Hay casos en los que se requiere conocer la estructura de una base de datos (nombre y
diseño de las tablas, tipos de los campos, etc.). Los datos que describen la estructura de
las bases de datos es lo que se conoce como metadatos.
Los metadatos se obtienen utilizando el método getMetaData de la clase
Connection., por lo que es el objeto de la conexión el que permite obtener estos
metadatos. El resultado de este método es un objeto de clase DatabaseMetaData.
269
Manual de Java
JDBC
270
© Jorge Sánchez Asenjo’ 2004
271
Manual de Java
JDBC
272
© Jorge Sánchez Asenjo’ 2004
273
Manual de Java
JDBC
274
© Jorge Sánchez Asenjo’ 2004
En este listado sólo se han señalado las instrucciones más utilizadas, hay el doble de
métodos en realidad con respecto a los aquí señalados.
275
Manual de Java
JDBC
276
Servlets y JSP
HTML Na
veg
ad
or
com
JavaScript Nave pa
tib
gado le
r com
patib
ActiveX le
Navegador com
patible Cliente
Flash Plugin Flash
ugin Java
Pl
Applet
Ilustración 35,Algunas tecnologías del lado del cliente y software necesario para ellas
Para evitar estos problemas se idearon técnicas de creación de aplicaciones para la web
del lado del servidor. En las que la interpretación se realiza en el propio servidor y no en
el cliente. Veremos a continuación las principales.
277
Manual de Java
Servlets y JSP
CGI
Common Gateway Interface, o interfaz de pasarela común (CGI) es la tecnología de
servidor más veterana. Apareció debido a las limitaciones de HTML para crear
verdaderas aplicaciones de red.
CGI define una serie de características que permiten comunicar a una página con
una aplicación residente en un servidor. La aplicación puede estar escrita casi en
cualquier lenguaje (aunque el más utilizado es el lenguaje Perl) lo único que tiene
conseguir es que su salida y entrada ha de ser pensada para comunicarse con la web de
forma que el usuario no necesite ningún software adicional (los datos de salida suelen
prepararse en formato HTML).
El servidor en el que reside la aplicación CGI debe tener implementado un
compilador compatible con el lenguaje utilizado para escribir la aplicación.
ASP y ASP.NET
ASP parte de simplificar la idea de la tecnología de servidor. Se trata de páginas HTML
que poseen etiquetas especiales (marcadas con los símbolos <% y %>) que marcan
instrucciones (en diversos lenguajes, sobre todo VBScript) que debe ejecutar el servidor.
El servidor interpreta esas instrucciones y obtiene una página HTML (que es la que
llega al cliente) resultado del código ASP. Es una tecnología muy exitosa gracias a la
cantidad de programadores Visual Basic.
El problema es que está pensada únicamente para servidores web IIS (Internet
Information Server los servidores web de Microsoft).
.NET es la nueva implementación de la tecnología de servidores de Microsoft que
incorpora diversos lenguajes bajo una interfaz común para crear aplicaciones web en los
servidores IIS. Se pueden utilizar varios tipos de lenguajes (especialmente C# y
VBScript) combinados en páginas ASP.NET con directrices de servidor y posibilidad de
conexión a bases de datos utilizando ADO (plataforma de conexión abierta de Microsoft
para acceder a bases de datos, sucesora de ODBC).
ColdFussion
Tecnología soportada por la empresa Macromedia que parte de la misma idea que ASP,
pero en lugar de usar etiquetas <%, utiliza etiquetas especiales que son traducidas por el
servidor. No posee lenguaje de script lo que hace más fácil su aprendizaje, ya que sólo
añade a las etiquetas normales de HTML una serie de etiquetas entendibles por los
servidores de aplicaciones ColdFussion.
La interpretación de los códigos ColdFussion proporciona de nuevo una página
HTML. Su desventaja es que no es muy estándar y que es difícil la creación de
aplicaciones muy complejas.
PHP
Es una tecnología similar a ASP. Se trata de una página HTML que posee etiquetas
especiales; en este caso son las etiquetas <?, que encierran comandos para el servidor
escritos en un lenguaje script especial (es un lenguaje con muchas similitudes con Perl).
La diferencia con ASP es que es una plataforma de código abierto, compatible con
Apache y que posee soporte para muchos de los gestores de base de datos más
populares (Oracle, MySQL, etc.).
278
© Jorge Sánchez Asenjo’ 2004
HTML
Servidor
Etiquetas
de servidor Cliente
HTML
Servlets y JSP
Son las tecnologías planteadas por Java para crear aplicaciones cuyas tecnologías
residan en el lado del servidor. JSP es similar a ASP y PHP. Los servlets son
equivalentes a las applets, pero en el lado del servidor. Ambas tecnologías se estudian
en este capítulo. Las dos forman parte de lo que hoy en día se conoce como J2EE (Java
2 Enterprise Edition). Una aplicación JSP o Servlet se conoce como aplicación web
GET pagina
motor servlets
servlets JSP
servicios J2EE
SGBD otros
279
Manual de Java
Servlets y JSP
J2EE
Se trata de una plataforma completa para construir aplicaciones completas desde la web
basadas en el lenguaje Java. Se trata de una serie de tecnologías que permiten escribir
aplicaciones en el lado del servidor para proporcionar servicios desde redes TCP/IP.
Lógicamente todas estas técnicas se basan en el lenguaje Java.
Mantienen el paradigma Java de la portabilidad incluso en el caso de cambiar el
sistema operativo del servidor. Sus APIs están en el paquete javax. Las fundamentales
son:
Servlets
JSP
Para ello hace falta ejecutar la aplicación J2EE en servidores web compatibles que
posean un servidor de aplicaciones compatibles, por ejemplo:
WebLogic http://www.bea.com
JBoss: http://www.jboss.org
280
© Jorge Sánchez Asenjo’ 2004
Un directorio raíz del que parten todas las carpetas de la aplicación, sólo este
directorio es visible para los navegadores
La carpeta lib en la que se almacenan los archivos jar para librerías de clases que
utiliza la aplicación (incluidos los drivers JDBC en forma de archivo jar).
Carpeta raíz
WEB-INF
archivos .class de la aplicación
classes incluidos los servlets
(nunca los .java)
http
El protocolo de transferencia de hipertexto es el encargado de transmitir páginas web
por las redes TCP/IP. Toda la programación de aplicaciones se basa en el uso de este
protocolo. Mediante http el proceso de petición y respuesta de datos sería:
281
Manual de Java
Servlets y JSP
Los servidores web no recuerdan ningún dato de las peticiones anteriores, cada petición
es independiente. Esto supone un serio problema al programar aplicaciones mediante
este protocolo. Para evitar este problema se puede hacer que el servidor nos de un
número de sesión que el cliente almacenará y enviará junto a las siguientes peticiones
para que el servidor recuerde.
peticiones http
Las peticiones mediante http pueden utilizar los siguientes comandos:
HEAD. Idéntico al anterior sólo que obtiene sólo las cabeceras del recurso.
POST. Petición mediante la cual se hace que el servidor acepte datos desde el
cliente.
cabeceras de petición
Se ponen tras la línea de petición. Son líneas que incluyen una clave y su valor
(separado por dos puntos). Mediante estas cabeceras el cliente indica sus capacidades e
informaciones adicionales (navegador, idioma, tipo de contenido...).
ejemplo
Tras conectar por el puerto 80 con el servidor www.terra.es, la siguiente petición GET:
282
© Jorge Sánchez Asenjo’ 2004
GET / HTTP/1.0
HTTP/1.0 200 OK
Age: 14
Date: Sun, 06 Jun 2004 23:34:55 GMT
Content-Length: 38761
Content-Type: text/html
Cache-Control: max-age=15
Server: Netscape-Enterprise/4.1
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
<base href="http://www.terra.es/">
....
Servlets
Aparecieron en 1997 como respuesta a las aplicaciones CGI. Sus ventajas son:
Mejora del rendimiento. Con las CGI lo que ocurría era que había que lanzar
la aplicación con cada nueva petición de servicio. Las Servlets usan la misma
aplicación y para cada petición lanzan un nuevo hilo (al estilo de los Sockets).
Control de sesiones. Se pueden almacenar datos sobre las sesiones del usuario
(una de las taras más importantes de http).
creación de Servlets
Los Servlets se deben compilar en la carpeta classes de la aplicación web. Se trata de
una clase normal pero que deriva de javax.servlet.Servlet. Hay una clase llamada
javax.servlet.GenericServlets que crea Servlets genéricos, y una clase llamada
javax.servlet.http.HttpServlet que es la encarga de crear servlets accesibles con el
protocolo http. Esos son los que nos interesan.
ciclo de vida
Un servlet genérico posee el siguiente ciclo de vida:
283
Manual de Java
Servlets y JSP
Pero para servidores web se utiliza la clase HttpServlet cuyo ciclo difiere un poco ya
que no se utiliza el método service (lo sustituyen los métodos doGet o doPost). De
hecho el proceso para el método service es :
2> Se llama al nuevo método service que posee dos parámetros. Uno es de tipo
HttpServletRequest (sirve para los requerimientos), el otro es un objeto de
tipo HttpServletResponse (sirve para las respuestas).
3> El método anterior llama a doGet(), doPost() (que recibirán los mismos
parámetros) u otro método programado, dependiendo del tipo de llamada
http realizada por el cliente (si es GET se llama a doGet, si es POST se llama a
doPost, etc.)
284
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException,
IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<title>Escribiendo html</title>");
...
Se implementan sólo los métodos init y doGet. El método doGet (cuya definición tiene
que coincidir con la del listado) es llamado si se ha requerido el servlet mediante una
petición http de tipo get
285
Manual de Java
Servlets y JSP
<web-app>
<servlet>
<servlet-name>Servlet_HolaMundoServlet2</servlet-name>
<display-name>Servlet HolaMundoServlet2</display-name>
<description>Default configuration created for
servlet.</description>
<servlet-class>HolaMundo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet_HolaMundoServlet2</servlet-name>
<url-pattern>/servlet/HolaMundoServlet2</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>
index.jsp
</welcome-file>
<welcome-file>
index.html
</welcome-file>
<welcome-file>
index.htm
</welcome-file>
</welcome-file-list>
</web-app>
286
© Jorge Sánchez Asenjo’ 2004
El primer método sólo vale para peticiones GET. Consiste en colocar tras la ruta al
servlet el símbolo ? seguido del nombre del primer parámetro, el signo '=' y el valor del
parámetro (se entiende que siempre es texto). Si hay más parámetros, los parámetros se
separan con el símbolo '&'. Ejemplo:
http://www.google.es/search?q=palencia&ie=UTF-8&hl=es&meta=
<HTML>
...
<FORM METHOD=GET ACTION=“/servlet/ServletSaludo”>
Escribe tu nombre
<INPUT TYPE=TEXT NAME=nombre SIZE=20>
<INPUT TYPE=SUBMIT VALUE=“Enviar”>
</FORM>
...
</HTML>
El método (METHOD) puede ser GET o POST, basta con indicarlo. En este código
HTML, se coloca un botón de tipo submit (enviar) y un cuadro de texto en el que el
usuario rellena sus datos. El atributo name indica el nombre que se le dará al
parámetro que se enviará al servlet. El parámetro tendrá como valor, lo que el usuario
introduzca.
El parámetro es recogido desde el Servlet por el método getParameter del objeto
HttpServletResponse (se le suele llamar response a secas) de los métodos doGet o
doPost:
287
Manual de Java
Servlets y JSP
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
implementación de SingleThreadModel
En las conexiones a servlet se sigue un modelo de múltiples threads en el que se
compartir las variables y objetos globales al servlet.
Esto significa, por ejemplo, que si se hace una conexión a una base de datos, el
objeto de conexión es común a varios clientes, lo que puede provocar conflictos.
La solución es hacer que los métodos de acceso a datos compartidos sean
sincronizados (synchronized) o implementar la interfaz SingleThreadModel. Esta
interfaz no obliga a escribir ningún método, pero hace que el servlet atienda a un cliente
cada vez (sincroniza el método service).
288
© Jorge Sánchez Asenjo’ 2004
métodos de HttpServletRequest
El objeto HttpServletRequest tiene métodos interesantes que permiten obtener
información sobre:
Manejo de cookies
Identificación de sesión
método uso
String getAuthType() Devuelve el nombre del sistema de
autentificación del servidor (si no usa
ninguno, devuelve null)
Cookie[] getCookies() Obtiene un array con todos los objetos
Cookie que ha enviado el cliente
String getContentLength () Devuelve el tamaño en bytes del contenido
solicitado
String getContentType () Devuelve la cadena Content Type de la
petición. El resultado es el tipo MIME que
indica la petición (por ejemplo text/html)
String getContextPath() Devuelve la porción de la URL referida a la
URL del servlet
Enumeration getHeaderNames() Devuelve una enumeración de todas las
cabeceras http presentes
Enumeration getHeader(String nombre) Devuelve el contenido de la cabecera http
cuyo nombre es el indicado
String getMethod() Devuelve el método de llamada a la
aplicación que utilizó el cliente (GET, PUT o
POST por ejemplo).
String getParameter(String parámetro) Obtiene el contenido del parámetro cuyo
nombre se pasa entre comillas
Enumeration getParameterNames() Obtiene una lista con los nombres de los
parámetros
String[] getParameterValues(String Devuelve un array de cadenas con los
nombreParámetro) valores correspondientes al parámetro
indicado o null si no tenía parámetro
Map getParameterMap() Devuelve un objeto map con los valores y
parámetros de la petición.
String getPathInfo() Obtiene la porción de ruta de la URL que
sigue al nombre del servlet
289
Manual de Java
Servlets y JSP
método uso
String getQueryString() Obtiene la cadena de la URL que sigue al
carácter “?”
String getRequestURL() Obtiene la URL completa empleada para la
petición de página (incluida la zona ?)
String getRemoteUser() Obtiene el nombre de usuario del cliente, si
hay posibilidad de autentificarle.
HttpSession getSession(boolean crear) Devuelve el objeto actual HttpSession si
no hay, devuelve null (se creará uno nuevo
si crear vale true).
boolean Indica si el identificador de sesión se obtuvo
isRequestedSessionIdFromCookie() de una Cookie
boolean isRequestedSessionIdValid() true si el indicador de sesión es válido
boolean isUserInRole(String rol) Devuelve true si el usuario está asociado al
rol indicado.
métodos de HttpServletResponse
Permite enviar información al cliente. En los servlets http, esa información se pasa en
formato HTML. Para ello se usa el método setContentType con valor text/html
(aunque podría poseer otro valor).
método uso
void addCookie(Cookie cookie) Añade una cabecera Set-Cookie para la
cookie indicada.
void addHeader(String nombre, String Establece el valor indicado para la cabecera
valor) http nombre.
void addIntHeader(String nombre, int Establece el valor indicado para la cabecera
valor) http nombre. El valor se coloca con valor int
boolean containsHeader(String Indica si la salida ya posee la cabecera
nombre) indicada
String encodeRedirectURL(String url) Soporta el seguimiento de sesiones
utilizando el identificador de sesión como
parámetro de URL que se enviará mediante
sendRedirect(). Si el cliente soporta
cookies, esto no es necesario.
String getContentType() Obtiene la cadena MIME con el tipo de
contenido de la respuesta
void sendError(int estado) Establece el código de estado http en el valor
throws IOException indicado.
void sendError(int estado, String msg) Lo mismo, pero además fija el mensaje de
throws IOException estado especificado.
void sendRedirect(String location) Coloca el estado http en 302 (movido
throws IOException provisionalmente) y se lanza al navegador a
la nueva localización.
290
© Jorge Sánchez Asenjo’ 2004
método uso
void setCharacterSet(String tipoMIME) Indica que codificación se envía en las
respuestas. La codificación se debe indicar
en formato estándar (el definido por la
IANA), por ejemplo:
text/html;charset=UTF-8
Para los códigos de respuesta utilizados por varias funciones (setStatus y sendError por
ejemplo), esta interfaz define una serie de constantes estática que empiezan por la
palabra SC. El valor de las constantes se corresponde con el valor estándar de respuesta.
Por ejemplo la constante HttpServletResponse.SC_NOT_FOUND vale 404
(código de error de "página no encontrada").
método uso
Object getAttribute(String nombre) Obtiene el atributo del servidor de
aplicaciones cuyo nombre sea el indicado. Se
necesita conocer el servidor de aplicaciones
para saber qué atributos posee.
Enumeration getAttributeNames() Obtiene una enumeración con todos los
atributos del contexto de servlet
291
Manual de Java
Servlets y JSP
método uso
void setAttribute(String nombre, Object Establece el atributo de servidor con
valor) nombre indicado dándole un determinado
valor.
ServletContext getContext(String ruta) Obtiene el contexto de servlet del servlet
cuya ruta se indica. La ruta debe empezar
con el símbolo "/" (es decir, debe de ser
absoluta) y el servlet debe estar en el mismo
servidor.
int getMajorVersion() Devuelve el número mayor de la versión de
Servlet que el servidor de aplicaciones es
capaz de ejecutar.
int getMinorVersion() Devuelve el número menor de la versión de
Servlet que el servidor de aplicaciones es
capaz de ejecutar.
String getMimeType(String archivo) Obtiene el tipo MIME del archivo cuya ruta
en el servidor se indica
String getRealPath(String ruta) Obtiene la ruta absoluta de la ruta interna al
servidor que se indica
URL getURL(String recurso) Devuelve un objeto URL correspondiente a
la ruta de recurso en el servidor indicada
String getServerInfo() Obtiene el nombre del servidor de
aplicaciones que se utiliza como motor de
Servlet
void removeAttribute(String nombre) Elimina el atributo indicado del contexto del
servlet.
sesiones http
La navegación mediante el protocolo http, dista mucho de parecerse a una
comunicación cliente—servidor típica. Cuando el cliente pide un recurso, el servidor se
lo da y punto. Los navegadores hacen nuevas peticiones para cada elemento de la página
que haya que descargar. Si el usuario hace clic se hace una petición para el enlace.
En definitiva, el servidor se olvida del cliente en cuanto resuelve su petición. Pero
esto provoca problemas cuando se desea ir almacenando información sobre el cliente
(listas de productos elegidos, datos del usuario, etc.).
Para recordar datos de usuario hay varias técnicas:
292
© Jorge Sánchez Asenjo’ 2004
utilizar los métodos getAttribute para obtener objetos asociados al usuario con esa
sesión y setAttribute para almacenar valores.
método uso
Object getAttribute(String nombre) Obtiene un atributo para la sesión actual. Si
el nombre del atributo no existe, devuelve
null
Enumeration getAttributeNames() Obtiene una enumeración con todos los
atributos de la sesión
void setAttribute(String nombre, Object Establece un atributo para la sesión con el
valor) nombre indicado, al que se asociará un
determinado valor.
void removeAttribute(String nombre) Elimina el atributo indicado de la sesión
String getID() Obtiene el identificador de la sesión
int setMaxInactiveInterval(int Establece el número de segundos que la
segundos) sesión podrá estar sin que el cliente efectúe
ninguna operación.
int getMaxInactiveInterval() Obtiene el valor actual del intervalo
comentado antes
long getLastAccessedTime() Obtiene la fecha y hora en la que el usuario
realizó su última petición. Devuelve el
número de milisegundos de esa fecha, el
formato es el mismo que el de la clase Date
void invalidate() Anula la sesión actual, eliminando todos los
objetos relacionados
boolean isNew() Con true indica que el usuario aún no ha
establecido sesión.
JSP
Se trata de una idea que puede convivir perfectamente con los servlets (de hecho
refuerza esa tecnología), pero cuyo método de trabajo es distinto.
Una página JSP es una página web normal (sólo que con extensión .jsp) a la que se
la puede añadir código java utilizando unas etiquetas especiales dentro del código de la
página. Estas etiquetas son traducidas por el servidor de aplicaciones al igual que
traduce el código de un servlet. Las etiquetas JSP comienzan por <% y terminan por %>
Es una técnica cada vez más popular ya que posee la potencia de Java (al mismo
nivel que los servlets), pero con la gracia de escribir directamente el código en una
página web, lo que facilita su diseño. Su compatibilidad es mucho mayor y su creación
es más simple.
Realmente en la práctica todo JSP se convierte en un servlet, por lo que se necesita
el mismo sistema de archivos (carpeta WEB-INF, web.xml, classes, lib, etc.). Por lo que
las páginas JSP se almacenan en la raíz de la aplicación y sus librerías comprimidas
como jar en la carpeta lib (todas las librerías deben almacenarse ahí, las del kit de Java
no porque el servidor de aplicaciones ya las incorporará).
293
Manual de Java
Servlets y JSP
1> Una página JSP comienza con el código nativo HTML-JSP. Es el código
escrito por el programador, mezcla de HTML clásico e instrucciones Java.
3> Se carga la clase para cada petición entrante, el ejemplar, que gestiona la
petición http del cliente concreto.
4> Finalmente la ejecución del servlet da lugar al código HTML que es el que
recibe el cliente.
directivas
Instrucciones dirigidas al servidor web que contiene la página indicando qué tipo de
código se ha de generar. Formato:
294
© Jorge Sánchez Asenjo’ 2004
295
Manual de Java
Servlets y JSP
include
La directiva include, al estilo de la directiva #include del lenguaje C, permite añadir a
la página código incluido en otro archivo cuyo nombre se indica. Pueden haber varias
directivas include.
<jsa:banderola>texto</jsa:banderola>
comentarios
Hay dos tipos:
Propios de JSP. Comienzan por <%-- y terminan por --%>. Sólo son visibles en
el código original JSP
Propios de HTML. Comienzan por <!-- y terminan por --> Son visibles en el
código HTML generado por el servidor
expresiones
Comienzan por <%= y terminan por %> Entre medias se coloca una expresión Java
válida que será traducida por el servidor como una instrucción out.print donde out es
el objeto de salida de texto para escribir código HTML hacia el cliente. Es decir, lo que
se coloca como expresión es directamente traducible como HTML
<%@page contentType="text/html"%>
<html>
<head><title>JSP Page</title></head>
<body>
<h1>
<%= "Hola mundo" %>
</h1>
</body>
</html>
El resultado es:
<html>
296
© Jorge Sánchez Asenjo’ 2004
<head><title>JSP Page</title></head>
<body>
<h1>
Hola mundo
</h1>
</body>
</html>
instrucciones
También llamadas scriptlets, van entre <% y %> y son sentencias puras Java. El
servidor las codifica como parte del método service del servlet resultante. Permite
escribir Java puro, utilizar objetos implícitos de JSP y variables, métodos y clases
declaradas.
declaraciones
Van entre <%! y %>y sirven para declarar variables de instancia, métodos o clases
internas. No pueden utilizar objetos implícitos.
Las variables declaradas son diferentes para cada instancia (al igual que los
métodos). En definitiva, los métodos, variables o clases de las declaraciones son
globales a todo el archivo JSP. Si se desea una variable o método común a todas las
instancias de la página (es decir, común a todas las sesiones del archivo), entonces se
pueden declarar con static. Ejemplo:
<%!
public int factorial(int n) {
int resultado=1;
for(int i=1; i<=n; i++) resultado*=n;
return resultado;
}
%>
Hay que recordar que las variables declaradas en esta zona, se consideran variables
globales a toda la clase del archivo JSP, es decir serán propiedades del Servlet que se
genera a partir del JSP.
En las declaraciones se pueden definir métodos que luego serán utilizados dentro del
JSP.
297
Manual de Java
Servlets y JSP
Ejemplo:
<%!
public double factorial(int n){
double resp=1;
while(n>1) resp*=n--;
return resp;
}
%>
objetos implícitos
Se necesitan algunos objetos predefinidos para poder realizar algunas operaciones
complejas.
request
Representa el objeto HttpServletRequest de los servlets, necesario para obtener
información. Por ejemplo request.getParameter(“nombre”) recoge este parámetro
para su uso posterior. Sus métodos son los comentados en la clase
HttpServletRequest página 289.
response
Representa el objeto HttpServletResponse de los servlets. Permite escribir datos en
la página. Sus métodos se comentaron en la página 290
pageContext
Para obtener datos sobre el contexto de la página.
session
Objeto HttpSession. Sus métodos se comentaron enla página 293
application
Objeto de contexto del servlet (ServletContext). Sus métodos son los explicados para
los Servlets en la página 291
out
Representa el flujo de salida hacia HTML. Equivalente a lo que devuelve el método
getWriter de la clase HttpServletResponse.
config
Objeto ServletConfig de esta aplicación
page
Referencia a la página JSP (es un objeto HttpServlet).
exception
Para captura de errores sólo válido en páginas de errores.
298
© Jorge Sánchez Asenjo’ 2004
3> Petición desde JavaScript tipo GET. Ésta es la menos elegante, pero
soluciona fácilmente pequeños problema. La instrucción JavaScript
location=URL permite modificar la página web actual sustituyéndola por la
página indicada por su URL. Si en esa URL se incluyen parámetros (por
ejemplo (?nombre=pepe&edad=28&nacionalidad=francia), entonces resulta
que se llamará al Servlet o JSP indicados pasando esos parámetros.
La instrucción desde un Servlet sería:
out.println("<script language='JavaScript'>"+
"location='/servlet/form?nombre=pepe&edad=28"+
"&nacionalidad=francia';</script>");
interfaz RequestDispatcher
Permite referenciar a un recurso web para recibir peticiones desde un Servlet. Permite
dos métodos:
299
Manual de Java
Servlets y JSP
método requiere una cadena con la dirección del recurso llamado. Esa dirección parte
desde el contexto raíz. Es decir si estamos en el Servlet /apli/servicios/servicio1 y
queremos obtener el servlet /aplic/servicios/servicio2 la cadena a utilizar es
"/servicios/servicio2".
Finalmente para poder pasar valores de un Servlet (o JSP) a otro, se puede utilizar el
método setAttribute. Este método utiliza dos parámetros: el primero es el nombre del
atributo que se desea pasar (es una cadena), el segundo es un objeto que permite
colocar valor al atributo.
El Servlet (o JSP) destinatario de la petición puede obtener el valor del atributo
utilizando el método getAttribute, que tiene como único parámetro el nombre del
atributo que se desea obtener.
Los nombres de atributos cumplen las mismas reglas que los paquetes (por ejemplo
un nombre de atributo sería com.miEmpresa.nombre). Los nombres que
comienzan por java. o javax. y sun. ya están cogidos.
1> Desde el Servlet o página JSP, utilizar el objeto request para invocar al
método getRequestDispatcher. A este método se le pasa la ruta del
Servlet/JSP que será invocado
establecer sesiones
Se dice que el protocolo http es un protocolo sin estado, ya que las llamadas a este
protocolo son independientes unas de otras. Los protocolos con estado permiten que las
llamadas sean dependientes unas de otras. Esto es fundamental para realizar multitud
de acciones que sería imposibles si el estado es independiente.
Temas como venta en línea, transacciones comerciales o páginas que se adapten a
los criterios del usuario, no se podrían realizar sin conseguir una dependencia entre las
llamadas. Por ello necesitamos establecer una sesión. Una sesión es una serie de
solicitudes que forman una tarea completa de trabajo en la que se distinga a un cliente
de otro.
El estado se consigue haciendo que el servidor recuerde información relacionada con
las sesiones anteriores. Como http cierra la conexión tras resolver la solicitud, no parece
posible realizar estas operaciones. Para resolver este dilema, en el API de los Servlets (y
por tanto en los JSP) se han incluido herramientas que solucionan el problema. Estas
son:
300
© Jorge Sánchez Asenjo’ 2004
SSL, Secure Socket Layer. Utiliza una tecnología de cifrado sobre TCP/IP
(especialmente bajo http). El protocolo HTTPS se define con esta tecnología. Ser
generan claves cifradas entre cliente y servidor llamadas claves de sesión.
1> La sesión se obtiene mediante el método getSession del objeto request (en
JSP ya hay creado un objeto llamado session). Realmente lo que ocurre es
que se creará
301
Manual de Java
Servlets y JSP
<web-app>
...
<session-config>
<session-timeout>30</session-timeout>
</session-config>
...
</web-app>
Hay que tener en cuenta que normalmente esta gestión se realiza por cookies.
Pero esto causa un problema: qué ocurre si el usuario desactiva las cookies. En ese caso
se podría detectar y avisar (habría que comprobar si se graban los datos o no), o mejor
utilizar paso de datos sin usar cookies.
Esto se consigue utilizando el método uso de sesiones por URL. Para ello al llamar a
las direcciones, en lugar de poner su URL sin más, hay que utilizar el método
encodeURL del objeto response. Ese método recibe una dirección URL y devuelve la
URL incluyendo la sesión. Ejemplo:
out.println("<A HREF="/app1/prueba.jsp">.....");
out.println("<A HREF="+response.encodeURL(/app1/prueba,jsp"+
">.....");
Este código hace lo mismo pero incluye en la URL la sesión, por lo que se permitirá usar
los datos de la sesión haya cookies o no.
eventos de Sesión
Las sesiones pueden producir eventos que pueden ser escuchados mediante clases que
implementen alguna de estas interfaces.
interfaz HttpSessionListener
Sirve para escuchar eventos de sesión. Ocurren estos eventos en la creación o en la
destrucción (o invalidación) de la sesión. Los métodos que define son:
302
© Jorge Sánchez Asenjo’ 2004
Cualquier objeto podría implementar esta interfaz, pero se necesita indicar este objeto
en el archivo web.xml
<web-app>
...
<listener>
<listener-class>CLaseEscuchadora</listener-class>
</listener>
...
</web-app>
interfaz HttpSessionActivationListener
Sirve para escuchar eventos de activación de la sesión. Los métodos que define son:
clase HttpSessionEvent
Representa los eventos de sesión. Deriva de la clase java.util.EventObject a la que
añade el método getSession que obtiene el objeto HttpSession que lanzó el evento.
interfaz HttpSessionBindingListener
Sirve para escuchar eventos de adición o eliminación de atributos en la sesión. Métodos:
Son los objetos que se utilizan como atributos de la sesión los que pueden implementar
esta interfaz.
interfaz HttpSessionAttributeListener
Similar a la anterior. Permite escuchar cuándo el estado de la sesión cambia.
303
Manual de Java
Servlets y JSP
clase HttpSessionBindingEvent
Clase que define los objetos capturados por las dos interfaces anteriores. Deriva de
java.util.EventObject y añade estos métodos:
304
JavaBeans
introducción
componentes
Uno de los paradigmas de la programación es aprovechar el código que ya está
creado. Inicialmente la primera idea fue crear módulos y funciones que luego se
aprovechan en otras aplicaciones. A esto se le llama reutilizar código. Empezó con un
mero copiar y pegar, para mejorar haciendo que las funciones se agruparan en librerías
que se invocaban desde el código.
Se mejoró aún más la idea con la llegada de la programación orientada a objetos. Las
clases mediante las que se definen objetos encapsulan métodos y propiedades, y esto
posibilita diseñar aplicaciones más fácilmente. Las clases se pueden utilizar en distintas
aplicaciones. Las clases también se agrupan en librerías (o paquetes como en el caso de
Java).
Pero desde hace años, el aprovechamiento del código ha sufrido una notable mejoría
con la llegada de los llamados lenguajes visuales (como Visual Basic y Delphi). En estos
lenguajes, se pueden colocar objetos en el código simplemente pintándolos en un área
visual. En estos lenguajes se diseña el formulario de la aplicación y se arrastran los
distintos componentes desde un cuadro de herramientas. Se modifican las propiedades
de los componentes y finalmente se añade el código necesario (que será muy poco).
Como desventaja, estos lenguajes se abstraen tanto del código que no son adecuados
para resolver algunos problemas (que Java sí es perfectamente capaz de resolver).
Realmente es una solución ideal para la programación de elementos visuales.
A este tipo de elementos que funcionan como bloques de construcción de
aplicaciones, es a los que se les llama componentes. De este modo una aplicación no
es más que un conjunto de componentes funcionando conjuntamente.
Los componentes representan desde cosas tan sencillas como un botón a cosas más
complicadas como un procesador de textos. La idea además es incluso hacer que este
modelo funcione para cualquier plataforma de ordenador y para cualquier lenguaje.
Esto último no se ha llegado a conseguir del todo.
Tecnologías como CORBA otorgan un modelo de componentes independiente del
lenguaje gracias a una capa que hace de interfaz entre el lenguaje que accede al
componente y el servidor CORBA que proporciona el componente.
Todos los componentes deben seguir un modelo concreto a fin de que puedan
fabricarse herramientas de desarrollo que permitan el trabajo con los componentes de
manera visual.
JavaBeans™
Los JavaBeans son los componentes fabricados con la tecnología Java. El nombre se
podría traducir como grano de Java (o grano de café, ya que en Estados Unidos se llama
Java al café por el consumo que se hace del café procedente de la isla de Java), es decir
es lo que forma una aplicación Java completa.
La idea es la misma que la comentada anteriormente como referencia a los
componentes, se colocan directamente JavaBeans en una aplicación y se utilizan sin
conocer su interior. Como además se utiliza un modelo concreto, numerosas
305
Manual de Java
JavaBeans
empaquetamiento de JavaBeans
Los Java Beans se empaquetan en archivos JAR, como las Applets. En el caso de los
Java Beans, el archivo manifest se tiene que editar obligatoriamente. En dicho archivo
hay que indicar qué clases del paquete JAR son Beans. En el contenido de este archivo,
hay que añadir una línea Name indicando el nombre de la clase que implementa el
Bean (incluyendo el paquete en el que está la clase, la ruta del paquete se indica con
símbolos / en lugar del punto, por ejemplo com/miPaquete/clase.class) y una línea con
el texto Java-Bean: True.
306
© Jorge Sánchez Asenjo’ 2004
Ejemplo:
Manifest-Version 1.0
Name: CuadroTextoFijo.class
Java-Bean: True
Name: CuadroNumérico.class
Java-Bean: True
Se supone que ambas clases implementan JavaBeans. En el archivo JAR hay que
empaquetar todos los archivos necesarios.
propiedades simples.
Son propiedades que permiten su uso mediante métodos get/set (obtener/cambiar).
Las propiedades permiten cambiar su valor usando el método set correspondiente (por
ejemplo para cambiar la propiedad título se utilizaría setTítulo("Imagen principal")).
El valor de la propiedad se obtiene mediante método get (por ejemplo getTítulo()
devolvería la cadena Imagen principal).
Hay una excepción a lo comentado anteriormente; si la propiedad es booleana,
entonces para obtener su valor se usa el método is (por ejemplo isTítuloActivo()) que
lógicamente devuelve true o false dependiendo del estado de la propiedad.
307
Manual de Java
JavaBeans
propiedades indexadas.
Son propiedades que admiten una serie concreta de valores. En este caso hay cuatro
métodos de acceso. Si la propiedad es por ejemplo Notas y representa un array de
números double. Entonces tendríamos:
Propiedades dependientes.
Permiten mandar mensajes de modificación de propiedades a otros Beans cuando una
propiedad cambia, de este modo se ligan dos componentes; cuando el primero cambia,
el segundo se da cuenta y realiza una determinada acción
Por ejemplo si tenemos un Java que muestra imágenes y un componente que sirve
para rellenar rutas de archivo, podemos hacer que al cambiar la propiedad de la ruta, el
cuadro de la imagen muestre la imagen correspondiente a esa ruta. El proceso para
realizar este mecanismo es:
2> Para permitir que haya componentes que escuchen al JavaBean hay que
definir dos métodos:
void addPropertyChangeListener(PropertyChangeListener
escuchador)
void removePropertyChangeListener(PropertyChangeListener
escuchador)
El primer método sirve para añadir un escuchador. El segundo sirve para
quitar un escuchador.
308
© Jorge Sánchez Asenjo’ 2004
En el constructor se indica el objeto al que se le quiere dar soporte (lo normal es this). Y
ahora la definición de los métodos anteriores, la definición sería:
void propertyChange(PropertyChangeEvent e)
propiedades restringidas
Es la propiedad más compleja de implementar, pero admite posibilidades muy
interesantes. La idea es similar a la de las propiedades dependientes; se valoran
cambios de valor en un JavaBean. Sin embargo, en este caso se basa en que el objeto
oyente de eventos (eventos de veto) puede vetar los cambios si no se ajustan a unas
309
Manual de Java
JavaBeans
Una vez más, como definir estos métodos es muy complejo, se puede crear un objeto
VetoableChangeSupport para conseguir un soporte sencillo a fin de definir los
métodos anteriores:
Los componentes que deriven de JComponent no necesitan este código ya que todos
estos métodos están definidos.
El lanzamiento del evento se realiza mediante el método fireVetoableChange (de
la clase JComponent o de la clase VetoableChangeSupport), que requiere tres
parámetros: el nombre de la propiedad que cambia (en forma de String), el valor
antiguo de la propiedad y el valor nuevo de la propiedad. Los valores antiguo y nuevo
deben ser de tipo Object lo que significa que si la propiedad es de tipo básica habrá que
usar un clase evolvente (Integer, Boolean,...). fireVetoableChange debe ser llamado
cada vez que se cambia la propiedad.
La escucha de este tipo de eventos la realiza cualquier objeto que pertenezca a clases
que implementen la interfaz VetoableChangeListener; esta interfaz obliga a definir
el método siguiente:
310
© Jorge Sánchez Asenjo’ 2004
Se puede observar que este método recibe un objeto del tipo PropertyChangeEvent
(no hay eventos VetoableChangeEvent) y que lanza excepciones del tipo
PropertyVetoException. De hecho la clave del funcionamiento de este tipo de
propiedades la tiene esta excepción. El método vetoableChange tiene que lanzar
(mediante la instrucción throw) excepciones de ese tipo cuando el cambio de valor en
la propiedad no se pueda realizar debido a que no cumple una condición.
El lanzamiento de estas excepciones tiene esta sintaxis:
1> El JavaBean que posee la propiedad restringida debe tener definidos los
métodos addVetoableChangeListener y removeVetoableChange-
Listener. Si no los tiene definidos (por no ser clase heredera de
JComponent), se puede ayudar de un objeto VetoableChangeSupport
para definirlos.
311
Manual de Java
JavaBeans
3> El objeto oyente de los eventos de tipo veto, debe implementar la interfaz
VetoableChangeListener. Esta interfaz obliga a definir el método
vetoableChange que será llamado cada vez que se lance el evento de veto
(que ocurrirá normalmente cada vez que la propiedad cambie):
312