Programacion en Java

También podría gustarte

Está en la página 1de 87

INTRODUCCIÓN A LA PROGRAMACIÓN EN JAVA

1. INTRODUCCIÓN
1.1. Entorno de desarrollo Java
1.1.1. El compilador Java y la Java Virtual Machine
1.1.2. Variables PATH y CLASSPATH
1.1.3. Ejemplos
1.2. Nomenclatura y estructura de un programa Java
2. PROGRAMACIÓN EN JAVA
2.1. Tipos y nombres de variables. Tipos Primitivos
2.1.1. Definición, inicialización, visibilidad y vida
2.1.2. Clases BigInteger y BigDecimal
2.2. Operadores y precedencia
2.3. Estructuras de control
2.3.1. Bifurcaciones
2.3.2. Bucles
2.3.2.1. While y Do while
2.3.2.2. For
2.3.2.3. Break, continue y return
2.3.3. Try, catch, finally
3. CLASES EN JAVA
3.1. Variables
3.2. Métodos (funciones miembro)
3.2.1. Métodos de objeto
3.2.1.1. Sobrecarga de métodos
3.2.1.2. Paso de argumentos a métodos
3.2.2. Métodos de clase (static)
3.3. Objetos. Creación, destrucción y finalizadores
3.4. Packages
3.5. Herencia
3.5.1. Clases y métodos abstractos. Constructores en clases derivadas
3.5.2. Interfaces
3.7. Clases internas
3.7.1. Clases e interfaces internas static
3.7.2. Clases internas miembro (no static)
3.7.3. Clases internas locales y clases anónimas
3.8. Permisos de acceso en java
3.9. Transformaciones de tipo: casting
3.10. Polimorfismo

Programación en Java 1
4. CLASES DE UTILIDAD
4.1. Arrays
4.2. Clases string y stringbuffer
4.3. Wrappers y clase Math
4.4. Colecciones
4.4.1. Clase Vector, Hashtable e interface Enumeration
4.4.2. Collections Framework Java 1.2
4.4.2.1. Interfaces Collection, Iterator y ListIterator
4.4.2.2. Interfaces Comparable y Comparator
4.4.2.3. Sets y SortedSets
4.4.2.4. Listas
4.4.2.5. Maps y SortedMaps
4.4.2.6. Clases Collections y Arrays
4.4.2.7. Clases abstract e interfaces Cloneable y Serializable
4.5. Otras clases del package java.util
4.5.1. Clase Date, Calendar y GregorianCalendar
4.5.2. Clases DateFormat, SimpleDateFormat, TimeZone y SimpleTimeZone
5. EL AWT (ABSTRACT WINDOWS TOOLKIT)
5.1. Componentes y eventos del AWT Java
5.1.1. Relación entre Componentes y Eventos
5.1.2. Interfaces Listener y Clases Adapter
5.2. Clase Component
5.2.1. Clase Container
5.2.1.1. Clase Panel y ScrollPane
5.2.1.2. Clase Window, Frame, Dialog y FileDialog
5.2.2. Clases Button, Canvas, Checkbox y CheckboxGroup
5.2.3. Clases Choice, Label, List y Scrollbar
5.2.4. Clases TextComponent, TextArea y TextField
5.3. Clases EventObject y AWTEvent
5.3.1. Clases ActionEvent, Adjustment Event, ItemEvent y TextEvent
5.3.2. Clase ComponentEvent
5.3.2.1. Clases ContainerEvent, FocusEvent y WindowEvent
5.3.2.2. Clases InputEvent, MouseEvent y KeyEvent
5.4. Menús
5.4.1 Clase MenuShortcut, MenuComponent, MenuBar, MenuItem y Menu
5.4.2 Clase CheckboxMenuItem y PopupMenu
5.5. Layout managers
5.5.1. FlowLayout
5.5.2. BorderLayout, GridLayout, CardLayout y GridBagLayout
5.6. Gráficos, texto e imágenes
5.6.1. Clase Graphics y Font

Programación en Java 2
5.6.2. Clase FontMetrics y Color
5.6.3. Imágenes
5.7. Animaciones
6. THREADS
6.1. Ciclo de vida de un thread
6.2. Sincronización y prioridad
6.3. Grupos de Threads
7. APPLETS
7.1. Inclusión de un applet en una página html y paso de parámetros
7.2. Carga de applets
7.3. Imagen y sonido en applets
7.4. Obtención de las propiedades del sistema y uso de threads
8. EXCEPCIONES
8.1. Lanzamiento y captura de excepciones
8.2. Creación de nuevas excepciones y trato con herencia
9. E/S DE DATOS
9.1. Nomenclatura de las clases de java.io
9.2. E/S estándar
9.2.1. Salida de texto y variables por pantalla
9.2.2. Lectura del teclado
9.3. Lectura y escritura de archivos
9.3.1. Clases file y filedialog
9.3.2. Lectura y escritura de archivos de texto
9.3.3. Archivos no de texto
9.4. Serialización
9.5. Lectura de un archivo en un servidor de Internet
10. OTRAS CAPACIDADES DE JAVA
10.1. Java Foundation Classes (JFC) y Java 2D
10.2. Java Media Framework (JMF) y Java 3D
10.3. Javabeans
10.4. Java en la red. Servlets, RMI y Java IDL
10.5. Seguridad en Java
10.6. Acceso a bases de datos (JDBC)
10.7. Java Native Interface (JNI)

Programación en Java 3
1. INTRODUCCIÓN
Java surgió en 1991. Un grupo de ingenieros de Sun diseñaron un lenguaje de programación destinado a
electrodomésticos. La reducida potencia de cálculo y memoria de los electrodomésticos llevó a desarrollar
un lenguaje sencillo capaz de generar código de tamaño reducido.
La variedad distintas CPUs y su evolución obligaba a una herramienta independiente. Desarrollaron un
código neutro que se ejecutaba sobre una “máquina virtual”, la Java Virtual Machine (JVM). La JVM
interpretaba el código neutro convirtiéndolo a código particular de la CPU en cuestión. Esto permitía lo que
luego se ha convertido en el lema del lenguaje: “Write Once, Run Everywhere”.
Ninguna empresa de electrodomésticos se interesó por el nuevo lenguaje y como lenguaje de programación
se introdujo a finales de 1995. La clave fue la incorporación de un intérprete Java en la versión 2.0 del
programa Netscape Navigator, produciendo una revolución en Internet. Java 1.1 apareció a principios de
1997, mejorando su primera versión. Java 1.2, más tarde rebautizado como Java 2, nació a finales de 1998.
Al programar en Java no se parte de cero. Cualquier aplicación se apoya, en un gran número de clases
preexistentes. Algunas de ellas las ha podido crear el propio usuario, otras pueden ser comerciales, pero
siempre abundantes, que forman parte del propio lenguaje (el API o Application Programming Interface).
Java incorpora muchos aspectos que en cualquier otro lenguaje son extensiones propiedad de empresas de
sw o fabricantes de ordenadores (threads, ejecución remota, seguridad, acceso a BBDD, etc.). Por eso
muchos expertos opinan que Java es el lenguaje ideal para aprender la informática moderna, porque
incorpora estos conceptos de un modo estándar, más sencillo que con extensiones de otros lenguajes. Es
consecuencia de haber sido diseñado más recientemente y por un único equipo.
El objetivo de Java es llegar a ser el “nexo universal” que conecte a los usuarios con la información, esté en
local, en un servidor, BBDD u otro lugar. Java es un lenguaje completo: Java 1.0 tenía 12 packages; Java
1.1 tenía 23 y Java 1.2 tiene 59. En cierta forma casi todo depende de casi todo. Por ello, conviene
aprenderlo de modo iterativo: primero una visión general, que se vaya refinando en realimentación. Una
forma es empezar con un ejemplo completo en que aparecen las características importantes.
Sun describe Java como “simple, orientado a objetos (OO), distribuido, interpretado, robusto, seguro, de
arquitectura neutra, portable, de altas prestaciones, multitarea y dinámico”.
Java 2 (antes Java 1.2 o JDK 1.2) es su tercera versión importante. No hay cambios conceptuales, sino
extensiones y ampliaciones. A efectos es casi lo mismo trabajar con Java 1.1 o Java 1.2. Los desarrollos en
Java presentan ventajas frente a los de otros lenguajes como C/C++. La ejecución de programas en Java
tiene muchas posibilidades: ejecución como aplicación independiente (Stand-alone Application); como
applet; como servlet, etc. La ejecución como aplicación independiente es análoga a los programas
tradicionales. Un applet es una aplicación que se ejecuta en un navegador al cargar una página HTML. Se
descarga del servidor y no requiere instalación en el cliente. Un servlet es una aplicación sin interface
gráfica que se ejecuta en un servidor.
Además de incorporar la ejecución como Applet, Java permite el desarrollo fácil tanto de arquitecturas
cliente-servidor como de aplicaciones distribuidas, consistentes en aplicaciones que se conectan a otras
máquinas y ejecutan tareas en varios equipos simultáneamente, repartiendo por tanto el trabajo. Aunque
también otros lenguajes de programación permiten crear aplicaciones de este tipo, Java incorpora en su API
estas funcionalidades.
1.1. Entorno de desarrollo Java
Existen varios entornos de desarrollo Java comerciales. Sun distribuye gratuitamente el Java Development
Kit (JDK). Es una suite y bibliotecas que permiten desarrollar, compilar y ejecutar programas Java. Incorpora
la posibilidad de ejecutar parcialmente el programa, deteniendo la ejecución en el punto deseado y
estudiando en cada momento el valor de cada variable (el Debugger). Existe una versión reducida del JDK,
denominada JRE (Java Runtime Environment) destinada sólo a ejecutar código Java (no permite compilar).
Los IDEs (Integrated Development Environment) son entornos de desarrollo integrados. En un mismo
programa es posible escribir código Java, compilarlo y ejecutarlo. Algunos incluyen una herramienta de
Debug gráfico, frente a la versión del JDK basada en CLI. Estos IDE permiten desarrollar aplicaciones de
forma rápida, incorporando bibliotecas de componentes. Como inconvenientes se pueden señalar fallos de
compatibilidad entre plataformas, y ficheros de mayor tamaño que los basados en clases estándar.
1.1.1. El compilador Java y la Java Virtual Machine
El compilador Java es una herramienta de desarrollo incluida en el JDK. Realiza un análisis de sintaxis del
código fuente (.java). Si no encuentra errores en el código genera los ficheros compilados (.class) y en caso

Programación en Java 4
contrario muestra las líneas erróneas. En el JDK de Sun el compilador se llama javac.exe. Tiene opciones
que varían de una versión a otra.
La JVM o Java Virtual Machine. Es el sw que interpreta el código convirtiéndolo a código máquina particular
de la CPU. Así se consigue independencia (abstracción) de la máquina y el sw. La condición es que una vez
compilado, el código no debe necesitar modificaciones por cambiar de máquina o CPU. La idea es que el
código se ejecuta sobre una máquina virtual, la JVM.
La JVM es el intérprete de Java. Ejecuta los “bytecodes” o ficheros compilados de extensión .class. Tiene
opciones entre las que destaca la posibilidad de usar el denominado JIT (Just-In-Time Compiler), que puede
mejorar entre 10 y 20 veces la velocidad de ejecución de un programa.
1.1.2. Variables PATH y CLASSPATH
PATH. Es una variable de entorno que el SO que indica los directorios donde se buscarán los ejecutables
no invocados con una ruta dada. El desarrollo y ejecución de aplicaciones Java exige que las herramientas
para compilar (javac.exe) y ejecutar (java.exe) sean accesibles. Por tanto, para compilar o ejecutar código
Java, el directorio donde se encuentran los programas (javac.exe y java.exe) debe encontrarse en PATH.
CLASSPATH. Es una variable de entorno usada por Java que determina dónde buscar clases o bibliotecas
de Java (el API) y otras clases de usuario. A partir de la versión 1.1.4 del JDK no es necesario indicar esta
variable, salvo que se deseen añadir clases de usuario no incluidas en JDK. CLASSPATH puede incluir la
ruta de directorios o ficheros .zip o .jar (ficheros comprimidos de archivos .class.) en los que se encuentren
los .class. En el caso de .zip, los ficheros en él incluidos no deben estar comprimidos; con .jar la
herramienta jar.exe, de JDK, permite generar estos ficheros a partir de los archivos compilados .class.
Una forma general de indicar estas dos variables es crear un fichero batch MS-DOS (.bat) donde se
indiquen los valores de las variables. Cada vez que se abra una ventana MS-DOS será necesario ejecutar
el fichero .bat para asignar bien estos valores.
Un posible fichero llamado set JAVAPATH=C:\jdk1.1.7
jdk117.bat, podría ser como se
set PATH=.;%JAVAPATH%\bin;%PATH%
muestra en el ejemplo; válido si el
JDK estuviera situado en C:\jdk1.1.7. set CLASSPATH=.\;%JAVAPATH%\lib\classes.zip;%CLASSPATH%
Si no se desea tener que ejecutar este fichero cada vez que se abre una consola MS-DOS es necesario
indicar estos cambios de forma “permanente”, por ejemplo modificando Autoexec.bat situado en C:\,
añadiendo las líneas mostradas. Una vez reiniciado el equipo estarán presentes en cualquier consola MS2
que se cree. La modificación al fichero Autoexec.bat incluirá en la tercera línea la ruta de los ficheros donde
están las clases Java. En el caso de WS NT se añadirá la variable PATH en el cuadro de diálogo que se
abre desde el Panel de Control > System > Environment > User Variables for NombreUsuario.
También es posible usar la opción –classpath en el momento de llamar al compilador javac.exe o al
intérprete java.exe. En este caso los .jar deben indicarse con su nombre completo en CLASSPATH: no
basta poner el PATH o directorio en que se encuentra. Por ejemplo, para compilar y ejecutar el fichero
ContieneMain.java con la biblioteca de clases G:\MyProject\OtherClasses.jar, además de las incluidas en el
CLASSPATH, la forma de compilar y ejecutar sería:
javac -classpath .\;G:\MyProject\OtherClasses.jar ContieneMain.java
java -classpath .\;G:\MyProject\OtherClasses.jar ContieneMain
Para cada versión JDK habrá que tener en cuenta las variaciones. Cuando un fichero .java se compila y en
el directorio ya existe un fichero .class, se comparan las fechas de ambos ficheros. Si el .java es más
antiguo que el .class no se produce un nuevo fichero .class. Esto sólo es válido para ficheros .class que se
corresponden con una clase public.
1.1.3. Ejemplos
El siguiente ejemplo muestra las
características centrales de Java. Las
sentencias se numeran para hacer
más fácil la referencia al código.
La ejecución del programa imprime
algunas líneas en la consola MS-DOS
y conduce a crear la ventana mostrada
en la figura.

Programación en Java 5
El programa principal, contenido en el fichero Ejemplo1.java, sólo usa la clase Geometría y sus clases
derivadas. Es un programa puramente “usuario”, a pesar de lo cual hay que definirlo en una clase, como
todo programa en Java.
La línea 1 es un comentario con 1. // fichero Ejemplo1.java
el nombre del fichero. El
2. import java.util.ArrayList;
compilador ignora lo que va
desde los // al final de línea. 3. import java.awt.*;
Las sentencias 2 y 3 “importan” 4. class Ejemplo1 {
clases de los packages de
5. public static void main(String arg[]) throws InterruptedException
Java; hacen posible acceder a
dichas clases usando nombres 6. {
cortos. Por ejemplo, se puede
acceder a la clase ArrayList 7. System.out.println("Comienza main()...");
sólo con el nombre ArrayList en 8. Circulo c = new Circulo(2.0, 2.0, 4.0);
lugar de con java.util.ArrayList.
9. System.out.println("Radio = " + c.r + " unidades.");
Un package es una agrupación
de clases con una finalidad 10. System.out.println("Centro = (" + c.x + "," + c.y + ") unidades.");
relacionada. Hay una jerarquía 11. Circulo c1 = new Circulo(1.0, 1.0, 2.0);
de packages reflejada en
nombres compuestos 12. Circulo c2 = new Circulo(0.0, 0.0, 3.0);
separados por un punto. 13. c = c1.elMayor(c2);
Es habitual nombrar los 14. System.out.println("El mayor radio es " + c.r + ".");
packages con letras minúsculas
(como java.util o java.awt), 15. c = new Circulo(); // c.r = 0.0;
mientras que los nombres de 16. c = Circulo.elMayor(c1, c2);
las clases suelen empezar por
mayúscula (ArrayList). 17. System.out.println("El mayor radio es " + c.r + ".");

En la sentencia 3, el asterisco 18. VentanaCerrable ventana =


indica que se importan todas las 19. new VentanaCerrable("Ventana abierta al mundo...");
clases del package. El package,
java.lang, se importa siempre 20. ArrayList v = new ArrayList();
automáticamente, pudiéndose 21. CirculoGrafico cg1 = new CirculoGrafico(200, 200, 100, Color.red);
usar sus clases directamente,
sin importar el package. 22. CirculoGrafico cg2 = new CirculoGrafico(300, 200, 100, Color.blue);
La sentencia 4 indica que se 23. RectanguloGrafico rg = new
comienza a definir la clase 24. RectanguloGrafico(50, 50, 450, 350, Color.green);
Ejemplo1. La definición va entre
llaves {}. Como hay otras 25. v.add(cg1);
construcciones que van entre
26. v.add(cg2);
llaves, es habitual sangrar el
código, para facilitar su lectura. 27. v.add(rg);
En Java todo son clases: no se 28. PanelDibujo mipanel = new PanelDibujo(v);
puede definir una variable o
29. ventana.add(mipanel);
función fuera de una clase. En
este caso, la clase Ejemplo1 30. ventana.setSize(500, 400);
tiene como única finalidad
31. ventana.setVisible(true);
acoger al método main(); el
programa principal del ejemplo. 32. System.out.println("Termina main()...");
Las clases usadas por main() 33. } // fin de main()
son más importantes que la
34. } // fin de class Ejemplo1
propia clase Ejemplo1.
Una clase es una agrupación de datos y métodos para manejarlos y comunicarse con otras clases. Así,
puede entenderse una clase como un tipo de “dato” creado por el usuario y un objeto como una “variable”
de ese tipo de clase. Así, un objeto, posee la estructura de su clase; su copia propia de los datos y métodos
definidos abstractamente en la clase.
Las líneas 5-33 son la definición del programa principal, que en Java se llama main(). La ejecución siempre
comienza por el método main().

Programación en Java 6
Public indica que la función puede ser usada por cualquier clase.
Static indica que un método de clase: que puede ser usado aunque no se cree ningún objeto de la clase.
Void indica que el método no tiene valor de retorno.
A continuación de “main” aparecen entre paréntesis los parámetros del método. En el caso de main() es
siempre un vector o array (por los corchetes []), en este caso llamado arg, de cadenas de caracteres
(objetos de la clase String). Estos parámentros se pasan al programa al comenzar la ejecución.
En las líneas 6-33 se define el cuerpo (body) del método main, también entre llaves. Un conjunto de
sentencias entre llaves se suele llamar bloque. Los bloques no pueden entrecruzarse; un bloque puede
contener a otro, pero no se puede cerrar un bloque exterior antes de cerrar el interior.
La sentencia 7 (System.out.println("Comienza main()...");) imprime una cadena de caracteres en la salida
estándar del sistema (normalmente una ventana de CLI o especial del entorno de programación). El método
println() está asociado con una variable static llamada out, de la clase System (del package java.lang). Una
variable miembro static (variable de clase) es única para toda la clase y existe aunque no se instancie
ningún objeto de la clase. La variable out es una variable static de la clase System. Todas las sentencias
terminan con el carácter punto y coma.
La sentencia 8 (Circulo c = new Circulo(2.0, 2.0, 4.0);) crea un objeto de la clase Circulo, que se define
posteriormente. Equivale a las sentencias Circulo c; c = new Circulo(2.0, 2.0, 4.0);. Primero se define un
objeto llamado c de clase Circulo. Con el operador new se crea el objeto c. Poir tanto, dos acciones:
definición y creación (con new). El nombre de la clase va seguido por tres argumentos entre paréntesis, que
se pasan al constructor de la clase como datos concretos para crear el objeto (en este caso las dos
coordenadas del centro y el radio).
La diferencia entre clase y objeto aquí, responde a que la clase Circulo es lo genérico, lo abstracto; el patrón
o modelo para crear círculos concretos. El objeto c es un círculo concreto, con su centro y radio. De la clase
Circulo se pueden crear tantos objetos como se desee. La clase indica que cada objeto necesita 3 datos
(coordenadas del centro y radio), las variables miembro de la clase. Otra similitud: la clase sería el ADN, o
la especie y el objeto el individuo.
La sentencia 9 (System.out.println("Radio = " + c.r + " unidades.");) imprime en la salida estándar una
cadena de texto que contiene el valor del radio. La cadena tiene 3 partes concatenadas con el operador ‘+’.
Para acceder al radio del objeto c, se usa su nombre, seguido de punto y la variable miembro r, en la forma
[c.r], similar al formato [registro.campo]. El valor numérico del radio se convierte automáticamente en
cadena de caracteres. La sentencia 10 es similar a la 9, imprimiendo las coordenadas del centro del círculo.
Las sentencias 11 y 12 crean 2 nuevos objetos (2 círculos) llamados c1 y c2.
La sentencia 13 (c = c1.elMayor(c2);) usa el método elMayor() de la clase Circulo. El método, se supone,
compara los radios de dos círculos y devuelve el de mayor radio, asignándose al círculo c, creado en la
sentencia 8. Todos los métodos de Java (excepto los de clase o static) se aplican a un objeto con el
operador punto (por ejemplo, c1.elMayor()). El otro objeto (c2) se pasa como argumento entre paréntesis.
Obsérvese la forma “asimétrica” en que se pasan los argumentos al método elMayor(). De ordinario se
llama parámetro implícito a c1 y parámetro explícito a c2.
La sentencia 14 imprime el resultado de la comparación y la sentencia 15 crea un nuevo objeto tipo Circulo
guardándolo en la referencia c. En este caso no se pasan argumentos al constructor. Eso quiere decir que
se usarán valores “por defecto” para el centro y radio. Esta sentencia anula o borra el resultado de la
comparación de la línea 13.
La sentencia 16 vuelve a usar el método elMayor() para comparar 2 círculos. No es el mismo método de la
sentencia 13; es otro método de mismo nombre. Los métodos diferentes (porque tienen distinto código) e
igual nombre, se dice que están sobrecargados (overloaded).
Los métodos sobrecargados se diferencian por número y tipo de sus argumentos. El de la sentencia 13
tiene un único argumento. El de la sentencia 16 tiene 2. El método de la sentencia 16 es un método static
(método de clase) que no necesita ningún objeto como argumento implícito. Los métodos static suelen ir
precedidos por el nombre de la clase y el operador punto (sen permite que vayan precedidos por el nombre
de cualquier objeto, pero ofusca la nomenclatura). La sentencia 16 es equivalente a la sentencia 13, pero el
método static de la sentencia 16 es más “simétrico”. La sentencia 17 ya se puede entender.
Las sentencias 18-31 se refieren a la parte gráfica. Las líneas 18-19 crean una ventana para dibujar sobre
ella. Una ventana es un objeto de la clase Frame, del package java.awt. La clase VentanaCerrable,
explicada más adelante, añade a la clase Frame la capacidad de responder a los eventos que provocan el

Programación en Java 7
cierre de una ventana. La cadena que se le pasa como argumento es el título que aparecerá en la ventana
(como en la figura anterior).
En la sentencia 20 (ArrayList v = new ArrayList();) se crea un objeto de la clase ArrayList (del package
java.util), que permite almacenar referencias a objetos de distintas clases. En este caso se usará para
almacenar referencias a varias figuras geométricas diferentes. Las sentencias 21-27 crean objetos gráficos
y los guardan en la lista v para ser dibujados más tarde en el objeto mipanel de la clase PanelDibujo. Los
objetos de la clase Circulo creados no eran objetos aptos para ser dibujados, pues sólo tenían información
del centro y el radio, sin color de línea. Las clases RectanguloGrafico y CirculoGrafico, derivan
respectivamente de las clases Rectangulo y Circulo, que se ven luego, heredando variables y métodos y
añadiendo la información y métodos necesarios para su manejo.
Las sentencias 21-22 definen 2 objetos de la clase CirculoGrafico. A las coordenadas del centro y al radio
se une el color de línea. En la sentencia 23-24 se define un objeto de clase RectanguloGrafico, cpm im color
y las coordenadas de los vértices superior izquierdo e inferior derecho. En las sentencias 25-27 los objetos
gráficos se añaden al objeto v de la clase ArrayList con el método add() de la clase. La sentencia 28
(PanelDibujo mipanel = new PanelDibujo(v);) crea un objeto de la clase PanelDibujo. Los objetos de dicha
clase son paneles; superficies en las que dibujar. Al constructor de PanelDibujo se le pasa como argumento
el vector v con las referencias de los objetos a dibujar. La sentencia 29 incluye el panel en la ventana y la
sentencia 30 establece el tamaño de la ventana en pixels;
La sentencia 31 (ventana.setVisible(true);) hace visible la ventana creada. Para dibujar todo la clase
PanelDibujo derivada de la clase Container a través de Panel, redefine el método paint() de Container. En
este método, explicado posteriormente, se dibujan los objetos gráficos creados. El usuario no se preocupa
de llamar al método paint(), pues se llama automáticamente cada vez que el SO lo solicita. La figura anterior
muestra la ventana resultante de la ejecución del programa main() de la clase Ejemplo1.
La clase Geometria es la más importante en el sentido de que las demás clases derivan de ella. La figura
anterior muestra la jerarquía de clases del ejemplo. En Java la clase base es siempre la clase Object.
Siempre que no se diga explícitamente que una clase deriva de otra, deriva implícitamente de la clase
Object (del package java.lang). De las clases Rectangulo y Circulo derivan respectivamente las clases
RectanguloGrafico y CirculoGrafico. En ambos casos aparece el elemento Dibujable. En términos Java, es
un interface. Se suelen usar los términos super-clase y sub-clase para referirse a una clase padre o hija.
Así Geometría es una super-clase de Circulo, mientras que 1. // fichero Geometria.java
CirculoGrafico es una sub-clase. En este ejemplo sólo se
2. public abstract class Geometria {
dibujan rectángulos y círculos. De la clase Geometría derivan
las clases Rectangulo y Circulo. Estas clases tienen en común 3. // clase abstracta que no tiene objetos
ser “geometrías” y como tales tendrán características comunes
4. public abstract double perimetro();
como perímetro y área. A considerar es que no va a haber
nunca objetos de la clase Geometria. Una clase de la que no 5. public abstract double area();
existen objetos se denomina abstracta, y como tal puede ser
declarada. El fichero Geometria.java define dicha clase. 6. }

La clase Geometria se declara public para permitir que sea usada por otras clases, y como abstract para
indicar que no se permite crear objetos de la misma. Geometria no define variables miembro; sí declara 2
métodos: perímetro y area como public (para poder ser usados por otras clases) y abstract para indicar que
no se implementan aquí. La diferencia entre declaración (primera línea o header del método) y definición (la
implementación, el código). Se indica también que su valor de retorno es de tipo double y que no tienen
argumentos (obtendrán sus datos del objeto que se pase como argumento). Es lógico que no se definan en
esta clase los métodos perimetro y área, ya que su cálculo difiere para un rectángulo y un círculo, y por
tanto estos métodos habrá que definirlos en las clases Rectangulo y Circulo. En la clase Geometria lo que
se indica es cómo serán dichos métodos: su nombre, número y tipo de argumentos y el tipo de retorno.
La clase Rectangulo deriva de 1. // fichero Rectangulo.java
Geometria. Esto se indica en la
2. public class Rectangulo extends Geometria {
sentencia 2 con la palabra
extends. 3. // definición de variables miembro de la claes
Define 5 variables miembro. En la 4. private static int numRectangulos = 0;
sentencia 4 se define una
5. protected double x1, y1, x2, y2;
variable miembro static. Estas
variables se caracterizan por ser 6. // constructores de la clase
propias de la clase y no de cada
objeto. Así, numRectangulos 7. public Rectangulo(double p1x, double p1y, double p2x, double p2y) {
pretende llevar cuenta el número 8. x1 = p1x;

Programación en Java 8
de objetos de esta clase creados.
9. x2 = p2x;
No tiene sentido que cada objeto
10. y1 = p1y;
tuviera su copia de esta variable,
actualizándola cada vez que se 11. y2 = p2y;
crea o destruye un rectángulo.
12. numRectangulos++;
De la variable numRectangulos,
13. }
que en la sentencia 4 se inicializa
a cero, se mantiene sólo una 14. public Rectangulo(){ this(0, 0, 1.0, 1.0); }
copia para toda la clase.
15. // definición de métodos
Además esta variable es privada
(private), lo cual quiere decir que 16. public double perimetro() { return 2.0 * ((x1-x2)+(y1-y2)); }
sólo las funciones miembro de 17. public double area() { return (x1-x2)*(y1-y2); }
esta clase tienen permiso para
utilizarla. 18. } // fin de la clase Rectangulo

La sentencia 5 define 4 nuevas variables miembro de tipo double, las coordenadas de 2 vértices opuestos.
Al declararlas protected se indica que sólo esta clase, las clases derivadas y las clases del package tienen
permiso para usarlas.
Las sentencias 7-14 definen los constructores de la clase. Los constructores son métodosque no retornan
valor (ni siquiera void) y su nombre coincide con el de la clase. Los constructores son ejemplo típico de
sobrecarga. En este caso hay 2 constructores, el segundo sin parámetros: es el constructor por defecto. Las
sentencias 7-13 definen el constructor general. Este constructor recibe 4 argumentos que asigna a las
variables miembro. La sentencia 12 incrementa en uno el número de rectángulos creados. La sentencia 14
define un segundo constructor que crea un rectángulo cuando no se dan datos, de lado unidad cuyo primer
vértice es el origen de coordenadas. Este constructor en realidad no tiene código para inicializar las
variables miembro; llama al constructor general previo con la palabra this.
Las sentencias 16 y 17 contienen la definición de 1. // fichero Circulo.java
los métodos miembro perimetro y area. El nombre
2. public class Circulo extends Geometria {
coincide con los de la clase Geometría, pero
seguido del cuerpo del método entre llaves. 3. static int numCirculos = 0;
La clase Circulo deriva de Geometria. Es similar a 4. public static final double PI=3.1415926535897932;
la clase Rectangulo. Se presenta en el cuadro.
5. public double x, y, r;
La sentencia 3 define una variable static o de clase
no definida como private. Si no se especifican 6. public Circulo(double x, double y, double r) {
permisos (public, private o protected) se supone la 7. this.x=x; this.y=y; this.r=r;
opción por defecto, package, con la que la variable
o método sólo puede ser usado por clases del 8. numCirculos++;
package. En el ejemplo no se ha definido ningún 9. }
package; se usa por defecto, el directorio donde se
definen las clases. Así, numCirculos podrá ser 10. public Circulo(double r) { this(0.0, 0.0, r); }
usada sólo por clases del directorio de Circulo. 11. public Circulo(Circulo c) { this(c.x, c.y, c.r); }
La sentencia 4 define una variable static, con la 12. public Circulo() { this(0.0, 0.0, 1.0); }
palabra final, o sea, una constante. Es lógico
definir el número π como constante y como static 13. public double perimetro() { return 2.0 * PI * r; }
de la clase Circulo, para compartirla. 14. public double area() { return PI * r * r; }
La sentencia 5 (public double x, y, r;) define las 15. // método de objeto para comparar círculos
variables miembro del objeto, las coordenadas del
centro y el radio del círculo. 16. public Circulo elMayor(Circulo c) {

La sentencia 6-9 define el constructor general de la 17. if (this.r>=c.r) return this; else return c;
clase Circulo. En este caso el nombre de los 18. }
parámetros (x, y, r) coincide con el de las variables
miembro. Esto es un problema, porque los 19. // método de clase para comparar círculos
parámetros son variables locales al bloque del 20. public static Circulo elMayor(Circulo c, Circulo d) {
método, destruidas al salir; ocultan variables de
ámbito más general de mismo nombre. Si en el 21. if (c.r>=d.r) return c; else return d;
código del constructor se usan las variables (x, y, r) 22. }
se refieren a los parámetros del método, no a las
variables miembro, las que se desea modificar. 23. } // fin de la clase Circulo

Programación en Java 9
La sentencia 7 indica cómo se resuelve el problema. Para cualquier método no static de una clase, la
palabra this es una referencia al objeto (argumento implícito) sobre el que se se aplica el método. Así, this.x
se refiere a la variable miembro, y x al parámetro del constructor.
Las sentencias 10-12 son otros 3 constructores (sobrecargados), que se diferencian en el número y tipo de
parámetros. Los 3 tienen en común llamar al constructor general, al que se hace referencia con la palabra
this. Al constructor de la sentencia 10 sólo se le pasa el radio, con lo cual construye un círculo con ese radio
centrado en el origen de coordenadas. Al constructor de la sentencia 11 se le pasa otro objeto círculo, del
que se hace copia. El de la sentencia 12 es un constructor por defecto, al que no se pasa parámetros y crea
un círculo de radio unidad centrado en el origen. Las sentencias 13 y 14 definen los métodos perimetro y
area, declarados como abstract en Geometria, adaptados a círculos.
Las sentencias 16-18 definen elMayor, como método de objeto para comparar círculos. Uno de los círculos
es parámetro implícito y el otro explícito. La sentencia 17 muestra cómo al radio del parámetro implícito se
accede en la forma this.r y al del explícito como c.r, donde c es el nombre del objeto pasado como
argumento. La sentencia return devuelve una referencia al objeto cuyo radio sea mayor. Cuando éste es el
argumento implícito se devuelve this.
Las sentencias 20-22 definen elMayor, como método de clase (con static) y por tanto no tiene parámetro
implícito. Los dos objetos a comparar se deben pasar como argumentos explícitos. En ambos casos lo que
se devuelve como retorno no es el objeto de mayor radio, sino una referencia.
Interface Dibujable. El diagrama de clases de la figura 1. // fichero Dibujable.java
indica que las clases RectanguloGrafico y CirculoGrafico
2. import java.awt.Graphics;
son el resultado, tanto de las clases Rectangulo y Circulo
de las que derivan, como de la interface Dibujable. El 3. public interface Dibujable {
concepto de interface es importante en Java.
4. public void setPosicion(double x, double y);
A diferencia de C++, Java no permite herencia múltiple, o
sea, que una clase derive de clases distintas heredando de 5. public void dibujar(Graphics dw);
ambas métodos y variables miembro. 6. }
La herencia múltiple es fuente de problemas, pero en ocasiones conveniente. Las interfaces de Java son
una alternativa a la herencia múltiple con ventajas prácticas y de estilo. Un interface es un conjunto de
declaraciones de métodos sin implementación. Se declara el tipo del valor de retorno y nombre del método,
seguido del tipo de parámetros entre paréntesis. Si una clase implementa un interface, se compromete a dar
definición a los métodos del interface. En cierta forma un interface es una clase abstract de métodos
abstract. Una ventaja de los interfaces Java es establecer pautas o modos de funcionamiento similares para
clases que pueden estar o no relacionadas con herencia. Todas las clases que implementan un interface
soportan los métodos declarados en ella y en este sentido se comportan de modo similar. Las interfaces
pueden relacionarse con herencia, con más flexibilidad que las clases.
El fichero Dibujable.java define el interface Dibujable para incorporar, en las clases que la implementen, la
capacidad de dibujar sus objetos. El listado muestra la declaración de los métodos setPosicion y dibujar. La
declaración de estos métodos no tiene nada particular. Como el método dibujar usa como argumento un
objeto de la clase Graphics, hay que importar la clase. Si las clases RectanguloGrafico y CirculoGrafico
implementan el interface Dibujable sus objetos podrán ser mostrados en pantalla.
La clase RectanguloGrafico. 1. // Fichero RectanguloGrafico.java
Deriva de Rectangulo (hereda
2. import java.awt.Graphics;
métodos y variables miembro) e
implementa la interface Dibujable 3. import java.awt.Color;
(debe definir los métodos
declarados en el interface). El 4. class RectanguloGrafico extends Rectangulo implements Dibujable {
cuadro muestra la definición de la 5. // nueva variable miembro
clase.
6. Color color;
Las sentencias 2 y 3 importan
dos clases del package java.awt. 7. // constructor
Podrían importarse todas las 8. public RectanguloGrafico(double x1,double y1, double x2, double y2,
clases del package con import
java.awt.*;. 9. Color unColor) {

La sentencia 4 indica que 10. // llamada al constructor de Rectangulo


RectanguloGrafico deriva de la 11. super(x1, y1, x2, y2);
clase Rectangulo e implementa la
interface Dibujable. Se pueden 12. this.color = unColor; // en este caso this es opcional

Programación en Java 10
implementar varias interfaces, en
13. }
cuyo caso se ponen en el
encabezamiento de la clase 14. // métodos de la interface Dibujable
separadas por comas.
15. public void dibujar(Graphics dw) {
La sentencia 6 define una nueva
16. dw.setColor(color);
variable miembro que se suma a
las que se tienen por herencia. 17. dw.drawRect((int)x1, (int)y1, (int)(x2-x1), (int)(y2-y1));
Esta nueva variable es un objeto
de la clase Color. 18. }

Las sentencias 8-13 definen el 19. public void setPosicion(double x, double y) {


constructor general de la clase, al 20. ; // método vacío, pero necesario de definir
que llegan los 5 argumentos
para dar valor a las variables 21. }
miembro. 22. } // fin de la clase RectanguloGrafico
En este caso los nombres de los parámetros coinciden con los de las variables miembro, pero sólo se usan
para pasárselos al constructor de la superclase. La sentencia 11 contiene la novedad que para dar valor a
las variables heredadas se llama al constructor de la clase padre o superclase con la palabra super.
Las sentencias 14-18 y 19-21 definen // fichero CirculoGrafico.java
los 2 métodos declarados en el
import java.awt.Graphics;
interface Dibujable. El método dibujar
recibe de parámetro un objeto dw de import java.awt.Color;
la clase Graphics que define el
public class CirculoGrafico extends Circulo implements Dibujable {
contexto de operación en un panel,
(color de líneas, del fondo, etc.). // se heredan las variables y métodos de la clase Circulo
La sentencia 16 hace uso de un Color color;
método de la clase Graphics para
determinar el color con que se // constructor
dibujará a partir de ese momento. public CirculoGrafico(double x, double y, double r, Color unColor) {
La sentencia 17 llama a otro método // llamada al constructor de Circulo
de esa misma clase que dibuja un
rectángulo a partir de las coordenadas super(x, y, r);
del vértice superior izquierdo y de la this.color = unColor;
anchura y altura.
}
Java obliga a implementar o definir
siempre los métodos declarados por el // métodos de la interface Dibujable
interface, aunque no se usen. public void dibujar(Graphics dw) {
Por eso, las sentencias 19-21 definen dw.setColor(color);
un método vacío, que sólo contiene un
carácter ´;´. Como no se usa no hay dw.drawOval((int)(x-r),(int)(y-r),(int)(2*r),(int)(2*r));
conflicto, pero hay que implementarlo }
por obligación de Java.
public void setPosicion(double x, double y) {
La clase CirculoGrafico. Deriva de la
clase Circulo e implementa la interface ;
Dibujable. Es similar a la clase }
RectanguloGrafico como se muestra
en el código del cuadro. } // fin de la clase CirculoGrafico

La clase PanelDibujo. Es responsable final de que los rectángulos y círculos se dibujen en pantalla. Deriva
de la clase Panel, que deriva de Container, que deriva de Component, que deriva de Object. La clase
Component comprende los objetos Java con representación gráfica como botones, barras de
desplazamiento, etc. Los objetos de la clase Container son objetos gráficos del AWT capaces de contener
otros objetos del AWT (Abstract Windows Toolkit; biblioteca Java que permite crear GUI).
La clase Panel define los Container más sencillos, capaces de contener otros elementos gráficos (como
otros paneles) y sobre la que dibujar. La clase PanelDibujo contiene el código mostrado a continuación.
Las sentencias 2-4 importan las clases necesarias para 1. // fichero PanelDibujo.java
construir la clase PanelDibujo. Se importan todas las clases
2. import java.awt.*;
del package java.awt. La clase ArrayList y el interface

Programación en Java 11
Iterator pertenecen al package java.util, y sirven para tratar
3. import java.util.ArrayList;
colecciones (en este caso conjuntos) de figuras dibujables.
4. import java.util.Iterator;
La sentencia 5 indica que la clase PanelDibujo deriva de la
Panel, heredando de ésta y de sus superclases Container y 5. public class PanelDibujo extends Panel {
Component todas sus capacidades gráficas.
6. // variable miembro
La sentencia 7 crea una variable miembro v, referencia a un
7. private ArrayList v;
objeto de la clase ArrayList (no es un objeto, es una
referencia o nombre de objeto). 8. // constructor
Las sentencias 9-12 definen el constructor de la clase, que 9. public PanelDibujo(ArrayList va) {
recibe de parámetro la referencia va a un objeto de la clase
ArrayList. En la lista se almacenarán las referencias a los 10. super(new FlowLayout());
objetos (rectángulos y círculos a dibujar). 11. this.v = va;
En la sentencia 10 se llama al constructor de la superclase 12. }
panel, pasándole como argumento un objeto creado de la
clase FlowLayout, que se ocupa de distribuir de determinada 13. // redefinición del método paint()
forma (de izqda. a dcha. y de arriba abajo) los componentes 14. public void paint(Graphics g) {
gráficos que se añaden a un “container” tal como la clase
Panel. En este caso no tiene mucha importancia, pero 15. Dibujable dib;
conviene utilizarlo. 16. Iterator it;
Un aspecto importante de Java y de la POO es el 17. it = v.iterator();
polimorfismo. Es la propiedad por la que una referencia a un
objeto de una clase puede referirse a objetos de cualquiera 18. while(it.hasNext()) {
de sus clases derivadas. Por ejemplo, es posible en Java 19. dib = (Dibujable)it.next();
hacer lo siguiente:
20. dib.dibujar(g);
Geometria geom1, geom2;
21. }
geom1 = new RectanguloGrafico(0, 0, 200, 100, Color.red);
22. }
geom2 = new CirculoGrafico(200, 200, 100, Color.blue);
23. } // Fin de la clase PanelDibujo
Se crean 2 referencias de la clase Geometria que apuntan a objetos de clases derivadas RectanguloGrafico
y CirculoGrafico. Sin embargo, hay limitación con las referencias geom1 y geom2. Por ser referencias a la
clase Geometria sólo se pueden usar las capacidades definidas en dicha clase, que se reducen al uso de
los métodos perimetro y area. De la misma forma que con Geometria, es posible usar una referencia del tipo
correspondiente a un interface para manejar objetos de clases que implementan el interface. Por ejemplo:
Dibujable dib1, dib2;
dib1 = new RectanguloGrafico(0,0,200,100,Color.red); dib2 = new CirculoGrafico(200, 200, 100, Color.blue);
donde los objetos referidos por dib1 y dib2 pertenecen a las clases RectanguloGrafico y CirculoGrafico, que
implementan la interface Dibujable. Estos objetos tienen una limitación: sólo pueden ser usados con los
métodos definidos en el interface Dibujable. Poder usar nombres de una superclase o de un interface
permite tratar de modo unificado objetos distintos, aunque pertenecientes a subclases diferentes o a clases
que implementan el interface. Esta es la idea central del polimorfismo. Así, en el código de la clase
PanelDibujo, en las sentencias 14-22 se define el método paint, heredado de Container, que a su vez
redefine el método heredado de Component. En PanelDibujo se redefine el método. El método paint, por lo
general, no lo llama el programador, sino Java y el SO. El programador prepara por una parte la ventana y
el panel en que va a dibujar, y por otra programa en el método paint las operaciones a realizar. El SO y Java
llaman a paint cada vez que entienden que la ventana debe ser dibujada o redibujada. El único parámetro
de paint es un objeto g de la clase Graphics que constituye el contexto gráfico (color de líneas, tipo de letra,
etc.) con el que se realizarán las operaciones.
La sentencia 15 crea una referencia de la clase Dibujable que podrá apuntar o contener objetos de cualquier
clase que implemente el interface. La sentencia 16 crea una referencia a un objeto de la interface Iterator
definida en el package java.util, que proporciona los métodos hasNext, que chequea si la colección de
elementos que se está recorriendo tiene más elementos y next, que devuelve el siguiente elemento de la
colección. Cualquier colección de elementos (como ArrayList u otra lista vinculada definida por el usuario)
puede implementar este interface y ser usada de modo uniforme.
En la sentencia 17 se usa el método iterator de la clase ArrayList que devuelve una referencia Iterator de los
elementos de la lista v. Obsérvese la diferencia entre el método iterator de la clase ArrayList y el interface
Iterator. En Java los nombres de las clases e interfaces siempre empiezan por mayúscula, mientras que los
Programación en Java 12
métodos con minúscula. Las sentencias 18-21 representan un bucle while cuyas sentencias se repetirán
mientras haya elementos en la enumeración e (o en el vector v).
La sentencia 19 contiene elementos nuevos. El método it.next devuelve el siguiente objeto de la lista de la
referencia de tipo Iterator. En principio este objeto podría ser de cualquier clase. Los elementos de la clase
ArrayList son referencias de la clase Object, lo que indica que esas referencias pueden apuntar a objetos de
cualquier clase. El nombre de la interface Dibujable entre paréntesis representa un cast o conversión entre
tipos diferentes. En Java como en C++, la conversión entre variables u objetos de distintas clases es
importante. Por ejemplo, (int)3.14 convierte el número double 3.14 en el entero 3.
No todas las conversiones son posibles, pero sí lo son las dadas entre clases de la misma línea jerárquica
o que implementen el mismo interface. Lo que se indica a la referencia dib con el cast a la interface
Dibujable en la sentencia 19, es que el objeto de la enumeración va a ser tratado exclusivamente con los
métodos del interface.
En la sentencia 20 se aplica el método dibujar al objeto referenciado por dib, que forma parte del iterator it,
obtenido a partir de la lista v. Esto es típico de Java y de la POO. La ventaja del método paint así
programado es que es general: no se hace referencia a las clases RectanguloGrafico y CirculoGrafico,
cuyos objetos son realmente los que se dibujan. Esto permite añadir nuevas clases tales como
TrianguloGrafico, PoligonoGrafico, LineaGrafica, etc., sin modificar el código: tan sólo deben implementar el
interface Dibujable. Esta es una ventaja para crear programas extensibles, flexibles y reutilizables.
La clase VentanaCerrable. Es 1. // Fichero VentanaCerrable.java
una clase de “utilidad” que
2. import java.awt.*;
mejora las características de la
clase Frame de Java, de la que 3. import java.awt.event.*;
deriva. La clase Frame estándar
4. class VentanaCerrable extends Frame implements WindowListener {
tiene una limitación: no responde
a las acciones normales en WS 5. // constructores
para cerrar una ventana o
aplicación, como hacer clic en la 6. public VentanaCerrable() {
X de la esquina superior derecha. 7. super();
En ese caso, para cerrar la 8. }
aplicación hay que recurrir p. ej.
al comando End Task del Task 9. public VentanaCerrable(String title) {
Manager de WS. Para evitarlo se 10. super(title);
crea la clase VentanaCerrable,
derivada de Frame e implementa 11. setSize(500,500);
la interface WindowListener, 12. addWindowListener(this);
como muestra el código ejemplo.
13. }
VentanaCerrable contiene 2
constructores. El primero por 14. // métodos de la interface WindowsListener
defecto (sin argumentos) que se 15. public void windowActivated(WindowEvent e) {;}
limita a llamar al constructor de la
superclase Frame con “super”. 16. public void windowClosed(WindowEvent e) {;}
El segundo constructor admite un 17. public void windowClosing(WindowEvent e) {System.exit(0);}
argumento para poner título a la 18. public void windowDeactivated(WindowEvent e) {;}
ventana. Llama también al
constructor de Frame pasándole 19. public void windowDeiconified(WindowEvent e) {;}
este mismo argumento y 20. public void windowIconified(WindowEvent e) {;}
establece un tamaño para la
ventana creada (el tamaño por 21. public void windowOpened(WindowEvent e) {;}
defecto para Frame es cero). 22. } // fin de la clase VentanaCerrable
La sentencia 12 muestra cómo el AWT de Java gestiona los eventos sobre ventanas y en general sobre la
GUI. Cuando un elemento gráfico (aquí la ventana) puede recibir eventos del usuario es necesario indicar
quién se va a encargar de procesarlos. De ordinario al producirse un evento se debe activar un método que
se encargue de procesarlo y realizar acciones (en este caso cerrar la ventana y la aplicación). La sentencia
12 ejecuta el método addWindowListener de la clase Frame (que a su vez heredado de la clase Window).
El parámetro que se pasa al método indica qué objeto gestiona los eventos de la ventana implementando la
interface WindowListener. En este caso, como el parámetro es this, la propia clase VentanaCerrable
gestiona los eventos. Como el constructor por defecto de las sentencias 6-8 no usa el método

Programación en Java 13
addWindowListener(), si se construye una VentanaCerrable sin título no podrá ser cerrada del modo
habitual. Así se ha hecho en este ejemplo para poder comprobarlo.
El interface WindowListener define los 7 métodos para gestionar los eventos con los que actuar sobre una
ventana. Para cerrar la ventana sólo es necesario definir el método windowClosing(). Sin embargo,
implementar un interface obliga a definir todos sus métodos. Por ello en las sentencias 15-21 los métodos
están vacíos, excepto el que interesa, que llama al método exit de System. El argumento “0” indica
terminación normal del programa.
1.2. Nomenclatura y estructura de un programa Java
Los nombres de Java son sensibles a mayúsculas y minúsculas (case-sensitive). Es habitual seguir ciertas
normas de estilo que facilitan la lectura y mantenimiento del código. Algunas son:
1. Es habitual usar nombres en minúsculas, con las excepciones siguientes
2. Cuando un nombre consta de varias palabras se escriben seguidas con mayúscula la primera letra de
cada palabra (elMayor, VentanaCerrable, RectanguloGrafico…)
3. Los nombres de clases e interfaces comienzan siempre por mayúscula (Geometria, Rectangulo…)
4. Los nombres de objetos, métodos y variables miembro, y los de las variables locales de los métodos,
comienzan siempre por minúscula (main, dibujar, numRectangulos…)
5. Los nombres de variables finales (constantes) siempre con mayúsculas (PI, TAM…)
El anterior ejemplo presenta la estructura habitual de un programa codificado en un lenguaje de POO.
Primero, una clase con el programa principal (con la función main y clases de usuario) usadas por el
programa principal. Los ficheros fuente tienen extensión *.java, y los compilados extensión *.class. Un
fichero fuente puede contener más de una clase, pero sólo una puede ser public. El nombre del fichero
fuente debe coincidir con el de la clase public, considerando el case-sensitive. Si la clase no es public, no es
necesario que su nombre coincida con el del fichero. Una clase puede ser public o package (default).
En general, una aplicación está formada por varios ficheros *.class. Cada clase realiza unas funciones
permitiendo construir aplicaciones con independencia entre clases. La aplicación se ejecuta con el nombre
de la clase que contiene la función main. Las clases se agrupan en packages; bibliotecas. Si las clases no
se definen pertenecientes a un package, se usa un package por defecto (default): el directorio de trabajo.
Clase. Una clase es una agrupación de datos y métodos que los manejan: las variables y métodos miembro.
La POO se basa en la programación de clases. Un programa se construye a partir de un conjunto de clases.
Objeto. Son elementos particulares de una clase. Cada objeto posee sus propias variables miembro, con
valores particulares distintos de los valores del resto de objetos de la clase. Las clases pueden tener
variables static, que son propias de la clase y no de cada objeto.
Herencia. Es la propiedad que permite definir nuevas clases basadas en clases existentes, reutilizando el
código. Si una clase deriva de otra (extends) hereda sus variables y métodos. La clase derivada puede
añadir nuevas variables y métodos y/o redefinir los heredados. En Java una clase sólo puede derivar de una
sóla clase: no permite herencia múltiple en base a clases. Sin embargo es posible “simular” la herencia
múltiple con interfaces.
Interface. Es un conjunto de declaraciones de funciones. Si una clase implementa (implements) un interface,
debe definir todas las funciones especificadas por el interface. Una clase puede implementar más de un
interface, simulando así herencia múltiple. A su vez, un interface puede derivar de una o varias interfaces,
en cuyo caso incorpora todos los métodos de las interfaces de las que deriva.
Package. Es una agrupación de clases. Una biblioteca. Existen packages estándar del lenguaje y el usuario
puede crear sus propios packages. Lo habitual es reunir en packages clases relacionadas. Todas las clases
que formen parte de un package deben estar en el mismo directorio.
Jerarquía de clases Java (API). La documentación on-line del API de Java muestra la jerarquía de clases
(relaciones de herencia) e información de los packages de las bibliotecas base de Java. La diferencia es
que el package es una agrupación arbitraria de clases. La jerarquía marca la herencia entre clases.
En la documentación on-line se presenta “Package Index” y “Class Hierarchy”. El primero presenta la
estructura del API de Java por packages; el segundo la jerarquía de clases. Si se selecciona una clase se
muestra una descripción de los métodos y variables de la clase. A su vez muestra su herencia completa.

Programación en Java 14
2. PROGRAMACIÓN EN JAVA
Java como lenguaje de programación algorítmico es simila a otros como C/C++, en los que se inspira.
2.1. Tipos y nombres de variables. Tipos Primitivos
Una variable es un nombre que identifica un valor que puede cambiar. Según el tipo de dato que contenga,
en Java hay 2 tipos principales de variables:
Variables de tipos primitivos. Se definen con un tipo de valor único que puede ser entero, de punto flotante,
carácter o booleano. Java permite distinta precición y rangos para estos tipos (char, byte, short, int, long,
float, double, boolean).
Variables referencia. Son referencias o nombres de datos más complejos: arrays u objetos de una clase.
Desde el punto de vista del ámbito del programa, las variables pueden ser:
Variables miembro. Se definen en una clase, fuera de métodos; pueden ser tipos primitivos o referencias.
Variables locales. Se definen en un método o en un bloque entre llaves. Se crean en el interior del bloque y
se destruyen al finalizar dicho bloque. Pueden ser tipos primitivos o referencias.
Un nombre de variable en Java se puede crear con libertad. Puede ser cualquier conjunto de caracteres
numéricos y alfanuméricos, sin algunos caracteres especiales como operadores o separadores ( ,.+-*/ etc.).
En Java existen palabras reservadas, que no se pueden usar como nombres de variables. Son:
Palabras Reservadas JAVA
abstract boolean break byte case catch char class
const* continue default do double else extends final
finally float for goto* if implements import instanceof
int interface long native new null package private
protected public return short static super switch synchronized
this throw Throws transient try void volatile while
(*) son palabras reservadas, pero no se utilizan en la actual implementación del lenguaje Java
Los tipos primitivos de variables Java son variables sencillas con tipos de datos simples como valores
boolean, enteros, etc. Java dispone 8 tipos primitivos de datos. Boolean almacena valores true y false; char
almacena caracteres y 6 tipos para valores numéricos, 4 tipos para enteros (byte, short, int y long) y 2 para
reales de punto flotante (float y double). Los rangos y memoria que ocupa cada uno se muestra en la tabla:
Tipo de variable Descripción
Boolean 1 B. Valores true y false
Char 2 B. Unicode. Comprende el código ASCII
Byte 1 B. Valor entero entre -128 y 127
Short 2 B. Valor entero entre -32768 y 32767
Int 4 B. Valor entero entre -2.147.483.648 y 2.147.483.647
Long 8 B. Valor entre -9.223.372.036.854.775.808 y 9.223.372.036.854.775.807
Float 4 B (entre 6 y 7 cifras decimales equivalentes). De -3.402823E38 a -1.401298E-45 y
de 1.401298E-45 a 3.402823E38
Double 8 bytes (~ 15 cifras decimales equivalentes). De -1.79769313486232E308 a -
4.94065645841247E-324 y de 4.94065645841247E-324 a 1.79769313486232E308
El tipo boolean no es numérico; no se identifica con igual o distinto de cero, como en C/C++. El resultado de
una expresión lógica es boolean. El tipo char se codifica en UNICODE (incluye ASCII y son 16 bits / car).
Los tipos byte, short, int y long son números enteros con rangos distintos. A diferencia de C/C++, en Java
no hay enteros unsigned. Los tipos float y double son valores de punto flotante (números reales) con 6-7 y
15 cifras decimales equivalentes, respectivamente. Se usa la palabra void para indicar ausencia de un tipo
de dato en una variable. A diferencia de C/C++, los tipos de variables en Java están perfectamente
definidos en todas las plataformas. Extensiones de Java 1.2 aprovechan la arquitectura Intel, permitiendo
operaciones de punto flotante con precisión extendida de 80 bits.

Programación en Java 15
2.1.1. Definición, inicialización, visibilidad y vida
Una variable se define especificando tipo y nombre. Pueden ser de tipos primitivos o referencias a objetos
de alguna clase de la API Java o generada por usuario. Si no se especifica un valor en su declaración, las
variables primitivas se inicializan a cero (salvo boolean y char, que se inicializan a false y '\0'). Las variables
de tipo referencia se inicializan por defecto al valor null.
Es importante distinguir entre la referencia a un objeto y el objeto. Una referencia es una variable que indica
dónde está guardado un objeto en memoria (a diferencia de C/C++, Java no permite acceder al valor de la
dirección, ya que no usa punteros). Al declarar una referencia no se apunta nada, por eso se asigna a null.
Si se desea que esa referencia apunte a un objeto hay que crearlo con el operador new, que reserva en
memoria espacio para ese objeto (variables y métodos). También se puede igualar la referencia a otra
referencia de un objeto existente.
Un tipo particular de referencias son los arrays o vectores, de variables primitivas u objetos. En la
declaración de una referencia tipo array hay que incluir corchetes. Java garantiza que los elementos del
vector son inicializados a null o a cero (según tipo de dato) en caso de no indicar valor. En los ejemplos
aparece cómo crear un vector de 10 números enteros y cómo crear uno de elementos MyClass:
int x; // Declaración de la variable primitiva x. Se inicializa a 0
int y = 5; // Declaración de la variable primitiva y. Se inicializa a 5
MyClass unaRef; // Declaración de una referencia a un objeto MyClass. Se inicializa a null
unaRef = new MyClass(); // La referencia “apunta” al objeto creado con el constructor por defecto
MyClass segundaRef = unaRef; // Declaración referencia a objeto MyClass. Se inicializa al valor de unaRef
int [] vector; // Declaración de un array. Se inicializa a null
vector = new int[10]; // Vector de 10 enteros, inicializados a 0
double [] v = {1.0, 2.65, 3.1}; // Declaración e inicialización de vector de 3 elementos
MyClass [] lista=new MyClass[5]; // Se crea un vector de 5 referencias a objetos inicializadas a null
lista[0] = unaRef; // Se asigna a lista[0] el mismo valor que unaRef
lista[1] = new MyClass(); // Se asigna a lista[1] la referencia al nuevo objeto. lista[2]… siguen a null
En el ejemplo las referencias unaRef, segundaRef y lista[0] actuarán sobre el mismo objeto. Es equivalente
usar cualquiera de las referencias ya que el objeto al que se refieren es el mismo.
Se entiende por visibilidad, ámbito o scope de una variable, la parte de la aplicación donde es accesible y
por tanto puede ser usada. En Java todas las variables deben estar incluidas en una clase.
En general las variables declaradas entre llaves, es decir en un bloque, son visibles y existen en ese bloque.
Por ejemplo las variables declaradas al principio de una función existen mientras se ejecute la función; las
variables declaradas en un bloque if no serán válidas al finalizar las sentencias del if y las variables miembro
de una clase son válidas mientras existe el objeto de la clase.
Las variables miembro de una clase declaradas public son accesibles con una referencia a un objeto de
dicha clase usando el operador punto.
Las variables miembro de una clase declaradas private no son accesibles directamente desde otras clases.
Los métodos miembro de una clase tienen acceso a todas las variables miembro de la clase sin necesidad
de anteponer el nombre de un objeto. Pero los métodos miembro de una clase B derivada de otra A, tienen
acceso a todas las variables miembro de A declaradas como public o protected, pero no a las private.
Una clase derivada sólo puede acceder directamente a las variables y métodos miembro public o protected
de su clase base. Otra característica es que es posible declarar una variable en un bloque con el mismo
nombre que una variable miembro, pero no con el nombre de otra variable local existente. La variable
declarada en el bloque oculta a la variable miembro. Para acceder a la variable miembro oculta será preciso
usar el operador this, en la forma this.varname.
Un aspecto importantes en la POO es la forma en que se crean y eliminan objetos. En Java la forma de
crear nuevos objetos es usar el operador new. Al usar new, la variable de tipo referencia guarda la posición
de memoria donde se almacena ese objeto. Para cada objeto se lleva cuenta de las variables que lo
apuntan. La eliminación de los objetos la realiza el programa Garbage Collector, que automáticamente
libera la memoria asignada a un objeto cuando no existe ninguna referencia que lo apunte. Esto es, que
aunque una variable de tipo referencia deje de existir, el objeto al quel apunta no es eliminado si hay otras
referencias apuntando a ese objeto.

Programación en Java 16
2.1.2. Clases BigInteger y BigDecimal
Java 1.1 incorporó 2 nuevas clases destinadas a operaciones aritméticas que requieran gran precisión:
BigInteger y BigDecimal. La forma de operar con objetos de estas clases difiere de las operaciones con
variables primitivas. En este caso hay que realizar las operaciones con métodos propios de estas clases
(add para sumar, subtract, etc.). Se puede consultar la ayuda del package java.math, donde aparecen
ambas clases con todos sus métodos. Los objetos tipo BigInteger almacenan cualquier número entero sin
perder información durante las operaciones. Análogamente los objetos de tipo BigDecimal permiten trabajar
con el número de decimales deseado.
2.2. Operadores y precedencia
Java es un lenguaje rico en operadores. La tabla muestra los típicos.
Operadores Léxico Sintaxis Semántica
Aritméticos +, -, *, /, % op1 [operador] op2 Operaciones aritméticas habituales
= [variable] = [expresión] Asignar un valor a una variable
De asignación +=, -=, op1 [operador] op2 op1 = op1 [operador] op2
*=, /=, %=
Relacionales ==, >, < op1 [operador] op2 El resultado es true si se cumple que op1
es igual, mayor que… op2. En caso
>=, <=, !=
contrario es false
&&, & (AND) op1 [operador] op2 Si se usa &&, si op1 es false no se evalúa
op2. Si se usa &, op2 se evalúa siempre.
Lógicos ||, | (OR)
Si se usa ||, si op1 es true, no se evaúa
op2. Si se usa |, op2 se evalúa siempre
! (NOT) ! [expresión] Negación lógica.
Instanceof instanceof [objeto] instanceof [clase] Indica si un objeto pertenece a una clase
Condicional ?: ?: [expression] ? res1 : res2 Bifurcación condicional sencilla
Incrementales ++, -- ++[variable], [variable]++ Se incrementa o decrementa la variable
en una unidad
--[variable], [variable]--
Concatenación + [cadena]+[cadena] Concatena cadenas de caracteres
>>, << op1 [operador] op2 Desplaza los bits de op1 a la derecha
(izquierda) una distancia op2
>>> op1 [operador] op2 Desplaza los bits de op1 a la derecha una
De operación
distancia op2 (positiva)
de bits
&, |, ^ op1 [operador] op2 Operaciones lógicas AND, OR y XOR
~ ~ op1 Complemento de los bits de op1
De asignación &=, |=, ^=,
a bits <<=, >>=,
op1 [operador] op2 op1 = op1 [operador] op2
>>>=
Los operadores aritméticos + y – aplicados sólo a un operando, se denominan unarios y mantienen o
cambian el signo de un valor como en el cálculo matemático habitual.
Los operadores relacionales sirven para realizar comparaciones. El resultado es siempre un valor boolean
(true o false) según se cumpla o no la relación expresada.
Los operadores lógicos construyen expresiones lógicas, combinando valores lógicos o resultados de
operaciones relacionales. La semántica puede variar en el caso que se evalúe o no op2. Por eso se
incluyen los operadores & y | individuales.
El operador condicional ?:, tomado de C/C++ evalúa una expresión lógica y se devuelve res1 si el resultado
es true y res2 si es false. Es el único operador ternario (3 argumentos) de Java. Como todo operador que
devuelve un valor puede ser usado en una expresión. Por ejemplo las sentencias: x=1 ; y=10; z =
(x<y)?x+3:y+8; asignarían a z el valor 4 (x+3).

Programación en Java 17
Los operadores incrementales se aplican en preincremento, si se escriben antes de la variable, de forma
que primero se incrementa el valor y luego se usa o en postincremento, en que primero se usa el valor de la
variable y luego se incrementa (o decrementa). Su uso en contadores de bucles es muy frecuente.
Los operadores que actúan a nivel de bit, se postfix operators [] . (params) expr++ expr--
suelen usar para definir banderas o flags; variables
unary operators ++expr --expr +expr -expr ~ !
enteras en las que cada bit posee un significado.
En binario, las potencias de dos se representan creation or cast new (type)expr
con un único bit activado. P. ej., los números (1, 2,
4, 8, 16, 32, 64, 128) se representan en binario con multiplicative */%
8 bits como (00000001, 00000010, 00000100, additive +-
00001000,…, 10000000).
shift << >> >>>
La suma de estos números permite construir una
variable flags con los bits activados que se deseen. relational < > <= >= instanceof
Por ejemplo, para construir una variable flags equality == !=
00010010 se haría flags=2+16. Para saber si el
segundo bit por la derecha está o no activado se bitwise AND &
usaría la sentencia, if (flags & 2 == 2) {...}. bitwise exclusive OR ^
La precedencia de operadores marca el orden en bitwise inclusive OR |
que se realizan las operaciones, lo que condiciona
su resultado. La lista muestra la precedencia de logical AND &&
mayor a menor. En Java, todos los operadores logical OR ||
binarios, excepto los de asignación, se evalúan de
izquierda a derecha. Los de asignación se evalúan conditional ?:
de derecha a izquierda; el valor de la derecha se assignment =, +=, -=,…, >>=, >>>=
copia sobre la variable de la izquierda.
2.3. Estructuras de control
Las estructuras de control permiten gestional el orden de ejecución del proceso. En resumen, bifurcaciones
y bucles. En la mayoría de lenguajes son comunes aunque varíe su sintaxis. En Java es similar a C/C++.
Una sentencia es una instrucción que acaba en punto y coma. Se permite incluir varias sentencias en una
línea, aunque lo habitual es usar una línea por sentencia.
Existen 3 formas de introducir comentarios en código Java, similares a C++. Se usa // para introducir
comentarios en una línea de código. Un comentario puede empezar al comienzo de la línea o a
continuación de una instrucción. También se puede usar /*…*/, para comentarios de más de una línea. En
Java además se puede usar el símbolo /**…*/ con algunos caracteres especiales, para generar la
documentación sobre clases y packages automáticamente. Introducidos los comentarios, el programa
javadoc.exe genera automáticamente la información de forma similar a la de la documentación del JDK.
2.3.1. Bifurcaciones
Permiten controlar el flujo de ejecución Bifurcación if Bifurcación if-elseif-else
eligiendo una de entre varias acciones en
función del valor de una expresión lógica o if (booleanExpression) { if (booleanExpression1) {
relacional. Existen 2 tipos: if y switch. statements; statements1;
If ejecuta un bloque de sentencias según el } } elseif (booleanExpression2) {
valor de la expresión de control: se ejecuta si
la condición es cierta. Las llaves agrupan en Bifurcación if else statements2;
un bloque las sentencias a ejecutar. Si sólo if (booleanExpression) { } elseif (booleanExpression3) {
hay una sentencia no se exigen llaves.
statements1; statements3;
La bifurcación if else es análoga a if; es una
ampliación. Las sentencias incluidas en el } else { } else {
else se ejecutan en el caso de no cumplirse la statements2; statements4;
expresión de comparación.
} }
La bifurcación if-elseif-else permite introducir más de una expresión de comparación. Si la primera condición
no se cumple, se compara la segunda y así sucesivamente. En caso de que no se cumpla ninguna de las
comparaciones elseif, se ejecutan las sentencias del else. Su estructura es la mostrada en la tabla.
La bifurcación switch es alternativa a if-elseif-else cuando switch (expression) {
se compara la misma expresión con distintos valores. Su
case ‘value 1’: statements 1; break;

Programación en Java 18
forma general es la mostrada. Sus características más
case ‘value 2’: statements 2; break;
relevantes son las siguientes:

1. Cada sentencia case se corresponde con un único valor
de expression. No se pueden establecer rangos o case ‘value n’: statements n; break;
condiciones; sólo valores concretos.
[default: statements m;]
2. Los valores no comprendidos en ninguna sentencia case
}
se pueden gestionar en default, que es opcional.
3. En ausencia de break, cuando se ejecuta una sentencia case se ejecutan también las que van a
continuación, hasta llegar a un break o terminar el switch.
El cuadro muestra 2 ejemplos de uso de las sentencias if y switch.
int numero = 61; // "numero" tiene 2 dígitos char c = (char)(Math.random()*26+'a'); // Generación
aleatoria de letras minúsculas
if(Math.abs(numero) < 10)
System.out.println("Numero tiene 1 digito "); System.out.println("La letra " + c );
else if (Math.abs(numero) < 100) switch (c) {
System.out.println("Nº de 1 digito ");
case 'a': // Se compara con a
else {
case 'e': // Se compara con e
System.out.println("Nº de dígitos>3 ");
case 'i': // Se compara con i
System.out.println ("Ejecutada opcion por defecto
case 'o': // Se compara con o
");
case 'u': System.out.println(" Es vocal "); break;
}
default: System.out.println(" Es consonante ");
}
2.3.2. Bucles
Un bucle realiza un proceso repetidas veces. El código incluido entre llaves (opcionales si el proceso consta
de única línea) se ejecuta mientras se cumpla su condición. Por eso, hay que controlar que la condición se
da al menos una vez para salir del bucle.
2.3.2.1. While y Do while
El bucle while se realiza mientras sus condiciones sean ciertas. La tabla muestra su estructura.
El bucle do while es el mismo, pero el control Bucle while Bucle do while
se hace al final del bucle, por lo que el
bloque se ejecutará al menos una vez, se while (booleanExpression) { do {
cumpla o no la condición. Este tipo de bucle statements; statements
se usa para controlar la satisfacción de una
condición de error o convergencia. } } while (booleanExpression);

2.3.2.2. For
En el cuadro se Bucle For Equivalencia While
presenta la forma
general del bucle for y initialization;
su equivalencia con el for (initialization; booleanExpression; increment) { while (booleanExpression) {
bucle while. La
initialización se hace al statements; statements;
comienzo del for y el } increment;
incremento después.
}
La expresión booleana se evalúa al comienzo de cada iteración; el bucle termina cuando la expresión de
comparación no se cumple.
Cualquiera de las tres partes puede estar for(int i = 1, j = i + 10; i < 5; i++, j = 2*i) { i = 1 j = 11
vacía. La initialization y el increment
System.out.println(" i = " + i + " j = " + j); i=2 j=4
pueden tener varias expresiones
separadas por comas. Por ejemplo, el } i=3 j=6
código de la izquierda produce la salida
i=4 j=8
de la derecha.

Programación en Java 19
2.3.2.3. Break, continue y return
Break es válido para bifurcaciones y bucles. Evita realizar el resto de sentencia del bloque en ejecución.
Continue se usa en bucles, no en bifurcaciones. Finaliza la iteración en ejecución, evitando el resto de
sentencias hasta el final del bucle y vuelve a la siguiente iteración (i+1).
Java provee etiquetas para indicar un Bucle1: // etiqueta
lugar donde continuar la ejecución tras un
for ( int i = 0, j = 0; i < 100; i++){
break o continue. El único lugar donde se
pueden incluir es delante de un bloque de while ( true ) {
código entre llaves (if, switch, do...while,
if( (++j) > 5) { break bucle1; } // Finaliza ambos bucles
while, for) y sólo se usan al disponer uno
o más bucles (o bloques) en otro bucle y else { break; } // Finaliza el bucle interior (while)
se desea salir o continuar con la
siguiente iteración (continue) de un bucle }
que no es el actual. }
Por tanto, break [labelName] finaliza el bloque labelName. En el ejemplo, “break bucle1” finaliza los 2 bucles
pero break sólo termina el while y sigue la ejecución del for. Ambos bucles finalizan con i=5 y j=6. Continue,
dentro de al menos un bucle, también permite transferir el control a un bucle con etiqueta. Por ejemplo, la
sentencia, continue bucle1 pasaría a ejecutar el bucle for tras la etiqueta bucle1 para su nueva iteración.
Otra forma de salir de un bucle (y un método) es con la sentencia return. En caso que la función devuelva
alguna variable, el valor se escribe a continuación de return (return value).
2.3.3. Try, catch, finally
Java incorpora gestión de errores. La detección de errores sintácticos se hace en compilación. El resto se
dan en ejecución. En Java, una Exception es un tipo de error o condición anormal producida en ejecución.
Algunas excepciones son fatales y provocan que se deba finalizar la ejecución. Caso en que conviene
terminar ordenadamente y dar mensaje de error explicativo. Otras excepciones, como no encontrar un
fichero en el que leer o escribir, pueden ser recuperables. El programa debiera dar al usuario la oportunidad
de corregir el error (definiendo por ejemplo un nuevo path del fichero no encontrado).
Un programador chequea errores con la clase Exception (java.lang.Exception) que deriva de la Throwable.
Java obliga a tener en cuenta ciertas excepciones con bloques try, catch y finally. El código en el bloque try
está controlado. Si se produce una situación de error y una excepción, el control pasa al bloque catch, que
se hace cargo de la situación y decide lo que hacer. Se pueden incluir tantos bloques catch como se desee.
Cada uno trata un tipo de excepción. Si void metodo1() {
está presente, se ejecuta el bloque
...
finally, que aunque pcional, en caso de
existir se ejecuta siempre. En caso que try { // Código que puede lanzar las excepciones
el código de un método genere una
Exception y no se incluya en él la ...
gestión del error (bucles try/catch) el } catch (IOException e1) { // IOException y da aviso
método debe pasarla al método desde
el que ha sido llamado. System.out.println(e1.getMessage());

Esto se consigue con “throws” [nombre } catch (MyException e2) { // MyException, avisa y finaliza
de la Exception], después de la lista de System.out.println(e2.getMessage()); return;
argumentos del método. El método
superior debe incluir los bloques } finally { // Sentencias que se ejecutarán en todo caso
try/catch o volver a pasar la Exception. ...
Así se puede pasar la Exception de un
método a otro hasta llegar al último del }]
programa, el main(). ...
En el ejemplo se presentan 2 métodos } // Fin del metodo1
que controlan una IOException
relacionada con la lectura ficheros y void metodo2() throws IOException, MyException {
una MyException propia. El primero de ...
ellos (metodo1) realiza la gestión de las
excepciones y el segundo (metodo2) // Código que puede lanzar IOException y MyException
las pasa al siguiente método. } // Fin del metodo2

Programación en Java 20
3. CLASES EN JAVA
Las clases son la esencia de la POO. Los conceptos que maneja la POO son:
Clase. Es un tipo de datos abstracto (TDA) que agrupa datos (variables o campos) y de funciones (métodos)
que operan sobre esos datos.
Encapsulación. Las clases pueden ser declaradas como públicas (public) y como package (accesibles sólo
a clases del package). Las variables miembro y los métodos pueden ser public, private, protected y
package. De esta forma se puede controlar el acceso y evitar un uso inadecuado.
Herencia. Una clase puede derivar de otra (extends), y en ese caso hereda todas sus variables y métodos.
Una clase derivada puede añadir variables y métodos y/o redefinir las variables y métodos heredados.
Polimorfismo. Los objetos de distintas clases pertenecientes a una misma jerarquía o que implementan una
misma interface pueden tratarse de una forma general e individualizada, al mismo tiempo. Esto facilita la
programación y el mantenimiento del código.
La definición sigue la sintaxis del cuadro. La palabra public [public] class Classname {
es opcional. Si no se indica la clase adquiere visibilidad por
// definición de variables y métodos
defecto: sólo para las clases del package. Los métodos y
variables deben ser definidos en el bloque de la clase. Un ...
objeto (instance) es un ejemplar concreto de una clase. De
}
ahí, que clase se asocia a tipo de dato y objeto a variable.
Las clases como TDA (tipos abstractos de datos) son pensadas como un tipo de variable. Por ejemplo, un
tipo entero. Un objeto es pensado como una variable de un tipo de datos, una clase. Características de las
clases en Java son:
1. Todas las variables y métodos Java deben pertenecer a una clase. No hay variables y funciones globales
2. Si una clase deriva de otra (extends), hereda todas sus variables y métodos
3. Java tiene una jerarquía de clases estándar de la // fichero Circulo.java
que pueden derivar las clases que se creen
public class Circulo extends Geometria {
4. Una clase sólo puede heredar de una única clase
static int numCirculos = 0;
(no hay herencia múltiple). Si al definir una clase no
se especifica quién deriva, por defecto se deriva de public static final double PI=3.141592653589;
Object, clase base de la jerarquía de clases Java
public double x, y, r;
5. En un fichero se pueden definir varias clases,
public Circulo(double x, double y, double r) {
pero sólo una como public. El fichero, de extensión
*.java se debe llamar como esa clase public. Con this.x=x; this.y=y; this.r=r;
excepciones, lo habitual es escribir una sola clase
por fichero numCirculos++;

6. Si una clase de un fichero no es public, no es }


necesario que el fichero se llame como la clase public Circulo(double r) { this(0.0, 0.0, r); }
7. Los métodos de una clase pueden ser referidos public Circulo(Circulo c) { this(c.x, c.y, c.r); }
de modo global al objeto de esa clase al que se
aplican con la referencia this 8. Las clases pueden public Circulo() { this(0.0, 0.0, 1.0); }
agruparse en packages con la línea al comienzo del public double perimetro() { return 2.0 * PI * r; }
fichero package packageName;. La agrupación en
packages se relaciona con la jerarquía de directorios public double area() { return PI * r * r; }
y ficheros en que se guardan clases // método de objeto para comparar círculos
Otro concepto importante en Java, asociado a la public Circulo elMayor(Circulo c) {
idea de herencia múltiple es el de interface.
if (this.r>=c.r) return this; else return c;
Interface. Es un conjunto de declaraciones de
funciones. Si una clase implementa (implements) un }
interface, debe definir las funciones especificadas en // método de clase para comparar círculos
la interface.
public static Circulo elMayor(Circulo c, Circulo d) {
Las interfaces pueden definir variables finales
(constantes). Una clase puede implementar más de if (c.r>=d.r) return c; else return d;
un interface: es la alternativa a la herencia múltiple. }
En algunos aspectos los nombres de las interfaces
pueden usarse en lugar de las clases. } // fin de la clase Circulo

Programación en Java 21
Por ejemplo, las interfaces pueden definir referencias a objetos de cualquier clase que la implemente. Con
ese nombre sólo se pueden usar los métodos de la interface. Es un aspecto importante del polimorfismo.
Una interface puede derivar de otra o de varias, caso en que incorpora las declaraciones de todos los
métodos de las interfaces de las que deriva. Las interfaces Java, no las clases, sí tienen herencia múltiple.
El ejemplo define una clase Circulo y muestra cómo se definen variables miembro y métodos. Variables y
métodos pueden ser de objeto o de clase (static). El nombre del fichero coincide con el de la clase public
con la extensión *.java.
3.1. Variables
Un aspecto importante es la inicialización de datos. Las variables miembro de tipos primitivos se inicializan
automáticamente, incluso antes de llamar al constructor (false para boolean, carácter nulo para char y 0
para tipos numéricos). Lo suyo es inicializarlas también en el constructor.
Variables miembro (de objeto). Cada objeto tiene su copia de las variables miembro de la clase (también
llamadas campos) que podrán ser de tipo primitivo (boolean, int…) o referencias a objetos de otra clase
(composición). La clase Circulo tiene sus propias coordenadas del centro x e y, y su propio valor del radio r.
Las variables miembro pueden inicializarse en la declaración, con constantes o llamadas a métodos (algo
no permitido en C++). Se inicializan en el orden en que aparecen en el código de la clase. Es importante
porque unas variables pueden apoyarse en otras previas. Por ejemplo, long nDatos = 100;.
Los métodos de objeto se referencian con el nombre del objeto, punto, nombre del método. A este objeto se
le llama argumento implícito. Para calcular el área del objeto c1 de la clase Circulo, se escribe: c1.area();.
Las variables miembro del argumento implícito se acceden directamente o con this y el punto.
Las variables miembro pueden precederse en su declaración por los modificadores de acceso: public,
private, protected y package (valor por defecto que puede omitirse). Junto a los modificadores de acceso de
la clase (public y package), determinan los permisos para usar la clase y sus métodos y variables miembro.
Existen otros 2 modificadores, no de acceso, para las variables miembro:
Transient. Indica que la variable miembro no forma parte de la persistencia de un objeto (capacidad de
mantener su valor al terminar la ejecución) y por tanto no debe ser serializada (convertida en flujo de
caracteres para poder almacenarse en disco o en BBDD) con el resto del objeto.
Volatile. Indica que la variable puede ser usada por distintos threads sincronizados y que el compilador no
debe realizar optimizaciones con esa variable.
Variables miembro (de clase o static). Una clase puede tener variables propias, pero no de cada objeto. A
estas variables se les llama de clase o static. Se suelen usar para definir constantes comunes o variables
con sentido para toda la clase. En Java son lo más parecido a variables globales de C/C++.
Se crean anteponiendo “static” a su declaración. Para llamarlas se suele usar el nombre de la clase (no es
imprescindible, pues se puede usar el nombre de cualquier objeto), para aclarar su sentido. P. ej.,
Circulo.numCirculos es una variable de clase que cuenta el número de círculos creados. Si no se les da
valor en la declaración, las variables miembro static se inicializan con los valores por defecto para los tipos
primitivos y con null si es una referencia.
Las variables static se crean en el momento en que pueden ser necesarias: al crear el primer objeto de la
clase, al llamar a un método static o al usar una variable static de dicha clase. Lo importante es que las
variables static se inicializan siempre antes que cualquier objeto de la clase.
Variables finales. Una variable de un tipo primitivo declarada como final no puede cambiar su valor en
ejecución. Puede ser considerada como una constante equivliendo a la palabra const de C/C++.
Java permite separar definición e inicialización de una variable final. La inicialización puede hacerse en
tiempo de ejecución, llamando a métodos o en función de otros datos. La variable final así definida es
constante, pero su valor puede variar entre ejecuciones del programa, pues depende de cómo se inicialice.
Además de las variables miembro, las locales y los argumentos de un método pueden ser declarados final.
Declarar como final un objeto miembro de una clase hace constante la referencia, pero no el objeto, que
puede ser modificado con otra referencia. En Java no es posible hacer que un objeto sea constante.
3.2. Métodos (funciones miembro)
3.2.1. Métodos de objeto
Los métodos son subrutinas definidas en una clase. Salvo los static o de clase, se aplican siempre a un
objeto con el operador punto. El objeto es su argumento implícito. Los métodos pueden además tener otros
argumentos explícitos entre paréntesis en forma habitual. La primera línea de la definición de un método se
llama declaración o header; el código comprendido entre las llaves {…} es el cuerpo del método.

Programación en Java 22
Sea el método de la clase Circulo mostrado. El header contiene el // header y comienzo del método
cualificador de acceso (public), del tipo del valor de retorno (Circulo;
public Circulo elMayor(Circulo c) {
void si no tiene), el nombre del mismo y una lista de argumentos
explícitos entre paréntesis, separados por comas. Si no hay if (this.r>=c.r) // body
argumentos explícitos los paréntesis se dejan vacíos.
return this; // body
Los métodos tienen visibilidad directa de las variables miembro del
else // body
objeto (el argumento implícito). Pueden acceder a ellas sin
referenciarlas. También se pueden referenciar con this, de modo return c; // body
discrecional (this.r) o si alguna variable local o argumento las oculta.
El valor de retorno puede ser de un tipo primitivo o una referencia. } // fin del método

En cualquier caso no puede haber más que un único valor de retorno (podrá ser un objeto o array). Se
puede devolver también una referencia a un objeto con un nombre de interface. El objeto devuelto debe
pertenecer a una clase que implemente el interface. Se puede devolver como valor de retorno un objeto de
la clase del método o de una sub-clase, pero nunca de una super-clase.
Los métodos pueden definir variables locales. Su visibilidad (ámbito) es desde la definición al final del
bloque en el que se defininen. No hace falta inicializar las variables locales en el punto en que se definen,
pero el compilador no permite usarlas sin inicializarlas. A diferencia de las variables miembro, las variables
locales no se inicializan por defecto.
Native. Si un método se declara native (Ej: public native void miMetodo();) no hay que incluir su
implementación. El código estará en una biblioteca dinámica (DLL), ficheros de funciones compiladas
normalmente en lenguajes distintos a Java. Es la forma de usar funciones de otros lenguajes desde Java.
Synchronized. Un método declarado así (Ej: public synchronized double miMetodoSynch(){...}) especifica
que sobre un objeto no pueden ejecutarse simultáneamente 2 métodos sincronizados.
3.2.1.1. Sobrecarga de métodos
Java permite sobrecarga de métodos (overloaded). Son métodos distintos con el mismo nombre, que se
diferencian por el número y/o tipo de argumentos. El ejemplo de la clase Circulo hay 2 casos de métodos
sobrecargados: los 4 constructores y los 2 métodos llamados elMayor(). A la hora de llamar a un método
sobrecargado, Java identifica el correcto con los criterios:
1. Si existe el método cuyos argumentos se ajustan exactamente al tipo de los argumentos de la llamada
(argumentos actuales), se llama a ese método
2. Si no existe un método que se ajuste exactamente, se intenta promover los argumentos actuales al tipo
inmediatamente superior (por ejemplo char a int, int a long, etc.) y se llama el método correspondiente
3. Si sólo existen métodos con argumentos de un tipo más restringido (p. ej. int en vez de long), el
programador debe hacer un cast explícito en la llamada, responsabilizándose de la ejecución
4. El valor de retorno no influye en la elección del método sobrecargado. Es imposible saber desde el
método lo que se hará con él. No es posible crear 2 métodos sobrecargados que difieran en el valor de
retorno
Diferente de la sobrecarga de métodos es la redefinición. Una clase puede redefinir (override) un método
heredado de una superclase. Esto es darle una nueva definición. En este caso el método debe tener
exactamente los mismos argumentos en tipo y número que el método redefinido.
3.2.1.2. Paso de argumentos a métodos
En Java los argumentos de los tipos primitivos se pasan siempre por valor. El argumento que se pasa no se
modifica. La forma de modificar una variable de tipo primitivo es incluirla como variable miembro en una
clase y pasar como argumento una referencia a un objeto de dicha clase.
Las referencias se pasan también por valor, pero permiten modificar los objetos referenciados. En Java no
se pueden pasar métodos como argumentos a otros métodos (en C/C++ se pueden pasar punteros a
métodos). Lo que se puede hacer en Java es pasar una referencia a un objeto y usar sus métodos. En un
método se pueden crear variables locales de los tipos primitivos o referencias. Estas variables locales dejan
de existir al terminar la ejecución del método.
Los argumentos formales de un método (las variables del header) tienen categoría de variables locales del
método. Si un método devuelve this (un objeto de la clase) o una referencia a otro objeto, ese objeto puede
encadenarse con otra llamada a otro método de la misma o diferente clase y así sucesivamente. En este
caso aparecerán varios métodos en la misma sentencia unidos por el operador punto.

Programación en Java 23
El ejemplo muestra el uso del método String numeroComoString = ”8.978”;
valueOf(String) de la clase java.lang. Float.
float p = Float.valueOf(numeroComoString).floatValue();
El método valueOf(String) devuelve un objeto de la clase Float sobre el que se aplica el método floatValue(),
que devuelve una variable primitiva de tipo float. El ejemplo anterior se podía desdoblar en las sentencias:
Así se pueden encadenar llamadas a métodos String numeroComoString = ”8.978”;
con el operador punto que, como todos los
Float f = Float.valueOf(numeroComoString);
operadores de Java excepto los de
asignación, se ejecuta de izquierda a derecha. float p = f.floatValue();
3.2.2. Métodos de clase (static)
Los métodos que no actúan sobre objetos con el operador punto se les llama métodos de clase o static.
Éstos pueden recibir objetos de su clase como argumentos explícitos, pero no tienen argumento implícito ni
pueden usar la referencia this.
Ejemplo típico de métodos static son los de la clase java.lang.Math (sin(), cos(), exp(), etc.). Usualmente el
argumento de estos métodos será de tipo primitivo y se pasará como explícito. Estos métodos no tienen
sentido como métodos de objeto, serán static. Para llamarlos se suele usar el nombre de la clase, en vez
del nombre de un objeto de la clase (por ejemplo, Math.sin(ang).
En POO se evita información incorrecta al no inicializar variables. Java no permite que haya variables
miembro no inicializadas. Java inicializa siempre con valores por defecto. El segundo requisito de correcta
inicialización de objetos es el uso de constructores.
Un constructor es un método llamado automáticamente al crear un objeto. Su misión es reservar memoria e
inicializar las variables miembro de la clase. No tienen valor de retorno y su nombre coincide con el de la
clase. Su argumento implícito es el objeto que se crea. Una clase suele tener varios constructores, que se
diferencian por el tipo y número de sus argumentos (ejemplo típico de métodos sobrecargados). Se llama
constructor por defecto al constructor que no tiene argumentos. El programador debe proporcionar en el
código valores iniciales adecuados para todas las variables miembro.
Un constructor de una clase puede llamar, con this, a otro constructor previo definido en la misma clase. En
este contexto, this sólo puede aparecer en la primera sentencia de un constructor. El constructor de una
sub-clase puede llamar al constructor de su super-clase con “super” con los argumentos apropiados. Así, un
constructor sólo inicializa las variables no heredadas.
Si no se define constructor para una clase, el compilador crea un constructor por defecto, inicializando las
variables al valor por defecto y las referencias a null. Si es necesario, se llama al constructor de la super-
clase para inicializar las variables heredadas. Los constructores también pueden ser public, private,
protected y package. Si es private, ninguna otra clase puede crear un objeto de esa clase. En este caso,
pueden disponerse métodos public y static (factory methods) que llamen al constructor y devuelvan un
objeto de esa clase. En una clase, los constructores sólo pueden ser llamados por otros constructores o
métodos static. No pueden ser llamados por métodos de objetos.
Java dispone una tercera vía para evitar variables sin inicializar: los inicializadores, que pueden ser static
(para la clase) o de objeto.
Inicializador static. Es un recurso similar a un método que se llama automáticamente al crear la clase. Se
diferencia del constructor en que no es llamado para cada objeto, sino una sola vez para toda la clase.
Los tipos primitivos pueden inicializarse con asignaciones en la clase o el constructor, pero para inicializar
objetos o elementos más complejos se puede usar inicializadores, ya que permiten gestionar excepciones.
Los inicializadores static se crean en la clase como métodos static{
sin nombre, sin argumentos y sin valor de retorno, precedidos
System.loadLibrary("MyNativeLibrary");
de static y el código entre llaves {...}, como en el ejemplo
mostrado a la derecha. }
En una clase pueden definirse varios inicializadores static, que se llamarán en el orden en que han sido
definidos. Los inicializadores static se pueden usar para dar valor a variables static. Además se suelen usar
para llamar a métodos nativos, escritos en otros lenguajes (llamando a los métodos System.load() o
System.loadLibrary(), que leen las bibliotecas nativas).
Existen también inicializadores de objeto, que no llevan la palabra static. Se usan para clases anónimas,
que por no tener nombre no tienen constructor. En este caso, los inicializadores de objeto se llaman cada
vez que se crea un objeto de la clase anónima.

Programación en Java 24
3.3. Objetos. Creación, destrucción y finalizadores
El proceso de creación de un objeto es:
1. Al crear el primer objeto de la clase o usar el primer método o variable static se localiza la clase y se
carga en memoria
2. Se ejecutan los inicializadores static (sólo una vez)
3. Cada vez que se quiere crear un nuevo objeto:
se reserva la memoria necesaria
se da valor por defecto a las variables miembro de los tipos primitivos
se ejecutan los inicializadores de objeto
se ejecutan los constructores
Java no dispone destructores como en C++. El sistema libera memoria automáticamente de los objetos que
han perdido la referencia, que no tienen nombre que permita accederlos, por ejemplo al llegado al final del
bloque en el que se definieron, porque a la referencia se le ha asignado null o porque a la referencia se le
ha asignado la dirección de otro objeto. Esta característica Java se conoce como garbage collection.
Es normal que distintas variables de tipo referencia apunten al mismo objeto. Java internamente cuenta las
referencias a cada objeto. El objeto podrá ser borrado si el número de referencias es 0. No se sabe
exactamente cuándo actuará el garbage collector. Si no falta memoria es posible que no se active. No es
conveniente confiarle tareas más críticas, por lo que se le puede llamar explícitamente con el método
System.gc(), pero el sistema lo considera como una sugerencia a la JVM.
Los finalizadores son métodos que completan la labor del garbage collector. Se llaman automáticamente al
destruir un objeto (antes que el sistema libere memoria). Se usan para operaciones de terminación distintas
a la liberación de memoria (p. ej. cerrar ficheros, conexiones de red, etc.). El garbage collector sólo libera
memoria reservada con new. Si se ha reservado memoria con funciones nativas en C (por ejemplo malloc),
esa memoria hay que liberarla explícitamente con finalize().
Un finalizador es un método de objeto (no static), sin valor de retorno (void), ni argumentos y que siempre
se llama finalize. Se llaman automáticamente si han sido definidos en la clase. Para su tarea un finalizador
debería terminar siempre llamando al finalizador de su super-clase. Tampoco se puede saber el momento
preciso en que los finalizadores van a ser llamados. En ocasiones será conveniente que el programador
realice esas operaciones de finalización de modo explícito mediante otros métodos.
El método System.runFinalization() sugiere a la JVM que ejecute los finalizadores de los objetos pendientes
(que han perdido referencia).
3.4. Packages
Un package es una agrupación de clases. El usuario puede crear sus propios packages. Para que una clase
forme parte de un package, se sigue la sintaxis: package [nombre del package]. Debe ser la primera
sentencia del fichero sin contar comentarios ni líneas en blanco. Los nombres de los packages se suelen
escribir en minúsculas, para distinguirlos de las clases, en mayúsculas.
Las clases de un package deben residir en el mismo directorio. Por eso, el nombre de un package puede
constar de varias palabras separadas por puntos, en relación a la jerarquía de directorios en que están las
clases. Es recomendable que los nombres de las clases Java sean únicos en Internet. El package facilita
esto, ya que se puede incluir el nombre del dominio, por ejemplo: es.cau.vgon.reserv.ord.
Las clases de un package se disponen en un directorio con el path del package. Por ejemplo, la clase,
es.cau.vgon.reserv.ord.Bubble.class estaría en CLASSPATH\es\cau\vgon\reserv\ord\, siendo CLASSPATH
una variable de entorno del PC que establece la posición absoluta de los directorios en los que hay clases
Java (de sistema o usuario), en este caso, los discos locales. Los packages poseen los objetivos de:
1. Agrupar clases relacionadas.
2. Evitar conflictos de nombres (el dominio de nombres Java es Internet). En caso de conflicto de nombres
entre clases importadas, el compilador obliga a cualificar en el código los nombres de dichas clases con el
nombre del package.
3. Ayudar en el control de la accesibilidad de clases y miembros.
Con la sentencia import packname; se puede evitar usar nombres largos y colisión de nombres. Si se da
colisión de nombres de clases, Java da un error y obliga a usar los nombres de las clases cualificados con
el nombre del package.

Programación en Java 25
Importar un package no hace que se carguen todas las clases del package: sólo las clases public a usar. Al
importar un package no se importan los sub-packages: deben importarse explícitamente, pues en realidad
son packages distintos. P. ej., al importar java.awt no se importa java.awt.event. Es posible guardar en
jerarquías de directorios diferentes los ficheros *.class y *.java, con objeto p. ej. de no mostrar la situación
del código fuente. Los packages hacen referencia a los ficheros compilados *.class.
En un programa Java, una clase puede ser referida con su nombre completo (nombre del package más el
de la clase, separados por punto), del mismo modo que sus variables y métodos. Proceder así esengorroso
y hace más difícil reutilizar código y portarlo. La sentencia import permite abreviar los nombres de las
clases, variables y métodos, evitando escribir el nombre del package. Se importan por defecto el package
java.lang y el package actual (clases del directorio actual). Existen 2 formas de usar import: para clase y
para package:
import es.cau.vgon.reserv.ord.Bubble.class; import es.cau.vgon.reserv.ord.*;
que deberían estar en el directorio es.cau.vgon.reserv.ord.
3.5. Herencia
La herencia permite definir una clase a partir de otra. Para indicarlo se usa la palabra extends. Ejemplo:
class CirculoGrafico extends Circulo {...}. Si una clase deriva de otra, hereda variables y métodos, que
podrán redefinirse y añadirse nuevos. Es similar a que la clase derivada contuviera un objeto de la
superclase; la amplía con nuevos atributos y métodos. Java permite múltiples niveles de herencia, pero no
herencia múltiple. Dada una clase, se pueden derivar tantas como se desee.
Las clases Java creadas tienen una superclase. Si no se indica con extends, se derivan de java.lang.Object,
la clase raíz de la jerarquía Java. Así todas las clases tienen métodos heredados de Object. La composición
(una clase contiene un objeto de otra clase) se diferencia de la herencia en que incorpora los datos del
objeto miembro, pero no sus métodos o interface si se hace private. La clase Object tiene métodos
interesantes que hereda cualquier objeto. Entre ellos:
Métodos que pueden ser redefinidos.
clone(). Crea un objeto a partir de otro de la misma clase. El método original heredado de Object
lanza una CloneNotSupportedException. Si se desea clonar una clase hay que implementar el
interface Cloneable y redefinir el método clone(). Este método debe hacer una copia miembro a
miembro del objeto original. No debería llamar al operador new ni a constructores.
equals(). Indica si 2 objetos son iguales o no. Devuelve true si lo son tanto si son referencias al
mismo objeto como si son objetos distintos con iguales valores de las variables miembro.
toString(). Devuelve un String que contiene una representación del objeto como cadena de
caracteres, p. ej. para imprimirlo o exportarlo.
finalize(). Ya visto.
Métodos que no pueden ser redefinidos (métodos final).
getClass(). Devuelve un objeto de la clase Class, al cual se pueden aplicar métodos para determinar
el nombre de la clase, su super-clase, las interfaces implementadas, etc. Se puede crear un objeto
de la misma clase que otro sin saber de qué clase es.
notify(), notifyAll() y wait(). Métodos relacionados con hilos (threads).
Una clase puede redefinir los métodos heredados de su super-clase que no sean final. El método redefinido
en la clase sustituye funcionalmente al heredado. Pueden ser accedidos con la palabra super desde la clase
derivada, aunque sólo se puede invocar el nivel inmediatamente superior. También pueden ampliar los
derechos de acceso de la super-clase (p. ej. ser public, en vez de protected o package), pero nunca
restringirlos. Los métodos de clase o static no pueden ser redefinidos en las clases derivadas.
3.5.1. Clases y métodos abstractos. Constructores en clases derivadas
Una clase abstracta (abstract) es aquella que no permite crear objetos. Permite que otras clases deriven de
ella, proporcionándoles un arquetipo o patrón a seguir y métodos de utilidad general. Las clases abstractas
se declaran anteponiéndo la palabra abstract. P. ej. public abstract class Geometria { ... }. Una clase
abstract puede tener métodos definidos como abstract, en cuyo caso no se implementan.
Si una clase tiene métodos abstract es obligatorio que sea abstract. En cualquier clase derivada ese método
debe ser redefinido, o volver a declararse abstract (método y sub-clase). Una clase abstract puede tener
métodos no abstract. Aunque no se puedan crear objetos de esa clase, sus derivadas heredarán el método
para ser usado. Como los métodos static no pueden ser redefinidos, un método abstract no puede ser static.

Programación en Java 26
Un constructor de una clase puede llamar con this a otro previamente definido en la misma clase. Así, this
sólo puede aparecer en la primera sentencia de un constructor. Análogamente el constructor de una clase
derivada puede llamar al constructor de su super-clase con super(), con los argumentos apropiados. De
esta forma, un constructor sólo tiene que inicializar directamente las variables no heredadas.
La sentencia de llamada al constructor de la superclase debe ser la primera, excepto si se llama a otro
constructor de la misma clase con this(). Si no se incluye, Java hace automáticamente una llamada al
constructor por defecto de la superclase (super). Esta llamada encadena a los constructores de las
superclases hasta el origen de la jerarquía de clases, esto es hasta el constructor de Object.
Si no se prepara un constructor por defecto, el compilador crea uno, inicializando las variables de los tipos
primitivos a valores por defecto y strings y demás referencias a objetos a null. Antes, incluirá una llamada al
constructor de la superclase. En el proceso de finalización o liberación de recursos (diferentes de memoria
reservada con new, de la que se encarga el garbage collector) es importante llamar a los finalizadores de
las distintas clases, normalmente en orden inverso al de llamada de los constructores. Esto hace que el
finalizador de la subclase realice sus tareas primero y luego llame al finalizador de la superclase en la forma
super.finalize(). Los métodos finalize() deben ser al menos protected, ya que el método finalize() de Object
lo es, y no está permitido reducir los permisos de acceso en la herencia.
Las variables declaradas como final no pueden cambiar su valor una vez inicializadas. Existen otros 2 usos
de la palabra final. Una clase declarada final no puede tener clases derivadas. Esto se puede hacerse por
motivos de seguridad o de eficiencia, ya que si el compilador sabe que los métodos no van a ser redefinidos
puede hacer optimizaciones adicionales. Análogamente, un método declarado como final no puede ser
redefinido por una clase que derive de su propia clase.
3.5.2. Interfaces
Una interface es un conjunto de declaraciones de métodos sin definición. También puede definir constantes,
que son implícitamente public, static y final y siempre se inicializan en la declaración. Estos métodos definen
un tipo de conducta. Todas las clases que implementan un interface dado están obligadas a proporcionar
una definición de sus métodos adquiriendo una conducta particular. Una clase puede implementar varias
interfaces. Para indicarlo se ponen los nombres de las interfaces, separados por comas, detrás de la
palabra implements, que a su vez va siempre a la derecha del nombre de la clase o del nombre de la
superclase en el caso de herencia, como en el ejemplo.
Una interface y una clase abstract pueden contener varias public class CirculoGrafico extends Circulo
declaraciones de métodos (la clase abstract puede además
implements Dibujable, Cloneable {
definirlos). Esto permite, a veces, que se pueda optar por
implementar el código con cualquiera de ambas opciones. ...
Pero, existen también diferencias importantes: }
1. Una clase no puede heredar de 2 clases abstract, pero sí puede heredar de una clase abstract e
implementar una interface, o bien implementar 2 o más interfaces
2. Una clase no puede heredar métodos (definidos) de una interface, aunque sí constantes
3. Las interfaces permiten más flexibilidad para conseguir que 2 clases se comporten igual, de forma
independiente de su situación en la jerarquía de clases de Java
4. Las interfaces permiten “publicar” el comportamiento de una clase desvelando un mínimo de información
5. Las interfaces tienen una jerarquía propia, independiente y más flexible que la de las clases, ya que
permiten herencia múltiple
6. En polimorfismo las referencias de un tipo interface se pueden usar de modo similar a las clases abstract
Una interface se define de modo similar a las clases. En el cuadro se muestra la definición de la interface
Dibujable presentada anteriormente. Cada interface public debe ser definida en un fichero *.java con el
mismo nombre de la interface. Los nombres de las interfaces suelen comenzar también con mayúscula.
Las interfaces no admiten más que los modificadores de // fichero Dibujable.java
acceso public y package. Si la interface no es public no será
import java.awt.Graphics;
accesible desde fuera del package (tendrá accesibilidad por
defecto; package). Los métodos declarados en una interface public interface Dibujable {
son siempre public y abstract, de modo implícito. Entre las
interfaces existe una jerarquía (independiente de la de las public void setPosicion(double x, double y);
clases) que permite herencia simple y múltiple. Cuando una public void dibujar(Graphics dw);
interface deriva de otra, incluye todas sus constantes y
declaraciones de métodos. }

Programación en Java 27
Una interface puede derivar de varias interfaces. Para la herencia de interfaces se usa la palabra extends,
seguida del nombre de las interfaces de las que deriva, separadas por comas. Una interface puede ocultar
una constante definida en una superinterface definiendo otra constante con el mismo nombre. De la misma
forma puede ocultar, redeclarándolo, un método heredado de una superinterface.
Las interfaces no deberían ser modificadas más que en caso de extrema necesidad. Si se modifican, por
ejemplo añadiendo alguna nueva declaración de un método, las clases que hayan implementado dicha
interface dejarán de funcionar, a menos que lo implementen.
Las constantes definidas en una interface se pueden usar en cualquier clase aunque no la implemente
precediéndolas del nombre de la interface (P.ej.: area = 2.0*Dibujable.PI*r;). En las clases que implementan
la interface las constantes se pueden usar directamente, como las constantes de la clase. A veces se crean
interfaces para agrupar constantes simbólicas relacionadas (similar al enum de C/C++).
En relación al polimorfismo, el nombre de un interface se puede usar como un nuevo tipo de referencia. El
nombre de un interface puede ser usado en lugar del nombre de cualquier clase que la implemente, aunque
su uso estará restringido a los métodos del interface. Un objeto de ese tipo puede también usarse como
valor de retorno o argumento de un método.
3.7. Clases internas
Una clase interna es la definida dentro de otra clase, llamada contenedora, en alguna variante como
muestra el cuadro, en su forma general. Además de su utilidad en sí, las clases internas se usan mucho en
el modelo de eventos. Hay cuatro tipos de clases internas:
1. Clases internas static. class ClaseContenedora {
2. Clases internas miembro. ...
3. Clases internas locales. class ClaseInterna {
4. Clases anónimas. ...
El término clase contenedora o global referencia la clase que contiene }
a la clase interna. La JVM no entiende de clases internas. Por ello, el
...
compilador convierte estas clases en globales, contenidas en ficheros
*.class cuyo nombre es ClaseContenedora$ClaseInterna.class. }
Esta conversión inserta variables ocultas, métodos y argumentos en los constructores. Lo más importante
se refiere al nombre de los ficheros que aparecen en el directorio donde se realiza la compilación, que
pueden resultar sorprendentes si no se conoce su origen.
3.7.1. Clases e interfaces internas static
También conocidas como nested classes (clases anidadas). Sólo pueden ser creadas dentro de otra clase
al máximo nivel, en el bloque de definición de la clase contenedora; no en un bloque más interno. Es posible
definirlas en una interface contenedora. Este tipo de clases internas se definen con “static”. Las interfaces
internas son implícitamente static. Para usar su nombre desde fuera de la clase contenedora hay que
precederlo por el nombre de la clase contenedora y el punto. Este tipo de relación entre clases permite
agrupar varias clases en una clase más general. Lo mismo para interfaces internas.
Las clases internas static pueden ver y usar los miembros static de la clase contenedora. No se necesitan
objetos de la clase contenedora para crear objetos de la clase interna static. Los métodos de la clase interna
static no pueden acceder directamente a los objetos de la clase contenedora, caso de que los haya: deben
disponer de una referencia a dichos objetos, como cualquier otra clase.
La sentencia import puede usarse para importar una clase interna ... implements List.Linkable
static, como si se importara una clase de un package (con el punto).
Para importarla se usa:
Por ejemplo, si la interface Linkable es interna a la clase List, para
implementar dicha interface se expresaría como en el cuadro. import List.*;
Otras características importantes son: o bien import List.Linkable;
1. Clases e interfaces internas pueden definirse en interfaces y clases contenedoras
2. Multinivel: una clase interna static puede ser clase contenedora de otra clase interna static, etc.
3. Clases e interfaces internas static pertenecen al package de la clase contenedora
4. Pueden usarse los calificadores final, public, private y protected. Ésta es una forma más de controlar el
acceso a ciertas clases. A continuación se presenta un ejemplo de clase interna static.

Programación en Java 28
// fichero ClasesIntStatic.java class ClasesIntStatic {
class A { public static void main(String [] arg) {
int i=1; // variable miembro de objeto A a1 = new A(11), a2 = new A(12);
static int is=-1; // variable miembro de clase println("a1.i=" + a1.i + " a2.i=" + a2.i);
public A(int i) {this.i=i;} // constructor // 2 formas de crear objetos de Bs
public void printA(Bs unBs) { A.Bs b1 = new A.Bs(-10); // obligatorio A.Bs
// al método printA hay que pasarle la referencia a A.Bs b2 = a1.new Bs(-11); // b2 independiente de a1
// los objetos de la clase interna static // referencia directa a los objetos b1 y b2
System.out.println("i="+i+" unBs.j="+unBs.j); println("b1.j=" + b1.j + " b2.j=" + b2.j);
}
// definición de clase interna static // los métodos de la clase Bs acceden directamente
// a las variables de la clase A sólo si son static
static class Bs {
b1.printBs(); // escribe: j=-10 is=-1
int j=2;
b2.printBs(); // escribe: j=-20 is=-1
public Bs(int j) {this.j=j;}
// a los métodos de A hay que pasarles referencias
// los métodos de la clase Bs no acceden a la i;
// a los objetos de Bs, para identificarlos
// es una variable de objeto. Sí acceden a is
a1.printA(b1); // escribe: i=11 unBs.j=-10
public void printBs() {
a1.printA(b2); // escribe: i=11 unBs.j=-11
System.out.println(" j=" + j + " is=" + is);
} // main
}
public static void println(String str)
} // fin clase Bs
{System.out.println(str);}
} // fin clase contenedora A
} // ClasesIntStatic
3.7.2. Clases internas miembro (no static)
También llamadas clases internas, sin más, son las definidas al máximo nivel de la clase contenedora, sin
“static”. No existen interfaces internas de este tipo y no pueden tener variables static. Característica es que
cada objeto de la clase interna existe siempre dentro de sólo un objeto de la clase contenedora. Pero un
objeto de la clase contenedora puede relacionarse con varios objetos de la clase interna. Esto es importante
para estudiar la relación entre clases interna y contenedora respecto al acceso a las variables miembro:
1. Debido a la relación uno a uno, los métodos de la clase interna ven directamente las variables miembro
del objeto de la clase contenedora, sin necesidad de cualificarlos
2. Los métodos de la clase contenedora no ven directamente las variables miembro de los objetos internos:
necesitan cualificarlos con la referencia correspondiente. Esto es debido a la relación uno a varios.
3. Clases distintas a la contenedora e interna pueden usar directamente los objetos de la clase interna, sin
cualificarlos con el objeto o nombre de la clase contenedora. Se puede acceder a los objetos de la clase
interna aunque se pierda la referencia al objeto de la clase contenedora al que están asociados.
En cuanto al acceso:
1. Las clases internas pueden ser private y protected (las normales sólo pueden ser public y package),
permitiendo nuevas posibilidades de encapsulación
2. Los métodos de las clases internas acceden a todos los miembros, incluso private, de la contenedora
3. La clase contenedora puede acceder (si dispone de una referencia) a todas las variables miembro
(incluso private) de sus clases internas
4. Una clase interna puede acceder a los miembros (incluso private) de otras clases internas definidas en la
misma clase contenedora
Otras características de las clases internas:
1. Una clase interna miembro puede contener otra clase interna miembro, hasta el nivel que se desee (no
son recomendables muchos niveles)

Programación en Java 29
2. En la clase interna, la palabra this se refiere a su objeto. Para acceder al objeto de la contenedora se usa
ClaseContenedora.this
3. Para crear un nuevo objeto de la clase interna se puede usar new, precedido de la referencia al objeto de
la clase contenedora que contendrá el nuevo objeto: unObjCC.new(). El tipo del objeto es el nombre de la
clase contenedora seguido del nombre de la clase interna, por ejemplo: ClaseCont.ClaseInt unObjClInt =
unObjClaCont.new ClaseInt(...);
4. Sea B una clase interna de A y A a = new A(); // se crea un objeto de la clase A
C una clase interna de B. Un
A.B b = a.new B(); // b es un objeto de B dentro de a
ejemplo de creación de objetos
de las 3 clases sería: A.B.C c = b.new C(); // c es un objeto de C dentro de b
5. No se puede crear un objeto de la clase interna sin referencia a un objeto de la contenedora. Los
constructores de la clase interna tienen como argumento oculto una referencia al objeto de la contenedora
6. La palabra super ahora entiende que una clase derivada de una interna, su constructor no puede llamar a
super directamente. El compilador no puede crear un constructor por defecto. Al constructor hay que pasarle
una referencia a la contenedora de la clase interna superclase y con esa referencia ref llamar a ref.super()
Las clases internas pueden derivar de clases distintas a la contenedora. Caso en que:
1. Las clases internas constituyen una especie de segunda jerarquía de clases Java. Pertenecen a la clase
contenedora y ven sus variables. Y al derivar de otra clase distinta a la contenedora deben evitar conflictos
con los nombres. En caso de conflicto entre un nombre heredado y uno de la clase contenedora, el nombre
heredado tiene prioridad
2. En caso de conflicto de nombres, Java obliga a usar this (this.name) para referirse a la variable o método
heredado y para el miembro de la clase contenedora NombreClaseCont.this.name
3. Si una clase contenedora deriva de una superclase con una clase interna, la interna de la subclase puede
a su vez derivar de la clase interna de la superclase y redefinir los métodos que necesite
El uso de las clases internas miembro tiene las siguientes restricciones:
1. Las clases internas no pueden tener el mismo nombre que la contenedora o package
2. Las clases internas no pueden tener miembros static: variables, métodos o clases
El cuadro muestra un ejemplo de uso de clases internas miembro.
// fichero ClasesInternas.java class ClasesInternas {
class A { // clase contenedora public static void main(String [] arg) {
int i=1; // variable miembro A a1 = new A(11); A a2 = new A(12);
public A(int i) {this.i=i;} // constructor println("a1.i=" + a1.i + " a2.i=" + a2.i);
// los métoodos de la contenedora necesitan A.B b1 = a1.new B(-10), b2 = a1.new B(-20);
// referencia a los objetos de la clase interna println("b1.j=" + b1.j + " b2.j=" + b2.j);
public void printA(B unB) { // los métodos de la clase interna pueden acceder a
// las variables del objeto de la contenedora
System.out.println("i="+i+" unB.j="+unB.j);
b1.printB(); // escribe: i=11 j=-10
// sí acepta unB.j
b2.printB(); // escribe: i=11 j=-20
}
a1.printA(b1); a1.printA(b2);
protected class B { // Clase interna
A a3 = new A(13); A.B b3 = a3.new B(-30);
int j=2;
println("b3.j=" + b3.j);
public B(int j) {this.j=j;} // constructor
a3 = null; b3.printB(); // escribe: i=13 j=-30
public void printB() {
a3 = new A(14); b3.printB(); // escribe: i=13 j=-30
System.out.println("i=" + i + " j=" + j); // sabe qué es j
} // fin de main()
}
public static void println(String str)
} // fin clase B
{System.out.println(str);}
} // fin clase contenedora A
} // fin clase ClasesInternas

Programación en Java 30
3.7.3. Clases internas locales y clases anónimas
Las clases internas locales (o locales) no se declaran en otra clase al máximo nivel. Se hace en un bloque
de código, en general un método, aunque pueden crearse en un inicializador static o de objeto. Además:
1. Sólo son visibles y utilizables en el bloque de código en que se definen. Los objetos de la clase local
deben crearse en el mismo bloque en la clase se define
2. Tienen acceso a todas las variables miembro y métodos de la clase contenedora. Pueden ver también los
miembros heredados por la clase interna local y la contenedora
3. Pueden usar las variables locales y argumentos de métodos visibles en ese bloque de código, pero sólo
si son final (la clase local trabaja con sus copias de las variables locales; por eso se exige)
4. Un objeto de una clase local sólo puede existir en relación con un objeto de la clase contenedora, que
exista previamente
5. La palabra this se puede usar en la misma forma que en las clases internas, pero no “new” y “super”
Restricciones en el uso de clases locales:
1. No pueden tener el mismo nombre que ninguna de sus clases contenedoras
2. No pueden definir variables, métodos y clases static
3. No pueden ser declaradas public, protected, private o package, pues su visibilidad es siempre la de las
variables locales; la del bloque en que han sido definidas
Las clases internas locales se usan para definir clases Adapter en el AWT. El cuadro muestra un ejemplo.
// fichero ClasesIntLocales.java
class A {
int i=-1; // variable miembro class ClasesIntLocales {
public A(int i) {this.i=i;} // constructor public static void main(String [] arg) {
public void getAi(final long k) { // argumento final // se crean 2 objetos de la clase contenedora
final double f=3.14; // variable local final A a1 = new A(-10);
class BL { // clase local A a2 = new A(-11);
int j=2; public BL(int j) {this.j=j;} // constructor a1.getAi(1000); // llama al método getAi()
public void printBL() { a2.getAi(2000); // crea y accede a un objeto
// de la clase local
System.out.println(" j="+j+" i="+i+" f="+f+" k="+k);
} // fin de main()
}
public static void println(String str)
} // fin clase BL
{System.out.println(str);}
BL bl = new BL(2*i); // se crea un objeto de BL
} // fin clase ClasesIntLocales
bl.printBL(); // imprime datos del objeto
} // fin getAi
} // fin clase contenedora A
Las clases anónimas son similares a las locales, pero sin nombre. En las locales primero se define la clase
y luego se crean objetos. En las clases anónimas se unen los procesos. Como la clase no tiene nombre sólo
se puede crear un objeto, ya que no se definen constructores. Se usan con frecuencia para definir clases y
objetos en el AWT que gestionen los eventos de los componentes de la interface de usuario. No hay
interfaces anónimas. Formas de definir una clase anónima son las siguientes.
1. En una expresión incluida en una asignación o la llamada a un método. Se incluye la palabra new
seguida de la definición entre llaves de la clase anónima
2. Con la palabra new seguida del nombre de la clase de la que hereda (sin extends) y su definición entre
llaves. El nombre de la superclase puede ir seguido de argumentos para su constructor entre paréntesis
3. Con la palabra new seguida del nombre del interface que implementa (sin implements) y su definición
entre llaves. En este caso la clase anónima deriva de Object y el nombre del interface va seguido de
paréntesis vacíos, pues el constructor de Object no tiene argumentos

Programación en Java 31
Para clases anónimas compiladas, el compilador produce ficheros asignando un número correlativo a cada
clase anónima con nombres del tipo ClaseContenedora$1.class. Debe prestarse atención a la caligrafía de
definición de clases anónimas, pues al no tener nombre suelen ser difíciles de leer e interpretar. Se
aconseja que la palabra new esté en la misma línea que el resto de la expresión; que las llaves se abran en
la misma línea que new, después del cierre del paréntesis de los argumentos del constructor; que el cuerpo
de la clase se sangre o indente respecto a las líneas anteriores de código por legibilidad; que el cierre de las
llaves vaya seguido por el resto de la expresión en la que se ha definido la clase. A continuación se muestra
un ejemplo de clase anónima con el AWT.
La clase anónima, derivada de Object, implementa unObjeto.addActionListener( new ActionListener() {
la interface ActionListener. Las clases anónimas se
public void actionPerformed(ActionEvent e) {
usan en lugar de las locales para poco código, de
las que sólo hace falta un objeto. No pueden tener ...
constructores, pero sí inicializadores static o de
objeto. Además de las citadas, tienen restricciones }
similares a las clases locales. });
3.8. Permisos de acceso en java
La encapsulación en POO es la propiedad de ocultar la información que no interesa sea manipulada por el
usuario. Para controlar esa manipulación, se dispone de los permisos de acceso Java.
Acceso a packages. Hace referencia a la accesibilidad física entre máquinas y sus permisos de acceso a
ficheros. En este sentido, un package es accesible si sus directorios y ficheros son accesibles
(disponibilidad y permiso). Además, serán accesibles los packages listados en la variable CLASSPATH.
Acceso a clases o interfaces. En principio, cualquier clase o interface de un package, sea public o no, es
accesible para las demás clases del package. Una clase public es accesible a cualquier clase si su package
es accesible. Clases e interfaces sólo pueden ser public o package (la opción por defecto).
Acceso a variables y métodos miembros de una clase. Se distinguen 3 casos.
Desde dentro de la propia clase
1. Los miembros de una clase son accesibles (sin cualificar o con “this”) desde dentro de la propia
clase. Los métodos no necesitan que las variables miembro sean pasadas como argumento
2. Los miembros private de una clase sólo son accesibles para la propia clase
3. Si el constructor de una clase es private, sólo un método static de la clase puede crear objetos
Desde una subclase.
Las subclases heredan los miembros private de su superclase, pero sólo pueden acceder a ellos a
través de métodos public, protected o package de la superclase
Desde otras clases del package
Desde una clase de un package se tiene acceso a todos los miembros que no sean private de las
demás clases del package
Desde otras clases fuera del package
1. Los métodos y variables son accesibles si la clase es public y el miembro es public
2. También son accesibles si la clase que accede es una subclase y el miembro es protected
La muestra un resumen de los permisos de acceso en Java.
Visibilidad public protected private default
Desde la propia clase SI SI SI SI
Desde otra clase en el propio package SI SI NO SI
Desde otra clase fuera del package SI NO NO NO
Desde una subclase en el propio package SI SI NO SI
Desde una subclase fuera del propio package SI SI NO NO
3.9. Transformaciones de tipo: casting
En Java se realizan de modo automático conversiones implícitas de un tipo a otro de más precisión, por
ejemplo de int a long, float a double, etc. cuando se mezclan variables de distintos tipos en expresiones

Programación en Java 32
matemáticas o al ejecutar sentencias de asignación en las que el miembro izquierdo tiene un tipo distinto
(más amplio) que el resultado de evaluar el miembro derecho.
Las conversiones de un tipo de mayor precisión a otro de menor requieren sentencia explícita, pues son
conversiones inseguras (p. ej. pasar a short un int, hay que asegurar que puede representarse con el
número de cifras binarias de short). Estas conversiones explícitas de tipo son el casting y se hace poniendo
el tipo al que se desea transformar entre paréntesis. Por ejemplo {long result; result = (long) (a/(b+c));}. A
diferencia de C/C++, en Java no se puede convertir un tipo numérico a boolean. La conversión de strings
(texto) a números se estudia posteriormente.
3.10. Polimorfismo
El polimorfismo se refiere a la relación entre la llamada a un método y el código que se ejecuta con ella.
Esta relación se denomina vinculación (binding). Puede ser temprana (en compilación) o tardía (en
ejecución). Con funciones normales o sobrecargadas se usa vinculación temprana (posible y más eficiente).
Con funciones redefinidas se usa siempre vinculación tardía, excepto si el método es final.
El polimorfismo es la opción por defecto en Java. La vinculación tardía posibilita que, con un método
declarado en una clase base (o interface) redefinido en clases derivadas (o que implementan esa interface),
sea el tipo de objeto y no el tipo de referencia el que determine qué definición del método se usa.
El tipo del objeto al que apunta una referencia sólo se conoce en tiempo de ejecución. Por eso el
polimorfismo requiere evaluación tardía, permitiendo al programador separar contextos de constantes,
favoreciendo la ampliación, mantenimiento y reutilización del código. El polimorfismo puede implementarse
con referencias de superclases normales, abstract e interfaces. Por su mayor flexibilidad e independencia
de la jerarquía de clases estándar, las interfaces permiten ampliar las posibilidades del polimorfismo.
El polimorfismo se basa en el uso de referencias más amplias que los objetos a los que apuntan. Pero
posee una limitación importante: el tipo de la referencia (clase abstracta, base o interface) limita los métodos
que se pueden usar y las variables miembro a las que se puede acceder. Por ejemplo, un objeto puede
tener una referencia a un tipo interface, sólo en caso de que su clase o una de sus superclases implemente
dicha interface. Un objeto cuya referencia es un tipo interface sólo puede usar los métodos definidos en
dicha interface. De otro modo, ese objeto no puede usar variables ni métodos propios de su clase. De esta
forma las referencias de tipo interface definen, limitan y unifican la forma de uso de objetos de clases muy
distintas que implementan dicha interface.
Si se desea usar todos los métodos y acceder a todas las variables que la clase de un objeto permite, hay
que usar un cast explícito, que convierta su referencia más general en la del tipo específico del objeto. De
aquí una parte importante del interés del cast entre objetos. Para la conversión entre objetos de distintas
clases, Java exige que dichas clases estén relacionadas con herencia. Se realiza una conversión implícita o
automática de una subclase a una superclase siempre que se necesite, ya que el objeto de la subclase
tiene toda la información necesaria para usarse en lugar de un objeto de la superclase. No importa que la
superclase no sea capaz de contener toda la información de la subclase.
La conversión en sentido contrario (un objeto de una superclase donde se espera encontrar uno de la
subclase) debe hacerse de modo explícito y puede producir errores por falta de información o métodos. Si
falta información, se obtiene una ClassCastException. No se puede acceder a las variables exclusivas de la
subclase con una referencia de la superclase. Sólo se pueden usar métodos definidos en la superclase,
aunque la ejecución de esos métodos sea el código de la subclase.
Por ejemplo, sea un objeto de la subclase B referenciado con un nombre de la superclase A: A a = new B();.
En este caso el objeto dispone de más información de la que la referencia “a” le permite acceder (p.ej., una
nueva variable miembro j de B). Para acceder a esa información adicional hay que hacer un cast explícito
en la forma (B)a. Para imprimir esa variable j habría que escribir (los paréntesis son necesarios):
System.out.println( ((B)a).j );.
Un cast de un objeto a la superclase x // se accede a la x de C
puede permite usar variables (no
this.x // se accede a la x de C
métodos) de la superclase, aunque estén
redefinidos en la subclase. Sea una clase super.x // se accede a la x de B. Sólo se sube un nivel
C derivada de B, derivada de A. Las 3
((B)this).x // se accede a la x de B
definen una variable x. En este caso, si
desde el código de la subclase C se usa: ((A)this).x // se accede a la x de A

Programación en Java 33
4. CLASES DE UTILIDAD
La programación Java siempre parte de la infraestructura de su API. Se describen algunas clases útiles.
4.1. Arrays
Los arrays de Java (vectores, matrices e hipermatrices) se tratan como objetos de características propias de
una clase predefinida. Pueden ser asignados a objetos de la clase Object y los métodos de Object pueden
ser usados con arrays.
Los arrays se crean con el operador new seguido del tipo y número de elementos y se puede acceder al
número de elementos de un array con la variable miembro implícita length (vect.length). A los elementos de
un array se accede con los corchetes [] y un índice (de 0 a length-1).
Se pueden crear arrays de objetos. En principio será un array de referencias que hay que completar con el
operador new. Los elementos de un array se inicializan al valor por defecto del tipo correspondiente (0 para
valores numéricos, nulo para char, false para boolean, null para Strings y referencias). Como todos los
objetos, los arrays se pasan a los métodos por referencia. Se pueden crear arrays anónimos (p. ej. un
nuevo array como argumento actual en la llamada a un método).
Los arrays se pueden inicializar con valores entre llaves separados por comas. Los arrays de objetos se
pueden inicializar con varias llamadas a new entre llaves. Si se igualan dos referencias a un array no se
copia el array, se genera un array con dos nombres, apuntando al mismo objeto.
Para crear una referencia a un array existen 2 formas, como double[] x; double x[];
se muestra en la primera línea del cuadro. Para crearlo, se
x = new double[100];
usa el operador new. Ambas, la creación de la referencia y
el propio array, se pueden resumir como en la última línea. double[] x = new double[100];
Los siguientes muestran ejemplos de creación de arrays.
// array de 10 enteros, por defecto inicializados a 0 // array de 5 objetos
int v[] = new int[10]; MiClase listaObj[] = new MiClase[5]; // los 5 a null
// arrays inicializados con valores for( i = 0 ; i < 5;i++)
int v[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; listaObj[i] = new MiClase(...);
String dias[] = {"lunes", "martes", "miercoles", // array anónimo
"jueves", "viernes", "sabado", "domingo"};
obj.metodo(new String[]={"uno", "dos", "tres"});
Los arrays bidimensionales se crean con reserva dinámica de memoria. En Java una matriz es un vector de
vectores fila, o un vector de referencias a los vectores fila.
Así, cada fila podría tener un número de elementos diferente. Una int[][] mat;
matriz se puede crear directamente en la forma int [][] mat = new
mat = new int[nfilas][];
int[3][4]; o bien dinámicamente, como en el cuadro. Primero se
crea la referencia a la matriz, indicándolo con un doble corchete. for (int i=0; i<nfilas; i++);
Luego se crea el vector de referencias a las filas y por fin, se
reserva memoria para los vectores correspondientes a las filas. mat[i] = new int[ncols];

Los siguientes muestran ejemplos de creación de arrays bidimensionales.


En el caso de una matriz b, b.length es el número double mat[][] = new double[3][3];
de filas y b[0].length el número de columnas (de
int [][] b = {{1, 2, 3},{4, 5, 6},};
la fila 0). Los arrays bidimensionales pueden
contener tipos primitivos de cualquier tipo u int c = new[3][]; // crea el array de referencias a arrays
objetos de cualquier clase.
c[0] = new int[5]; c[1] = new int[4]; c[2] = new int[8];
4.2. Clases string y stringbuffer
Las clases String y StringBuffer se orientan a manejar cadenas de caracteres. La primera a cadenas de
caracteres constantes y StringBuffer permite cambiar la cadena. String es más eficiente y menos versátil.
Ambas clases pertenecen al package java.lang; no hay que importarlas. El operador de concatenación (+)
entre objetos de tipo String usa internamente objetos de StringBuffer y el método append. Los métodos de
String se pueden usar directamente sobre literals (cadenas entre comillas), como "Hola".length().
Los objetos de la clase String se pueden crear a partir de cadenas constantes o literals, definidas entre
dobles comillas ("Hola"). Java crea siempre un objeto String al encontrar una cadena entre comillas.

Programación en Java 34
En el cuadro se muestran 2 formas de crear objetos de la clase String str1 = "Hola"; // más eficaz
String. El primero es más eficiente, porque al encontrar un texto
String str2 = new String("Hola");
entre comillas se crea automáticamente un objeto String.
En la práctica, con new se llama al constructor 2 veces. También se pueden crear objetos de String con
constructores de la clase, a partir de objetos StringBuffer, y de arrays de bytes o chars. La tabla siguiente
muestra los métodos más importantes de la clase String. Existen métodos, como System.out.println que
exigen que su argumento sea un objeto de la clase String. Si no lo es, habrá que convertirlo.
Métodos de String Función
String(...) Constructores a partir de arrays de bytes o caracteres
String(String str), String(StringBuffer sb) Costructores a partir de un objeto String o StringBuffer
charAt(int) Devuelve el carácter en la posición especificada
getChars(int, int, char[], int) Copia los caracteres indicados en la posición indicada de un
array de caracteres
indexOf(String, [int]) Devuelve la posición en la que aparece por primera vez un String
en otro String, a partir de una posición dada (opcional)
lastIndexOf(String, [int]) Devuelve la última vez que un String aparece en otro empezando
en una posición y hacia el principio
length() Devuelve el número de caracteres de la cadena
replace(char, char) Sustituye un carácter por otro en un String
startsWith(String) Indica si un String comienza con otro String o no
substring(int, int) Devuelve un String extraído de otro
toLowerCase() Convierte en minúsculas (puede tener en cuenta el locale)
toUpperCase() Convierte en mayúsculas (puede tener en cuenta el locale)
trim() Elimina los espacios en blanco al comienzo y final de la cadena
valueOf() Devuelve la representación como String de sus argumentos.
Admite Object, arrays de caracteres y los tipos primitivos
El locale citado en la tabla es la forma que Java tiene para adaptarse a las peculiaridades de los idiomas
distintos al inglés, fechas y horas, unidades monetarias, etc.
La clase StringBuffer se usa prácticamente siempre que se desea modificar una cadena de caracteres.
Completa los métodos de la clase String, que sólo realizan operaciones sobre el texto si no implica aumento
o disminución del número de letras del String. La Tabla muestra los métodos destacados de StringBuffer.
Métodos de StringBuffer Función
StringBuffer(), StringBuffer(int), Constructores
StringBuffer(String)
Tiene muchas definiciones diferentes para añadir un String o una variable
append(...)
(int, long, double, etc.) a su objeto
capacity() Devuelve el espacio libre del StringBuffer
charAt(int) Devuelve el carácter en la posición especificada
Copia los caracteres indicados en su posición de un array de caracteres
getChars(int, int, char[], int)
insert(int, ) Inserta un String o un valor (int, long, double, ...) en la posición
especificada de un StringBuffer
length() Devuelve el número de caracteres de la cadena
reverse() Cambia el orden de los caracteres
setCharAt(int, char) Cambia el carácter en la posición indicada
setLength(int) Cambia el tamaño de un StringBuffer
toString() Convierte en objeto de tipo String

Programación en Java 35
4.3. Wrappers y clase Math
Wrappers (envoltventes) son clases diseñadas para complementar los tipos primitivos, los únicos elementos
Java que no son objetos. Presenta ventajas de eficiencia e inconvenientes de funcionalidad. Por ejemplo,
los tipos primitivos siempre se pasan como argumento a los métodos por valor; los objetos por referencia.
No hay forma de modificar en un método un argumento de tipo primitivo y que se trasmita al entorno que lo
llamó. Una forma de conseguirlo es con un Wrapper, un objeto cuya variable miembro es el tipo primitivo a
modificar. Las clases Wrapper también proporcionan métodos para realizar otras tareas con tipos primitivos,
como conversión con cadenas de caracteres en uno y otro sentido.
Existe una clase Wrapper para cada tipo primitivo numérico; existen las clases Byte, Short, Integer, Long,
Float y Double. A continuación se verán las clases Double e Integer. Las otras cuatro son similares.
La clase java.lang.Double deriva de Number, que deriva de Object. Esta clase contiene un valor primitivo de
tipo double. La tabla muestra algunos métodos y constantes predefinidas de la clase Double. El Wrapper
Float es similar al Wrapper Double.
Métodos Función
Double(double) y Double(String) Constructores
doubleValue(), floatValue(), longValue(), intValue(), Métodos para obtener el valor del tipo primitivo
shortValue(), byteValue()
String toString(), Double valueOf(String) Conversores con la clase String
isInfinite(), isNaN() Métodos de chequeo de condiciones
equals(Object) Compara con otro objeto
MAX_DOUBLE, MIN_DOUBLE, POSITIVE_INFINITY, Constantes predefinidas. TYPE es el objeto Class
NEGATIVE_INFINITY, NaN, TYPE representando esta clase
La clase java.lang.Integer tiene como variable miembro un valor de tipo int. La siguiente tabla muestra los
métodos y constantes de la clase Integer. Los Wrappers Byte, Short y Long son similares a Integer.
Métodos Función
Integer(int) y Integer(String) Constructores
doubleValue(), floatValue(), longValue(), intValue(), Conversores con otros tipos primitivos
shortValue(), byteValue()
Integer decode(String), Integer parseInt(String), String Conversores con String
toString(), Integer valueOf(String)
String toBinaryString(int), String toHexString(int), Conversores a cadenas representando enteros en
String toOctalString(int) otros sistemas de numeración
Integer getInteger(String) Determina el valor de una propiedad del sistema a
partir del nombre de dicha propiedad
MAX_VALUE, MIN_VALUE, TYPE Constantes predefinidas
Los Wrappers se usan para convertir cadenas de caracteres (texto) en números. Es útil cuando se leen
valores del teclado, de un fichero de texto, etc. Los ejemplos siguientes muestran algunas conversiones:
String numDecimalString = "8.978";
float numFloat=Float.valueOf(numDecimalString).floatValue(); // numFloat = 8,979
double numDouble=Double.valueOf(numDecimalString).doubleValue(); // numDouble = 8,979
String numIntString = "1001";
int numInt=Integer.valueOf(numIntString).intValue(); // numInt = 1001
En caso que el texto no se pueda convertir directamente al tipo especificado se lanza una excepción de tipo
NumberFormatException, por ejemplo al intentar convertir directamente “4.897” a un número entero. El
proceso a seguir será convertirlo primero a un número float y luego a número entero.
La clase java.lang.Math deriva de Object. Proporciona métodos static para operaciones matemáticas
habituales y las constantes E y PI. La tabla muestra los métodos matemáticos de la clase.

Programación en Java 36
Métodos Significado Métodos Significado
abs() Valor absoluto sin(double) Seno
acos() Arcocoseno tan(double) Tangente
asin() Arcoseno exp() Función exponencial
atan() Arcotangente entre -PI/2 y log() Logaritmo natural (base e)
PI/2
atan2( , ) Arcotangente entre -PI y max( , ) Máximo de dos argumentos
PI
ceil() Entero más cercano min( , ) Mínimo de dos argumentos
dirección infinito
floor() Entero más cercano random() Nº aleatorio entre 0.0 y 1.0
dirección -infinito
er
round() Entero más cercano al power( , ) 1 argumento elevado al 2º
argumento
rint(double) Entero más próximo sqrt() Raíz cuadrada
IEEEremainder(doublé, resto de la división toDegrees(double) Pasa de radianes a grados
double)
cos(double) Coseno toRadians() Pasa de grados a radianes
4.4. Colecciones
Java dispone clases e interfaces para trabajar con colecciones de objetos. En primer lugar se verán las
clases Vector y Hashtable, así como la interface Enumeration.
4.4.1. Clase Vector, Hashtable e interface Enumeration
La clase java.util.Vector deriva de Object, implementa Cloneable (para sacar copias con el método clone) y
Serializable (para convertirse en cadena de caracteres). Vector es un array de objetos (referencias a objetos
tipo Object) que puede crecer y reducirse, según el número de elementos. Permite acceder a los elementos
con un índice, pero no permite usar corchetes.
El método capacity() devuelve el tamaño (número de elementos) que puede tener el vector; size el que
realmente contiene y capacityIncrement es una variable que indica el salto que se dará en el tamaño
cuando haya que crecer. Además existen otras 2 variables miembro: elementCount, número de
componentes válidos del vector y elementData[], el array de Objects donde realmente se guardan los
elementos del objeto Vector. Las tres variables citadas son protected. La tabla muestra los métodos
destacados de Vector. Su número proporciona gran flexibilidad a la clase.
Métodos Función
Vector(), Vector(int), Vector(int, int) Constructores. Crean un vector vacío de la capacidad
indicada y un vector de capacidad e incremento indicados
void addElement(Object obj) Añade un objeto al final
boolean removeElement(Object obj) Elimina el primer objeto que encuentra como su argumento y
desplaza los restantes. Si no lo encuentra devuelve false
void removeAllElements() Elimina todos los elementos
Object clone() Devuelve una copia del vector
void copyInto(Object anArray[]) Copia un vector en un array
void trimToSize() Ajusta el tamaño a los elementos que tiene
void setSize(int newSize) Establece un nuevo tamaño
int capacity() Devuelve el tamaño (capacidad) del vector
int size() Devuelve el número de elementos
boolean isEmpty() Devuelve true si no tiene elementos

Programación en Java 37
Enumeration elements() Devuelve una Enumeración con los elementos
boolean contains(Object elem) Indica si contiene o no un objeto
int indexOf(Object elem, int index) Devuelve la posición de la primera vez que aparece un objeto
a partir de una posición dada
int lastIndexOf(Object elem, int index) Devuelve la posición de la última vez que aparece un objeto a
partir de una posición, hacia atrás
Object elementAt(int index) Objeto de una posición dada
Object firstElement() Primer elemento
Object lastElement() Último elemento
void setElementAt(Object obj, int index) Cambia el elemento que está en una posición dada
void removeElementAt(int index) Elimina el elemento que está en una posición dada
void insertElementAt(Object obj, int index) Inserta un elemento por delante de una posición dada
La clase java.util.Hashtable extiende Dictionary (abstract) e implementa Cloneable y Serializable. Una hash
table es una relación entre clave y un valor (hash). Cualquier objeto distinto de null puede ser clave o valor.
La clase a la que pertenecen las claves debe implementar hashCode y equals para hacer búsquedas y
comparaciones. El método hashCode() devuelve un entero único y distinto para cada clave, siempre el
mismo en una ejecución del programa; puede cambiar de una ejecución a otra. Para 2 claves iguales
(según equals) hashCode() devuelve el mismo entero(hash). Las hash tables se diseñan para mantener una
colección de pares clave/hash, permitiendo insertar y realizar búsquedas eficientes.
Cada objeto de Hashtable tiene 2 variables: capacity y load factor Hashtable numeros = new Hashtable();
(entre 0.0 y 1.0). Si el número de elementos excede su producto,
numbers.put("uno", new Integer(1));
la Hashtable crece llamando al método rehash. Un load factor
más grande apura la memoria, pero es menos eficiente en numbers.put("dos", new Integer(2));
búsqueda. Conviene partir de una Hashtable adecuada para
evitar ampliaciones. numbers.put("tres", new Integer(3));

El ejemplo define una Hashtable con el método put(). La tabla muestra los métodos de la clase.
Métodos Función
Hashtable(), Hashtable(int nElements), Hashtable(int Constructores
nElements, float loadFactor)

int size() Tamaño de la tabla


boolean isEmpty() Indica si la tabla está vacía
Enumeration keys() Devuelve una Enumeration con las claves
Enumeration elements() Devuelve una Enumeration con los valores
boolean contains(Object value) Indica si hay alguna clave con ese valor
boolean containsKey(Object key) Indica si existe esa clave en la tabla
Object get(Object key) Devuelve un valor dada la clave
void rehash() Amplía la capacidad de la tabla
Object put(Object key, Object value) Establece una relación clave-valor
Object remove(Object key) Elimina un valor por la clave
void clear() Limpia la tabla
Object clone() Hace una copia de la tabla
String toString() Devuelve un string representando la tabla
La interface java.util.Enumeration define métodos para recorrer una colección de objetos. Puede haber
distintas clases que implementen esta interface, todas con comportamiento similar. Declara 2 métodos:
1. public boolean hasMoreElements(). Indica si hay más elementos en la colección o si se llega al final

Programación en Java 38
2. public Object nextElement(). Devuelve el siguiente objeto de la colección. Si no hay más elementos lanza
una NoSuchElementException.
En el cuadro se muestra un ejemplo que for (Enumeration e = vec.elements(); e.hasMoreElements(); ) {
imprime los elementos de un vector vec
System.out.println(e.nextElement());
donde el método elements() devuelve
una referencia de tipo Enumeration. }
Con hasMoreElements() y nextElement() y un bucle for se pueden imprimir los elementos del objeto Vector.
4.4.2. Collections Framework Java 1.2
La versión 1.2 del JDK introdujo Java Framework Collections (JCF), un conjunto de clases e interfaces que
mejoran la capacidad del lenguaje respecto a estructuras de datos. Es el paradigma de aplicación de los
conceptos de POO. La figura muestra la jerarquía de interfaces JCF.

En letra cursiva se indican las clases que implementan las correspondientes interfaces. Por ejemplo, 2
clases implementan la interface Map: HashMap y Hashtable. Las clases vistas existían antes de JDK 1.2.
Se marcan en la figura con la letra “h” entre paréntesis. Se han mantenido por compatibilidad pero sus
métodos no siguen las reglas de diseño general de JCF. Se recomienda usar las nuevas clases.
En el diseño de JCF las interfaces son importantes porque determinan la capacidad de las clases que las
implementan. Dos clases que implementan la misma interface se pueden usar en la misma forma, como las
clases ArrayList y LinkedList. La diferencia es la implementación. ArrayList almacena los objetos en un array
y LinkedList en una lista vinculada. La primera será más eficiente para acceder a un elemento arbitrario; la
segunda será más flexible para borrar e insertar elementos.
La figura derecha muestra la jerarquía de clases JCF. En fondo blanco se muestran clases abstractas y con
gris quellas de las que se pueden crear objetos. Las clases Collections y Arrays no son abstract, pero no
tienen constructores públicos con los que se pueden crear objetos. Fundamentalmente contienen métodos
static para realizar ciertas operaciones de utilidad: ordenar, buscar, introducir ciertas características en
objetos de otras clases, etc. Los elementos del JCF se pueden estructurar como sigue.
Interfaces de la JCF. Constituyen el elemento central de la JCF.
Collection Define métodos para tratar una colección genérica de elementos
Set Colección que no admite elementos repetidos
SortedSet Set cuyos elementos se mantienen ordenados según criterio
List Admite elementos repetidos y mantiene un orden inicial
Map Conjunto de pares clave/valor, sin repetición de claves
SortedMap Map cuyos elementos se mantienen ordenados criterio
Interfaces de soporte.
Iterator. Sustituye a Enumeration. Dispone métodos para recorrer una colección y borrar elementos
ListIterator. Deriva de Iterator y permite recorrer lists en ambos sentidos

Programación en Java 39
Comparable. Declara el método compareTo que permite ordenar colecciones según un orden
natural (String, Date, Integer, Double…)
Comparator. Declara el método compare y se usa en lugar de Comparable cuando se desea
ordenar objetos no estándar o sustituir a dicha interface
Clases de propósito general. Son las implementaciones de las interfaces de la JFC
HashSet Interface Set implementada con una hash table
TreeSet Interface SortedSet implementada con un árbol binario ordenado
ArrayList Interface List implementada con un array
LinkedList Interface List implementada con una lista vinculada
HashMap Interface Map implementada con una hash table
WeakHashMap Interface Map implementada de modo que la memoria de los pares clave/valor
pueda ser liberada cuando las claves no tengan referencia desde el exterior de la WeakHashMap
TreeMap Interface SortedMap implementada con un árbol binario
Clases Wrapper. Colecciones con características adicionales, como no poder ser modificadas o estar
sincronizadas. No se accede con constructores, sino mediante métodos “factory” de la clase Collections
Clases de utilidad. Pequeñas implementaciones que permiten obtener sets especializados, como sets
constantes de un sólo elemento (singleton) o lists con n copias del mismo elemento (nCopies). Definen las
constantes EMPTY_SET y EMPTY_LIST. Se accede a través de la clase Collections
Clases históricas. Son las clases Vector y Hashtable de las primeras versiones de Java. En las actuales,
implementan respectivamente las interfaces List y Map, aunque conservan los métodos anteriores
Clases abstractas. Son las de la figura. Implementan total o parcialmente los métodos de la interface
correspondiente. Sirven para que los usuarios deriven de ellas sus propias clases con mínimo esfuerzo
Algoritmos. La clase Collections dispone de métodos static para ordenar, desordenar, invertir orden, realizar
búsquedas, llenar, copiar, hallar el mínimo y hallar el máximo.
Clase Arrays. Es una clase de utilidad introducida en JDK 1.2 que contiene métodos static para gestionar
los arrays clásicos. También permite tratar los arrays como lists.
4.4.2.1. Interfaces Collection, Iterator y ListIterator
La interface Collection es public interface java.util.Collection
implementada por conjuntos (sets) y
{
listas (lists). Declara métodos
generales utilizables con Sets y Lists. public abstract boolean add(java.lang.Object); // opcional
La declaración (header) de los
public abstract boolean addAll(java.util.Collection); // opcional
métodos se muestra ejecutando el
comando > javap java.util.Collection. public abstract void clear(); // opcional
Su salida es la mostrada.Los métodos
explican su funcionalidad en su public abstract boolean contains(java.lang.Object);
nombre, los argumentos y su valor de public abstract boolean containsAll(java.util.Collection);
retorno.
public abstract boolean equals(java.lang.Object);
Los métodos marcados con opcional
pueden no estar disponibles en public abstract int hashCode();
algunas implementaciones, como en public abstract boolean isEmpty();
las clases que no permiten modificar
sus objetos. Estos métodos deben public abstract java.util.Iterator iterator();
definirse, pero al ser llamados lanzan public abstract boolean remove(java.lang.Object); // opcional
una UnsupportedOperationException.
public abstract boolean removeAll(java.util.Collection); // opcional
El método add trata de añadir un
objeto a una colección aunque puede public abstract boolean retainAll(java.util.Collection); // opcional
no conseguirlo porque la colección public abstract int size();
sea un set que ya lo contenga. Si
modifica la colección devuelve true. Lo public abstract java.lang.Object toArray()[];
mismo con addAll. El método remove public abstract java.lang.Object toArray(java.lang.Object[])[];
elimina un elemento, si lo encuentra y
devuelve true si tiene éxito. }

Programación en Java 40
El método iterator devuelve una referencia Iterator que permite recorrer una colección con next y hasNext.
El método remove borra el elemento actual y los 2 métodos toArray convertir una colección en array.
La interface Iterator sustituye a Enumeration de versiones anteriores de JDK. Dispone de los métodos
mostrados a la izquierda. El método remove permite borrar el último elemento accedido con next. Es la
única forma segura de eliminar un elemento al recorrer una colección.
Compiled from Iterator.java Compiled from ListIterator.java
public interface java.util.Iterator public interface java.util.ListIterator extends java.util.Iterator
{ {
public abstract boolean hasNext(); public abstract void add(java.lang.Object);
public abstract java.lang.Object next(); public abstract boolean hasNext();
public abstract void remove(); public abstract boolean hasPrevious();
} public abstract java.lang.Object next();
Los métodos de la interface ListIterator son public abstract int nextIndex();
los mostrados a la derecha. Permite recorrer
public abstract java.lang.Object previous();
una lista en ambas direcciones y hacer
modificaciones al recorrerla. Los elementos public abstract int previousIndex();
se numeran desde 0 a n-1 y los valores
public abstract void remove();
válidos del índice son de 0 a n.
public abstract void set(java.lang.Object);
El índice está entre los elementos i-1 e i, de
forma que previousIndex devuelve i-1 y }
nextIndex devolvería i.
Si el índice es 0, previousIndex devuelve –1 y si es n nextIndex devuelve el resultado de size.
4.4.2.2. Interfaces Comparable y Comparator
Estas interfaces se orientan a ordenar listas, sets y maps. Dispone de las interfaces java.lang.Comparable y
java.util.Comparator (de packages diferentes).
Comparable declara el método compareTo como “public int compareTo(Object obj)”, que compara su
argumento implícito con el que se le pasa por ventana. El método devuelve un entero negativo, cero o
positivo según el argumento implícito (this) sea anterior, igual o posterior al objeto obj.
Las listas de objetos de clases que implementan esta interface tienen un orden natural. En Java 1.2 esta
interface la implementan, entre otras, las clases String, Character, Date, File, BigDecimal, BigInteger, Byte,
Short, Integer, Long, Float y Double. La implementación estándar de estas clases no asegura un orden
alfabético correcto con mayúsculas y minúsculas ni en idiomas distintos al inglés.
Si se redefine el método compareTo debe ser programado con cuidado. Es conveniente que sea coherente
con el método equals y que cumpla la propiedad transitiva. Listas y arrays cuyos elementos implementan
Comparable pueden ser ordenados con los métodos static Collections.sort() y Arrays.sort().
Comparator permite ordenar listas y colecciones cuyos objetos pertenecen a clases de tipo cualquiera.
Permitiría ordenar figuras geométricas planas por área o perímetro. Su papel es similar al de Comparable,
pero el usuario debe proporcionar su implementación. Sus dos métodos se declaran en la forma mostrada.
El objetivo de equals es comparar Comparators y compare public int compare(Object o1, Object o2)
devuelve un entero negativo, 0 o positivo en función de que su
public boolean equals(Object obj)
primer argumento sea anterior, igual o posterior al segundo.
Los objetos que implementan Comparator pueden pasarse como argumentos al método Collections.sort o a
algunos constructores de las clases TreeSet y TreeMap, para mantenerlos ordenados de acuerdo con dicho
Comparator. Es importante que compare sea compatible con el equals de los objetos a ordenar. Su
implementación debe cumplir condiciones similares a las de compareTo. Java 1.2 dispone de clases
capaces de ordenar cadenas de texto en diferentes lenguajes, como se expone en la documentación de las
clases CollationKey, Collator y sus derivadas del package java.text.
4.4.2.3. Sets y SortedSets
La interface Set permite acceder a una colección, ordenada o no, sin elementos repetidos. No declara
métodos adicionales a los de Collection. Como un Set no admite elementos repetidos hay que saber cuándo
2 objetos son considerados iguales (p. ej. las palabras Mesa y mesa sean consideradas iguales). Para ello
se dispone de los métodos equals y hashcode, que el usuario puede redefinir si lo desea.
Programación en Java 41
Con los métodos de Collection los Sets permiten las operaciones algebraicas de unión, intersección y
diferencia. Así, s1.containsAll(s2) permite saber si s2 está contenido en s1.La interface SortedSet extiende
la interface Set y añade los métodos de la lista.
Se orientan a trabajar con Compiled from SortedSet.java
elementos ordenados.. El
public interface java.util.SortedSet extends java.util.Set
método comparator
permite obtener el objeto {
pasado al constructor para
establecer el orden. Si se public abstract java.util.Comparator comparator();
ha usado el orden natural public abstract java.lang.Object first();
definido por la interface
Comparable, el método public abstract java.util.SortedSet headSet(java.lang.Object);
devuelve null. public abstract java.lang.Object last();
Los métodos first y last public abstract java.util.SortedSet subSet(java.lang.Object, java.lang.Object);
devuelven el primer y
último elemento del public abstract java.util.SortedSet tailSet(java.lang.Object);
conjunto, respectivamente. }
Los métodos headSet, subSet y tailSet obtienen subconjuntos al principio, en medio y al final del conjunto
original. Existen 2 implementaciones de conjuntos, la clase HashSet implementa la interface Set y la clase
TreeSet implementa SortedSet. La primera se basa en una hash table y la segunda en un TreeMap.
Los elementos de un HashSet no mantienen el orden natural, ni el de introducción. Los elementos de un
TreeSet mantienen el orden natural o el especificado por Comparator. Ambas clases definen constructores
que admiten un objeto Collection lo que permite convertir un HashSet en un TreeSet y viceversa.
4.4.2.4. Listas
La interface List define métodos para operar Compiled from List.java
con colecciones ordenadas que pueden tener
public interface java.util.List extends java.util.Collection
elementos repetidos. Declara métodos relativos
al orden y acceso a elementos o rangos de los {
mismos. Además de los métodos de Collection,
public abstract void add(int, java.lang.Object);
List declara los métodos mostrados.
Los nuevos métodos add y addAll tienen un public abstract boolean addAll(int, java.util.Collection);
argumento adicional para insertar elementos en public abstract java.lang.Object get(int);
una posición dada, desplazando el elemento de
esa posición y los siguientes. Los métodos get y public abstract int indexOf(java.lang.Object);
set permiten obtener y cambiar un elemento de public abstract int lastIndexOf(java.lang.Object);
posición. Los métodos indexOf y lastIndexOf
permiten saber la posición de la primera o public abstract java.util.ListIterator listIterator();
última vez que un elemento aparece en la lista; public abstract java.util.ListIterator listIterator(int);
si el elemento no se encuentra se devuelve –1.
El método subList(int fromIndex, toIndex) public abstract java.lang.Object remove(int);
devuelve una vista de la lista, desde el public abstract java.lang.Object set(int, java.lang.Object);
elemento fromIndex inclusive hasta toIndex
exclusive. Un cambio en la vista se refleja en la public abstract java.util.List subList(int, int);
lista original, aunque no conviene hacer }
cambios simultáneamente en ambas.
Lo aconsejable es eliminar la vista cuando no se necesite. Existen 2 implementaciones de la interface List:
las clases ArrayList y LinkedList. La diferencia estriba en que la primera almacena los elementos de la
colección en un array de Objects y la segunda en una lista vinculada. Los arrays proporcionan un acceso a
elementos más eficiente que las listas, pero tienen dificultades para crecer y para insertar y/o borrar
elementos. Las listas vinculadas sólo permiten acceso secuencial, pero presentan gran flexibilidad para
crecer, borrar e insertar elementos. Elegir la opción adecuada dependerá del análisis.
4.4.2.5. Maps y SortedMaps
Un Map es una estructura de datos agrupados en parejas clave/valor (como una tabla de 2 columnas). La
clave debe ser única y se usa para acceder al valor.
Aunque la interface Map no Compiled from Map.java
deriva de Collection, puede
public interface java.util.Map
entenderse como colecciones de
Programación en Java 42
parejas clave/valor. Sus métodos
{
son los mostrados en el cuadro
(comando > javap java.util.Map). public abstract void clear();
El método entrySet() devuelve public abstract boolean containsKey(java.lang.Object);
una “vista” del Map como Set.
public abstract boolean containsValue(java.lang.Object);
Los elementos del Set son
referencias de la interface public abstract java.util.Set entrySet();
Map.Entry, una interface interna
de Map. public abstract boolean equals(java.lang.Object);

Esta vista del Map como Set public abstract java.lang.Object get(java.lang.Object);
permite modificar y eliminar public abstract int hashCode();
elementos, pero no añadir
nuevos elementos. public abstract boolean isEmpty();

El método get(key) permite public abstract java.util.Set keySet();


obtener el valor a partir de la public abstract java.lang.Object put(java.lang.Object, java.lang.Object);
clave.
public abstract void putAll(java.util.Map);
El método keySet() devuelve una
vista de las claves como Set. public abstract java.lang.Object remove(java.lang.Object);

El método values() devuelve una public abstract int size();


vista de los valores del Map public abstract java.util.Collection values();
como Collection (porque puede
haber elementos repetidos). public static interface java.util.Map.Entry

El método put() permite añadir {


una pareja clave/valor, mientras public abstract boolean equals(java.lang.Object);
que putAll() vuelca todos los
elementos de un Map en otro public abstract java.lang.Object getKey();
Map (los pares con clave nueva public abstract java.lang.Object getValue();
se añaden; en los pares con
clave ya existente los valores public abstract int hashCode();
nuevos sustituyen a los public abstract java.lang.Object setValue(java.lang.Object);
antiguos).
}
El método remove() elimina una
pareja clave/valor a partir de la }
clave.
La interface SortedMap añade los siguientes métodos, similares a los de SortedSet.
La clase HashMap Compiled from SortedMap.java
implementa la
public interface java.util.SortedMap extends java.util.Map
interface Map y está
basada en una hash {
table, mientras que
public abstract java.util.Comparator comparator();
TreeMap implementa
SortedMap y se basa public abstract java.lang.Object firstKey();
en un árbol binario.
public abstract java.util.SortedMap headMap(java.lang.Object);
public abstract java.lang.Object lastKey();
public abstract java.util.SortedMap subMap(java.lang.Object, java.lang.Object);
public abstract java.util.SortedMap tailMap(java.lang.Object);
}
4.4.2.6. Clases Collections y Arrays
La clase Collections (no Collection) es una clase que define métodos static con distinta funcionalidad.
Destacan los siguientes.
Métodos que definen algoritmos
Ordenación mediante el método mergesort public static void sort(java.util.List);
public static void sort(java.util.List, java.util.Comparator);
Programación en Java 43
Eliminación del orden de modo aleatorio public static void shuffle(java.util.List);
public static void shuffle(java.util.List, java.util.Random);
Inversión del orden establecido public static void reverse(java.util.List);
Búsqueda en una lista public static int binarySearch(java.util.List, java.lang.Object);
public static int binarySearch(java.util.List, java.lang.Object, java.util.Comparator);
Copiar una lista o reemplazar todos los elementos con el elemento especificado
public static void copy(java.util.List, java.util.List);
public static void fill(java.util.List, java.lang.Object);
Cálculo de máximos y mínimos
public static java.lang.Object max(java.util.Collection);
public static java.lang.Object max(java.util.Collection, java.util.Comparator);
public static java.lang.Object min(java.util.Collection);
public static java.lang.Object min(java.util.Collection, java.util.Comparator);
Métodos de utilidad
Set inmutable de un único elemento public static java.util.Set singleton(java.lang.Object);
Lista inmutable con n copias de un objeto public static java.util.List nCopies(int, java.lang.Object);
Constantes para representar el conjunto y la lista vacía
public static final java.util.Set EMPTY_SET;
public static final java.util.List EMPTY_LIST;
La clase Collections dispone de 2 conjuntos de métodos “factory” que pueden ser usados para convertir
objetos de distintas colecciones en objetos “read only” y para convertir distintas colecciones en objetos
“synchronized” (por defecto las clases anteriores no están sincronizadas), lo que quiere decir que se puede
acceder a la colección desde distintas threads sin problemas. Los métodos correspondientes son:
public static java.util.Collection synchronizedCollection(java.util.Collection);
public static java.util.List synchronizedList(java.util.List);
public static java.util.Map synchronizedMap(java.util.Map);
public static java.util.Set synchronizedSet(java.util.Set);
public static java.util.SortedMap synchronizedSortedMap(java.util.SortedMap);
public static java.util.SortedSet synchronizedSortedSet(java.util.SortedSet);
public static java.util.Collection unmodifiableCollection(java.util.Collection);
public static java.util.List unmodifiableList(java.util.List);
public static java.util.Map unmodifiableMap(java.util.Map);
public static java.util.Set unmodifiableSet(java.util.Set);
public static java.util.SortedMap unmodifiableSortedMap(java.util.SortedMap);
public static java.util.SortedSet unmodifiableSortedSet(java.util.SortedSet);
Estos métodos se usan de forma sencilla: se les pasa como argumento una referencia a un objeto que no
cumple la característica deseada y se obtiene como retorno una referencia a un objeto que sí la cumple.
4.4.2.7. Clases abstract e interfaces Cloneable y Serializable
Las clases abstract sirven para desarrollar clases propias con funcionalidades no cubiertas por las clases
que provee Java. Las clases HashSet, TreeSet, ArrayList, LinkedList, HashMap y TreeMap (al igual que
Vector y Hashtable) implementan las interfaces Cloneable y Serializable, lo que permite obtener copias bit a
bit de sus objetos con Object.clone y que se pueden convertir en cadenas o flujos (streams) de caracteres.
Una de las ventajas de implementar la interface Serializable es que los objetos de estas clases pueden ser
impresos con los métodos System.Out.print y System.Out.println.

Programación en Java 44
4.5. Otras clases del package java.util
El package java.util tiene otras clases para distintas aplicaciones, entre ellas algunas destinadas a lo
relacionado con fechas y horas. Algunas de estas clases se presentan a continuación.
4.5.1. Clase Date, Calendar y GregorianCalendar
La clase Date representa un instante de Compiled from Date.java
tiempo dado con precisión de ms. La
public class java.util.Date extends java.lang.Object implements
información de fecha y hora se guarda
en un entero long (64 bits) que contiene java.io.Serializable, java.lang.Cloneable, java.lang.Comparable {
los ms transcurridos desde las 00:00:00
public java.util.Date();
del 1 ENE 1970 GMT (Greenwich Mean
Time). Otras clases permiten a partir de public java.util.Date(long);
un objeto Date obtener información del
año, mes, día, hora, minuto y segundo. public boolean after(java.util.Date);
En el cuadro se muestran los métodos public boolean before(java.util.Date);
de la clase Date, sin los métodos
obsoletos en JDK 1.2. public java.lang.Object clone();

El constructor por defecto Date crea un public int compareTo(java.lang.Object);


objeto a partir de la fecha y hora actual public int compareTo(java.util.Date);
del ordenador y el segundo crea el
objeto a partir de los ms desde el public boolean equals(java.lang.Object);
01/01/1970. Los métodos after y before public long getTime();
permiten saber si la fecha indicada
como argumento implícito (this) es public int hashCode();
posterior o anterior a la pasada como public void setTime(long);
argumento explícito. Los métodos
getTime y setTime obtienen o public java.lang.String toString();
establecen los ms desde el 01/01/1970 }
para un determinado objeto Date.
Otros métodos son consecuencia de las interfaces implementadas por Date. Los objetos de esta clase
suelen usarse, más que directamente, en combinación con las clases Calendar y GregorianCalendar. La
clase Calendar es una clase abstract que dispone métodos para convertir objetos tipo Date en enteros que
representan fechas concretas. La clase GregorianCalendar es la única clase que deriva de Calendar y es la
usada habitualmente.
Java representa fechas y horas con las siguientes particularidades: las horas se representan con enteros de
0 a 23 (la hora "0" va de las 00:00:00 hasta la 1:00:00) y los minutos y segundos con enteros entre 0 y 59.
Los días del mes se representan con enteros entre 1 y 31, pero los meses con enteros de 0 a 11. Los años
se representan con enteros de 4 dígitos. Si se representan con 2 dígitos, se resta 1900.
La clase Calendar tiene una serie de variables miembro y constantes (variables final) útiles:
La variable int AM_PM puede tomar 2 valores: las constantes enteras AM y PM
La variable int DAY_OF_WEEK puede tomar los valores int SUNDAY, MONDAY,…, SATURDAY.
La variable int MONTH puede tomar los valores int JANUARY, FEBRUARY,…, DECEMBER. Para hacer los
programas más legibles es preferible usar estas constantes simbólicas que los números del 0 al 11.
La variable miembro HOUR se usa en los métodos get y set para indicar la hora de la mañana o de la tarde
(de 0 a 11). La variable HOUR_OF_DAY indica la hora del día en formato 24h (de 0 a 23).
Las variables DAY_OF_WEEK, DAY_OF_WEEK_IN_MONTH, DAY_OF_MONTH (o bien DATE),
DAY_OF_YEAR, WEEK_OF_MONTH, WEEK_OF_YEAR tienen un significado evidente. Lo mismo para las
variables ERA, YEAR, MONTH, HOUR, MINUTE, SECOND, MILLISECOND.
Las variables ZONE_OFFSET y DST_OFFSET indican la zona horaria y el desfase en ms respecto a la
zona GMT.
La clase Calendar dispone métodos para establecer u obtener los distintos valores de la fecha y hora.
Algunos de ellos se muestran en la tabla.
Compiled from Calendar.java public static synchronized java.util.Calendar
getInstance(java.util.TimeZone);
public abstract class java.util.Calendar extends
java.lang.Object implements java.io.Serializable, public static synchronized java.util.Calendar

Programación en Java 45
java.lang.Cloneable { getInstance(java.util.TimeZone, java.util.Locale);
protected long time; public final java.util.Date getTime();
protected boolean isTimeSet; protected long getTimeInMillis();
protected java.util.Calendar(); public java.util.TimeZone getTimeZone();
protected public final boolean isSet(int);
java.util.Calendar(java.util.TimeZone,java.util.Locale);
public void roll(int, int);
public abstract void add(int, int);
public abstract void roll(int, boolean);
public boolean after(java.lang.Object);
public final void set(int, int);
public boolean before(java.lang.Object);
public final void set(int, int, int);
public final void clear();
public final void set(int, int, int, int, int);
public final void clear(int);
public final void set(int, int, int, int, int, int);
protected abstract void computeTime();
public final void setTime(java.util.Date);
public boolean equals(java.lang.Object);
public void setFirstDayOfWeek(int);
public final int get(int);
protected void setTimeInMillis(long);
public int getFirstDayOfWeek();
public void setTimeZone(java.util.TimeZone);
public static synchronized java.util.Calendar
public java.lang.String toString();
getInstance();
}
public static synchronized java.util.Calendar
getInstance(java.util.Locale);
La clase GregorianCalendar añade las constante BC y AD para la ERA, que representan respectivamente
antes y después de Jesucristo. Añade varios constructores que admiten como argumentos la información de
fecha/hora y, opcionalmente, la zona horaria. En el cuadro se muestra un ejemplo de uso de estas clases.
import java.util.*; System.out.println("Semana del mes:
"+gc.get(Calendar.WEEK_OF_MONTH));
public class PruebaFechas {
System.out.println("Fecha:
public static void main(String arg[]) {
"+gc.get(Calendar.DATE));
Date d = new Date();
System.out.println("Hora:
GregorianCalendar gc = new GregorianCalendar(); "+gc.get(Calendar.HOUR));
gc.setTime(d); System.out.println("Tiempo del dia:
"+gc.get(Calendar.AM_PM));
System.out.println("Era: "+gc.get(Calendar.ERA));
System.out.println("Hora del dia:
System.out.println("Year: "+gc.get(Calendar.YEAR));
"+gc.get(Calendar.HOUR_OF_DAY));
System.out.println("Month: "+gc.get(Calendar.MONTH)); System.out.println("Minuto:
System.out.println("Dia del mes: "+gc.get(Calendar.MINUTE));
"+gc.get(Calendar.DAY_OF_MONTH));
System.out.println("Segundo:
System.out.println("Día de la semana en "+gc.get(Calendar.SECOND));
mes:"+gc.get(Calendar.DAY_OF_WEEK_IN_MONTH));
System.out.println("Dif. horaria:
System.out.println("No de semana: "+gc.get(Calendar.ZONE_OFFSET));
"+gc.get(Calendar.WEEK_OF_YEAR)); }}
4.5.2. Clases DateFormat, SimpleDateFormat, TimeZone y SimpleTimeZone
DateFormat es una clase abstract del import java.util.*;
package java.text, no de java.util. La
import java.text.*;
razón es facilitar lo referente a la
internacionalización, un aspecto class SimpleDateForm {
importante de la conversión, que
permite dar formato a fechas y horas public static void main(String arg[]) throws ParseException {
de acuerdo a criterios locales. La SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy
clase dispone de métodos static para hh:mm:ss");

Programación en Java 46
convertir Strings representando fechas
SimpleDateFormat sdf2 = new SimpleDateFormat("dd-MM-yy");
y horas en objetos de la clase Date, y
viceversa. Date d = sdf1.parse("12-04-1968 11:23:45");
La clase SimpleDateFormat es la String s = sdf2.format(d);
única derivada de DateFormat. Es la
System.out.println(s);
que conviene usar. Se usa pasando al
constructor un String definiendo el }
formato a usar, como en el ejemplo
del cuadro. }

La clase TimeZone es también una clase abstract para definir la zona horaria. Los métodos de esta clase
tienen en cuenta el cambio de la hora en verano. La clase SimpleTimeZone deriva de TimeZone. Es la que
conviene usar. El valor por defecto de la zona horaria es el definido en la máquina en que se ejecuta el
programa. Los objetos de esta clase pueden ser usados con los constructores y algunos métodos de la
clase Calendar para establecer la zona horaria.

Programación en Java 47
5. EL AWT (ABSTRACT WINDOWS TOOLKIT)
El AWT es la parte de Java dispuesto para la construcción de interfaces gráficas de usuario (GUI). Desde la
versión 1.1 de Java, presentó un cambio notable, en particular en el modelo de eventos. La versión 1.2 ha
incorporado un modelo distinto de componentes llamado Swing.
Para construir una GUI hace falta un “contenedor” (container), componentes y un modelo de eventos. El
contenedor es la ventana o parte de ella donde se sitúan los componentes (botones, barras…) y se realizan
los dibujos. Se correspondería con un formulario o una picture box de VB. Los componentes serían los
controles de VB. Y el modelo de eventos se refiere al control del usuario sobre la aplicación al actuar sobre
los componentes, en general con ratón o teclado.
Cada vez que se realiza una acción, se produce un evento, que el SO transmite al AWT. Éste crea un objeto
de una clase de evento, derivada de AWTEvent. El evento es transmitido a un método para que lo gestione.
En VB el entorno de desarrollo crea automáticamente el procedimiento que va a gestionar el evento y el
usuario no tiene más que introducir el código. En Java esto es diferente: el componente u objeto que recibe
el evento debe “registrar” o indicar previamente qué objeto va a gestionar ese evento.
El modelo de eventos Java se basa en que los objetos sobre los que se producen los eventos (event
sources) “registran” los objetos que los gestionarán (event listeners). Los métodos se llamarán
automáticamente al producirse el evento. Para garantizar que los event listeners disponen de los métodos
apropiados para gestionar los eventos se les obliga a implementar una interface Listener dada. Así, las
interfaces Listener se corresponden con los tipos de eventos que se pueden producir.
Las capacidades gráficas del AWT resultan pobres y complicadas en comparación con VB, pero pueden ser
ejecutadas casi en cualquier ordenador y con cualquier SO.
El proceso típicos para construir una aplicación orientada a eventos sencilla, con GUI empieza identificando
los componentes que constituyen el interface de usuario (botones, menús…). Con esto, se crea una clase
para la aplicación que contenga la función main y una clase Ventana, subclase de Frame, que responda al
evento WindowClosing. La función main debe crear un objeto de la clase Ventana (en que se introducirán
los componentes identificados) y mostrarlo por pantalla con tamaño y posición correctos.
Al objeto Ventana hay que añadirle los componentes y menús que contenga, bien en el constructor, bien en
el main. También hay que definir los objetos Listener (que responden a los eventos, cuyas clases
implementan las interfaces Listener) para cada evento que deba soportarse. En aplicaciones pequeñas, el
propio objeto Ventana puede responder a los eventos de sus componentes. En programas más grandes se
puede crear uno o más objetos de clases especiales para ocuparse de los eventos. Finalmente, se deben
implementar los métodos de las interfaces Listener que vayan a gestionar los eventos.
5.1. Componentes y eventos del AWT Java
Como todas las clases Java, los componentes del AWT pertenecen a una jerarquía de clases; la que
muestra la figura. Todos los componentes derivan de Component, de la que pueden heredar. El package al
que pertenecen estas clases se llama java.awt.

La siguiente figura muestra la jerarquía de clases para los eventos de Java y algunos componentes. Por
conveniencia, estas clases se agrupan en el package java.awt.event.De sus características destaca que
todos los Components (excepto Window y derivados) deben añadirse a un Container. Un Container puede
ser añadido a otro Container. Para añadir un Component a un Container se usa el método add de Container
(containerName.add(componentName)). Los Containers de máximo nivel son Windows (Frames y Dialogs).
Los Panels y ScrollPanes siempre están dentro de otro Container. Un Component sólo puede estar en un
Container. Si está en un Container y se añade a otro, deja de estar en el primero. La clase Component tiene
una serie de funcionalidades básicas comunes (variables y métodos), heredadas por todas sus subclases.

Programación en Java 48
Todos los eventos en Java 1.2 son
objetos de clases de una jerarquía
de clases dada. La superclase
EventObject pertenece al package
java.util y de ella deriva la clase
AWTEvent, de la que dependen
todos los eventos de AWT.
Los eventos de Java pueden ser
de alto y bajo nivel. Los de alto
nivel también se llaman eventos
semánticos, porque la acción de la
que derivan tiene significado en sí
misma, en el contexto de las GUI.
Los eventos de bajo nivel son las acciones elementales que hacen posible los eventos de alto nivel.
Son eventos de alto nivel: los 4 que tienen que ver con pulsar botones o elegir comandos en menús
(ActionEvent), cambiar valores en barras de desplazamiento (AdjustmentEvent), elegir valores (ItemEvents)
y cambiar el texto (TextEvent). En la figura, los eventos de alto nivel aparecen en gris. Los de bajo nivel son
los que se producen con operaciones elementales del ratón, teclado, containers y windows.
Las 6 clases de eventos de bajo nivel son los eventos relacionados con componentes (ComponentEvent),
containers (ContainerEvent), con pulsar teclas (KeyEvent), con mover, arrastrar, pulsar y soltar el ratón
(MouseEvent), obtener o perder el focus (FocusEvent) y con las operaciones con ventanas (WindowEvent).
El modelo de eventos se complica cuando se quiere construir un tipo de componente propio, no estándar
del AWT. En ese caso hay que interceptar los eventos de bajo nivel de Java y adecuarlos al problema.
5.1.1. Relación entre Componentes y Eventos
La siguiente tabla muestra los componentes del AWT y los eventos específicos de cada uno.
Component Eventos Significado
generados
Button ActionEvent Clicar en el botón
Checkbox ItemEvent Seleccionar o deseleccionar un item
CheckboxMenuItem ItemEvent Seleccionar o deseleccionar un item
Choice ItemEvent Seleccionar o deseleccionar un item
Component ComponentEvent Mover, cambiar tamaño, mostrar u ocultar un componente
FocusEvent Obtener o perder el focus
KeyEvent Pulsar o soltar una tecla
MouseEvent Pulsar o soltar un botón del ratón; entrar o salir de un
componente; mover o arrastrar el ratón (tiene 2 Listener)
Container ContainerEvent Añadir o eliminar un componente de un container
List ActionEvent Hacer doble click sobre un item de la lista
ItemEvent Seleccionar o deseleccionar un item de la lista
MunuItem ActionEvent Seleccionar un item de un menú
Scrollbar AdjustementEvent Cambiar el valor de la scrollbar
TextComponent TextEvent Cambiar el texto
TextField ActionEvent Terminar de editar un texto pulsando Intro
Window WindowEvent Acciones sobre una ventana: abrir, cerrar, iconizar, restablecer e
iniciar el cierre
La relación entre componentes y eventos indicada en la tabla puede ser confusa si no se tiene en cuenta
que los eventos propios de una superclase de componentes pueden afectar a los componentes de sus
subclases. Por ejemplo, la clase TextArea no tiene eventos específicos o propios, pero puede recibir los de

Programación en Java 49
su superclase TextComponent. La siguiente tabla muestra los componentes del AWT y los tipos de eventos
que se pueden producir sobre cada uno, teniendo en cuenta también los específicos de sus superclases.
Entre ambas tablas se puede inferir qué tipos de eventos están soportados en Java y qué eventos puede
recibir cada componente del AWT. En la práctica, no todos los tipos de evento tienen el mismo interés.
Eventos que se pueden generar

AWT Components

Adjustement

Component

Container

Window
Mouse
Action

Focus

Item

Text
Key
Button X X X X X
Canvas X X X X
Checkbox X X X X X
Checkbox-MenuItem X
Choice X X X X X
Component X X X X
Container X X X X X
X
Dialog X X X X X
X
Frame X X X X X
Label X X X X
List X X X X X X
MenuItem X
Panel X X X X X
Scrollbar X X X X X
X
TextArea X X X X
X
TextField X X X X X
Window X X X X X X
5.1.2. Interfaces Listener y Clases Adapter
Vistos los eventos que se pueden producir, debe verse cómo gestionarlos según el modelo Java.
Por un lado, cada objeto que puede recibir un evento (event source), “registra” uno o más objetos para que
se gestionen (event listener). Esto se hace con un método donde eventSourceObject es el objeto en el que
se produce el evento y eventListenerObject es que deberá gestionar los eventos, en la forma,
eventSourceObject.addEventListener(eventListenerObject).
La relación entre ambos se establece a través de una interface Listener que la clase del eventListenerObject
debe implementar. Esta interface proporciona la declaración de los métodos que se llamarán cuando se
produzca el evento. La interface a implementar depende del tipo de evento. La siguiente tabla relaciona
tipos de eventos, con la interface a implementar para gestionarlos. Se indican también los métodos
declarados en cada interface.
La correspondencia entre eventos e interfaces Listener es que cada evento tiene su interface, excepto el
ratón que tiene 2: MouseListener y MouseMotionListener. La razón está en los eventos que se producen al
mover el ratón, que se producen con más frecuencia que las pulsaciones y por eficiencia se gestionan con
la interface MouseMotionListener. El nombre de la interface coincide con el del evento, sustituyendo Event
por Listener. Una vez registrado el objeto que gestionará el evento, que será de una clase que implemente
la interface Listener adecuada, se deben definir los métodos de dicha interface. Siempre hay que definir los
métodos de la interface, aunque algunos puedan estar “vacíos”. Un ejemplo es la implementación de la
interface WindowListener vista, en el que todos los métodos estaban vacíos excepto windowClosing.

Programación en Java 50
Evento Interface Listener Métodos Listener
ActionEvent ActionListener actionPerformed()

AdjustementEvent AdjustementListener adjustementValueChanged()


ComponentEvent ComponentListener componentHidden(), componentMoved(),
componentResized(),componentShown()
ContainerEvent ContainerListener componentAdded(), componentRemoved()
FocusEvent FocusListener focusGained(), focusLost()
ItemEvent ItemListener itemStateChanged()
KeyEvent KeyListener keyPressed(), keyReleased(), keyTyped()
MouseEvent MouseListener mouseClicked(), mouseEntered(), mouseExited(),
mousePressed(), mouseReleased()
mouseDragged(), mouseMoved()
MouseMotionListener
TextEvent TextListener textValueChanged()
WindowEvent WindowListener windowActivated(), windowDeactivated(), windowClosed(),
windowClosing(), windowIconified(), windowDeiconified(),
windowOpened()
Java ofrece ayudas para definir los métodos declarados en las interfaces Listener. Por ejemplo, las clases
Adapter, que existen para cada interface Listener con más de un método. Su nombre se construye a partir
del nombre de la interface, sustituyendo “Listener” por “Adapter”. Hay 7 clases Adapter: ComponentAdapter,
ContainerAdapter, FocusAdapter, KeyAdapter, MouseAdapter, MouseMotionAdapter y WindowAdapter.
La clase Adapter derivan de Object. Son clases // Fichero VentanaCerrable2.java
predefinidas que contienen definiciones vacías
import java.awt.*; import java.awt.event.*;
de los métodos de la interface. Para crear un
objeto que responda al evento, en vez de crear class VentanaCerrable2 extends Frame {
una clase que implemente Listener, se crea una
public VentanaCerrable2() { super(); } // constructores
clase que derive del Adapter correspondiente y
redefina sólo los métodos de interés. P. ej. la public VentanaCerrable2(String title) {
clase VentanaCerrable vista, se podía haber
definido como en el cuadro. super(title);

Las sentencias que definen la clase auxiliar setSize(500,500);


(helper class) derivada de WindowAdapter CerrarVentana cv = new CerrarVentana();
indican que hereda la definición de los métodos
de la interface WindowListener. Lo que hay que this.addWindowListener(cv);
hacer es redefinir el único método necesario }
para cerrar ventanas. El constructor de la clase
VentanaCerrable crea un objeto de clase } // Fin de la clase VentanaCerrable2
CerrarVentana y lo registra como event listener class CerrarVentana extends WindowAdapter {
en la sentencia (this.addWindowListener). La
palabra this es opcional. Si no se incluye, se void windowClosing(WindowEvent we) { System.exit(0); }
supone que el event source es el objeto de la } // fin de la clase CerrarVentana
clase en que se produce el evento: la ventana.
Otra forma de responder al evento que se produce cuando el usuario desea cerrar la ventana es usar clases
anónimas. Para gestionar eventos sólo hace falta un objeto registrado como event listener con los métodos
adecuados de Listener.
Las clases anónimas son útiles si sólo se necesita un // Fichero VentanaCerrable3.java
objeto de la clase: este caso. La nueva definición de la
import java.awt.*; import java.awt.event.*;
clase VentanaCerrable podría ser la mostrada en el
cuadro. El objeto event listener se crea justo al pasarlo class VentanaCerrable3 extends Frame {
como argumento al método addWindowListener. Se
crea un nuevo objeto porque aparece new. public VentanaCerrable3() { super(); }
public VentanaCerrable3(String title) {
Debe tenerse en cuenta que no se crea un nuevo objeto

Programación en Java 51
de WindowAdapter (clase abstract), sino que se
super(title);
extiende WindowAdapter, aunque “extends” no
aparezca. Esto lo indican las llaves que se abren tras setSize(500,500);
new WindowAdapter().
this.addWindowListener(new WindowAdapter() {
Los paréntesis podrían contener argumentos para el
public void windowClosing() {System.exit(0);}
constructor de WindowAdapter, en caso que los
necesitase. En la siguiente sentencia se redefine el });
método windowClosing. En la siguiente línea se cierran
las llaves de la clase anónima y el paréntesis del }
método addWindowListener. } // fin de la clase VentanaCerrable
5.2. Clase Component
La clase Component es una clase abstract de la que derivan las clases del AWT. Los métodos de esta clase
se conocen como componentes gráficos o componentes que permiten construir GUI y generar eventos. Son
heredados por todos los componentes del AWT. La tabla muestra algunos de sus métodos característicos.
Métodos de Component Función
boolean isVisible(), void setVisible(boolean) Chequean o establecen visibilidad a un componente
Indican si un componente se está viendo. Debe ser
boolean isShowing()
visible y su container estar mostrándose
Permiten saber si un componente está activado y
boolean isEnabled(), void setEnabled(boolean)
activarlo o desactivarlo
Point getLocation(), Point getLocationOnScreen() Obtienen la posición de la esquina superior izqda..
de un componente respecto al padre o la pantalla
Desplazan un componente a la posición dada
void setLocation(Point), void setLocation(int x, int y)
respecto al container o componente-padre
Dimension getSize(), void setSize(int w, int h), void Obtienen o establecen el tamaño de un componente
setSize(Dimension d)
void setBounds(int x, int y, int width, int height), void Obtienen o establecen la posición y el tamaño de un
setBounds(Rectangle), Rectangle getBounds() componente
Invalidate marca un componente y contenedores
invalidate(), validate(), doLayout()
indicando que hay que volver a aplicar Layout
Manager; validate asegura que está bien aplicado y
doLayout hace que se aplique
paint(Graphics), repaint() y update(Graphics) Métodos gráficos para dibujar en la pantalla
setBackground(Color), setForeground(Color) Métodos para establecer los colores por defecto
En las declaraciones de los métodos de la clase aparecen las clases Point, Dimension y Rectangle. La clase
java.awt.Point tiene 2 variables miembro int; x e y. Dimension tiene 2 variables miembro int; height y width.
Rectangle 4 variables int (height, width, x e y). Las 3 son subclases de Object. Además de los métodos de la
tabla, la clase Component tiene otros muchos. Entre otras, permiten controlar colores, fuentes y cursores.
5.2.1. Clase Container
La tabla muestra algunos de los métodos de la clase container.
Métodos de Container Función
Component add(Component) Añade un componente al container
doLayout() Ejecuta el algoritmo de ordenación del layout manager
Component getComponent(int) Obtiene el n-ésimo componente en el container
Component getComponentAt(int, int), Component Obtiene el componente con un determinado punto
getComponentAt(Point)
int getComponentCount() Obtiene el número de componentes en el container
Component[] getComponents() Obtiene los componentes en este container
remove(Component), remove(int), removeAll() Elimina el componente especificado
setLayout(LayoutManager) Determina el layout manager para este container

Programación en Java 52
La clase Container es una clase muy general. No suele instarciar objetos. Sus métodos son heredados por
Frame y Panel, que sí instancian objetos con mucha frecuencia. Los containers mantienen la lista de objetos
que se les va añadiendo. Cuando se añade uno se incorpora al final de la lista, salvo que se especifique una
posición dada. Tiene mucha importancia lo relacionado con los Layout Managersmás adelante.
5.2.1.1. Clase Panel y ScrollPane
Un Panel es un Container de propósito general. Se puede usar para contener otros componentes o para
crear una subclase para alguna finalidad más específica. Por defecto, el Layout Manager de Panel es
FlowLayout. Los Applets son subclases de Panel. La tabla muestra los métodos más importantes usados
con Panel, algunos heredados de Componet y Container, Panel no tiene métodos propios.
Métodos de Panel Función
Panel(), Panel(LayoutManager miLM) Constructores de Panel
Métodos heredados de Container y Component Función
add(Component), add(Component, int) Añade componentes al panel
setLayout(), getLayout() Establece o permite obtener el layout manager usado
validate(), doLayout() Para reorganizar los componentes después de algún
cambio. Es mejor utilizar validate
remove(int), remove(Component), removeAll() Eliminan componentes
Dimension getMaximumSize(), Dimension Permite obtener los tamaños máximo, mínimo y
getMinimumSize(), Dimension getPreferredSize() preferido
Insets getInsets()
Un Panel puede contener otros Panel. Es una ventaja respecto a otros containers, que al ser de máximo
nivel no lo permiten. Insets es una clase que deriva de Object. Sus variables son top, left, botton, right.
Representa el espacio libre en los bordes de un Container. Se establece llamando al constructor adecuado
del Layout Manager.
Por su parte, un ScrollPane es una especie de ventana de tamaño limitado en la que se puede mostrar un
componente de mayor tamaño con dos Scrollbars, una horizontal y otra vertical. El componente puede ser,
por ejemplo, una imagen. Las Scrollbars son visibles sólo si son necesarias (por defecto). Las constantes de
ScrollPane son SCROLLBARS_AS_NEEDED, SCROLLBARS_ALWAYS, SCROLLBARS_NEVER. Los
ScrollPanes no generan eventos. La tabla muestra algunos métodos de esta clase. En caso en que no
aparezcan scrollbars (SCROLLBARS_NEVER) será necesario desplazar el componente (hacer scrolling)
desde programa, con el método setScrollPosition.
Métodos de ScrollPane Función
ScrollPane(), ScrollPane(int scbs) Constructores que pueden incluir las ctes.
Dimension getViewportSize(), int Obtiene el tamaño del ScrollPane y la altura y anchura
getHScrollbarHeight(), int getVScrollbarWidth() de las barras de desplazamiento
setScrollPosition(int x, int y), setScrollPosition(Point Permiten establecer u obtener la posición del
p), Point getScrollPosition() componente
setSize(int, int), add(Component) Heredados de Container, permiten establecer el
tamaño y añadir un componente
5.2.1.2. Clase Window, Frame, Dialog y FileDialog
Los objetos de la clase Window son ventanas de máximo nivel, sin bordes ni barra de menús. Interesan más
sus derivadas, Frame y Dialog. Los métodos más útiles, se muestran en la tabla.
Métodos de Window Función
toFront(), toBack() Para desplazar la ventana hacie adelante y hacia atrás en la pantalla
show() Muestra la ventana y la trae a primer plano
pack() Hace que los componentes se reajusten al tamaño preferido
La clase Frame es una ventana con borde que puede tener barra de menús. Si una ventana depende de
otra es mejor usar una Window a Frame. La tabla muestra métodos comunes de la clase Frame. Además de
los métodos citados, se usan mucho show, pack, toFront y toBack, heredados de Window.

Programación en Java 53
Métodos de Frame Función
Frame(), Frame(String title) Constructores
String getTitle(), setTitle(String) Obtienen o determinan el título de la ventana
MenuBar getMenuBar(), setMenuBar(MenuBar), Permite obtener, establecer o eliminar la barra de
remove(MenuComponent) menús
Image getIconImage(), setIconImage(Image) Obtienen o determinan el icono que apareceré en la
barra de títulos
setResizable(boolean), boolean isResizable() Determinan o chequean si se puede cambiar el tamaño
dispose() Libera los recursos de una ventana. Todos los
componentes de la ventana son destruidos
La clase Dialog implementa ventanas que dependen de otras (un Frame). Si una Frame se cierra, se cierran
sus Dialog; si se iconifica, desaparecen; si se restablece, aparecen de nuevo. Este comportamiento es
automático. Los Applets estándar no soportan Dialogs porque no son Frames de Java. Los Applets que
abren Frames sí soportan Dialogs. Un Dialog modal require la atención inmediata del usuario: no se puede
hacer nada hasta cerrarlo. Por defecto, los Dialogs son no modales. La tabla muestra los métodos más
importantes de Dialog.
Métodos de Dialog Función
Dialog(Frame fr), Dialog(Frame fr, boolean mod), Constructores
Dialog(Frame fr, String title), Dialog(Frame fr, String
title, boolean mod)
String getTitle(), setTitle(String) Permite obtener o determinar el título
boolean isModal(), setModal(boolean) Pregunta o determina si el Dialog es modal o no
boolean isResizable(), setResizable(boolean) Pregunta o determina si puede cambiar el tamaño
show() Muestra y trae a primer plano el Dialog
La clase FileDialog deriva de Dialog y muestra un diálogo para seleccionar un fichero. Las constantes
enteras LOAD (abrir ficheros para lectura) y SAVE (abrir ficheros para escritura) definen el modo de
apertura del fichero. La tabla muestra métodos de la clase. La interface java.io.FilenameFilter a las clases
que la implementan les permite filtrar ficheros de un directorio.
Métodos de FileDialog Función
FileDialog(Frame parent), FileDialog(Frame parent, String Constructores
title), public FileDialog(Frame parent, String title, int mode)
int getMode(),setMode(int mode) Modo de apertura (SAVE o LOAD)
String getDirectory(), String getFile() Obtiene el directorio o fichero elegido
setDirectory(String dir), setFile(String file) Determina el directorio o fichero elegido
FilenameFilter getFilenameFilter(), Determina o establece el filtro para los
setFilenameFilter(FilenameFilter filter) ficheros
5.2.2. Clases Button, Canvas, Checkbox y CheckboxGroup
Un Button de clase Button puede recibir seis tipos de eventos de clase ActionEvent, generados al hacer clic.
El aspecto de un Button depende de la plataforma, pero la funcionalidad es la misma. Se puede cambiar el
texto, fuente, foreground y backgroung color y establecer que esté activo o no.
Métodos de Button Función
Button(String label) y Button() Constructores
setLabel(String str), String getLabel() Permite establecer u obtener la etiqueta del Button
addActionListener(ActionListener al), Permite registrar el objeto que gestionará los eventos, que
removeActionListener(ActionListener al) debe implementar ActionListener
setActionCommand(String cmd), String Establece y recupera un nombre para el objeto Button
getActionCommand() independiente del label y del idioma

Programación en Java 54
Un Canvas es una zona rectangular de pantalla en la que dibujar y que puede generar eventos. Las Canvas
permiten realizar dibujos, mostrar imágenes y crear componentes a medida, de modo que muestren un
aspecto similar en todas las plataformas. La tabla muestra los métodos de la clase Canvas.
Desde los objetos de Canvas se puede llamar a los métodos paint y repaint de Component. Con frecuencia
conviene redefinir los métodos de Component getPreferredSize, getMinimumSize y getMaximumSize, que
devuelven un objeto de clase Dimension. El LayoutManager utiliza estos valores. La clase Canvas no tiene
eventos propios, pero puede recibir los ComponentEvent de su superclase Component.
Métodos de Canvas Función
Canvas() El único constructor de la clase
paint(Graphics g); Dibuja un rectángulo con el color de background. Lo normal es que las
subclases redefinan el método
Los objetos de la clase Checkbox son botones de opción o selección con 2 posibles valores: on y off. Al
cambiar la selección de un Checkbox se produce un ItemEvent. La clase CheckboxGroup permite la opción
de agrupar Checkbox de modo que sólo uno esté en on (al comienzo pueden estar todos off). Se
corresponde con los botones de opción de VB. La tabla muestra los métodos más importantes.
Métodos de Checkbox Función
Checkbox(), Checkbox(String), Checkbox(String, Constructores. Algunos permiten establecer la
boolean), Checkbox(String, boolean, CheckboxGroup), etiqueta, si está o no seleccionado y si pertenece
Checkbox(String, CheckboxGroup, boolean) a un grupo
addItemListener(ItemListener), Registra o elimina los objetos que gestionarán los
removeItemListener(ItemListener) eventos ItemEvent
setLabel(String), String getLabel() Establece u obtiene la etiqueta del componente
setState(boolean), boolean getState() Establece u obtiene el estado (true / false) según
esté seleccionado o no
setCheckboxGroup(CheckboxGroup), CheckboxGroup Establece u obtiene el grupo al que pertenece el
getCheckboxGroup() Checkbox
Métodos de CheckboxGroup Función
CheckboxGroup() Constructores de CheckboxGroup
Checkbox getSelectedCheckbox(), Obtiene o establece el componente seleccionado
setSelectedCheckbox(Checkbox box) de un grupo
Cuando el usuario actúa sobre un objeto Checkbox se ejecuta el método itemStateChanged(), único método
de la interface ItemListener. Si hay varias checkboxes cuyos eventos se gestionan en un mismo objeto y se
quiere saber el que recibe el evento, se puede usar el método getSource del evento EventObject.
También se puede usar el método getItem de ItemEvent, cuyo valor de retorno es un Object con el label del
componente (para convertirlo a String habría que hacer cast). Para crear un grupo o conjunto de botones de
opción (sólo uno podrá estar activo), se debe crear un objeto de clase CheckboxGroup. Este objeto no tiene
datos, sólo sirve de identificador. Cuando se crean los objetos Checkbox se pasa a los constructores el
objeto CheckboxGroup del grupo al que pertenecerán. Cuando se selecciona un Checkbox de un grupo se
producen 2 eventos: uno por el elemento seleccionado y otro por haber perdido la selección el elemento
seleccionado anteriormente.
5.2.3. Clases Choice, Label, List y Scrollbar
La clase Choice permite elegir un item de una lista desplegable. Los objetos Choice ocupan menos espacio
en pantalla que los Checkbox. Al elegir un item se genera un evento ItemEvent. Un index permite
determinar un elemento de la lista (empezando en 0). La tabla muestra algunos métodos de la clase. En
este sentido es similar a las clases Checkbox y CheckboxGroup, así como a los menús de selección.
La clase Label introduce en un container un texto no seleccionable ni editable, que por defecto se alinea por
la izquierda. Define las constantes Label.CENTER, Label.LEFT y Label.RIGHT para alinear el texto. La
elección de la fuente, colores, tamaño y posición se realiza con los métodos heredados de Component y no
tiene más eventos que los de Component.
La clase List define una zona de pantalla con varias líneas, de las que se muestran sólo algunas y de las
que se puede hacer una selección simple o múltiple. Las List generan eventos de la clase ActionEvents (al
pulsar 2 veces un item o return) e ItemEvents (al seleccionar o deseleccionar un item). Al gestionar el

Programación en Java 55
evento ItemEvent se puede preguntar si el usuario estaba pulsando a la vez alguna tecla (Alt, Ctrl, Shift).
List se diferencia de Choices en que muestra varios items a la vez y permite hacer selección múltiples.
Métodos de Choice Función
Choice() Constructor
addItemListener(ItemListener), Establece o elimina un ItemListener
removeItemListener(ItemListener)
add(String), addItem(String) Añade un elemento a la lista
insert(String label, int index) Inserta un elemento con un label en la posición
indicada
int getSelectedIndex(), String getSelectedItem() Obtiene el index o label del elemento de la lista
int getItemCount() Obtiene el número de elementos
String getItem(int) Obtiene el label a partir del index
select(int), select(String) Selecciona un elemento por el index o el label
removeAll(), remove(int), remove(String) Elimina todos o uno de los elementos de la lista
Métodos de Label Función
Label(String lbl), Label(String lbl, int align) Constructores
setAlignement(int align), int getAlignement() Establecer u obtener la alineación del texto
setText(String txt), String getText() Establecer u obtener el texto del Label
Métodos de List Función
List(), List(int nl), List(int nl, boolean mult) Constructor: por defecto una línea y selección simple
add(String), add(String, int), addItem(String), Añadir un item. Por defecto al final
addItem(String, int)
addActionListener(ActionListener), Registra los objetos que gestionarán los dos tipos de
addItemListener(ItemListener) eventos soportados
insert(String, int) Inserta un nuevo elemento en la lista
replaceItem(String, int) Sustituye el item en posición int por el String
delItem(int), remove(int), remove(String), removeAll() Eliminar uno o todos los items de la lista
int getItemCount(), int getRows() Obtener el número de items o el número de items
visibles
String getItem(int), String[] getItems() Obtiene uno o todos los elementos de la lista
int getSelectedIndex(), String getSelectedItem(), int[] Obtiene el/los elementos seleccionados
getSelectedIndexes(), String[] getSelectedItems()
select(int), deselect(int) Selecciona o elimina la selección de un elemento
boolean isIndexSelected(int), Boolean Indica si un elemento está seleccionado o no
isItemSelected(String)
boolean isMultipleMode(), setMultipleMode(boolean) Pregunta o establece modo de selección múltiple
int getVisibleIndex(), makeVisible(int) Indicar o establecer si un item es visible
Un Scrollbar es una barra de desplazamiento con cursor que permite introducir y modificar valores, entre un
valor mínimo y máximo, con pequeños y grandes incrementos. Las Scrollbars de Java se usan tanto como
“sliders” o barras de desplazamiento aisladas (al estilo de VB), como unidas a una ventana en posición
vertical y/u horizontal para mostrar una cantidad de información superior a la que cabe en la ventana.
La clase Scrollbar tiene dos constantes, Scrollbar.HORIZONTAL y Scrollbar.VERTICAL, que indican la
posición de la barra. Cambiar el valor del Scrollbar genera un AdjustementEvent. En el constructor general,
el parámetro pos es la constante que indica la posición de la barra (horizontal o vertical); el rango es el
intervalo entre los valores mínimo min y máximo max; el parámetro vis (visibleAmount) el tamaño del área
visible en el caso en que las Scrollbars se usen en TextAreas.

Programación en Java 56
En ese caso, el tamaño del cursor representa la relación entre el área visible y el rango, como es habitual en
aplicaciones WS. El valor seleccionado lo da la variable value. Cuando es igual a min el área visible
comprende el inicio del rango; cuando es max el área visble comprende el final del rango. Cuando la
Scrollbar se va a usar aislada (como slider), se debe anular visibleAmount.
Las variables Unit Increment y Block Increment representan, respectivamente, los incrementos pequeño y
grande. Por defecto, Unit Increment es “1” y Block Increment “10”, mientras que min es “0” y max “100”. Al
cambiar el valor de una Scrollbar se genera un evento AdjustementEvent y se ejecuta el único método de la
interface AdjustmentListener, que es adjustmentValueChanged.
Métodos de Scrollbar Función
Scrollbar(), Scrollbar(int pos), Scrollbar(int pos, int val, int Constructores
vis, int min, int max)
addAdjustmentListener(AdjustmentListener) Registra el objeto que gestionará los eventos
int getValue(), setValue(int) Permiten obtener y fijar el valor
setMaximum(int), setMinimum(int) Establecen los valores máximo y mínimo
setVisibleAmount(int), int getVisibleAmount() Establecen y obtienen el tamaño del área visble
setUnitIncrement(int), int getUnitIncrement() Establecen y obtienen el incremento pequeño
setBlockIncrement(int), int getBlockIncrement() Establecen y obtienen el incremento grande
setOrientation(int), int getOrientation() Establecen y obtienen la orientación
setValues(int value, int vis, int min, int max) Establecen los parámetros de la barra
5.2.4. Clases TextComponent, TextArea y TextField
La clase TextComponent es general y deriva en las clasesTextArea y TextField, que muestran texto editable
y seleccionable. Su diferencia es que TextField sólo puede tener una línea y TextArea varias. Además,
TextArea ofrece posibilidades de edición de texto adicionales (fuente, color de foreground y background).
Sólo la clase TextField genera ActionEvents, pero al heredar ambas de TextComponent ambas pueden
recibir TextEvents. La tabla muestra algunos métodos de las clases TextComponent, TextField y TextArea.
No se pueden crear objetos de la clase TextComponent porque su constructor no es public.
Métodos heredados de TextComponent Función
String getText() y setText(String str) Permiten establecer u obtener el texto del componente
setEditable(boolean b), boolean isEditable() Hace que el texto sea editable o pregunta por ello
setCaretPosition(int n), int getCaretPosition() Fija la posición del punto de inserción o la obtiene
String getSelectedText(), int getSelectionStart(), Obtiene texto seleccionado, inicio y final de la selección
int getSelectionEnd()
selectAll(), select(int start, int end) Selecciona todo o parte del texto
Métodos de TextField Función
TextField(), TextField(int ncol), TextField(String Constructores
s), TextField(String s, int ncol)
int getColumns(), setColoumns(int) Obtiene o establece el nº de columnas del TextField
setEchoChar(char c), char getEchoChar(), Establece, obtiene o pregunta por el carácter usado para
boolean echoCharIsSet() passwords, para que no se pueda leer lo tecleado
Métodos de TextArea Función
TextArea(), TextArea(int nfil, int ncol), Constructores
TextArea(String text), TextArea(String text, int
nfil, int ncol)
setRows(int), setColumns(int), int getRows(), int Establece u obtiene el nº de filas y columnas
getColumns()
append(String str), insert(String str, int pos), Añadir texto al final, insertarlo en una posición
replaceRange(String s, int i, int f) determinada y reemplazar un texto determinado

Programación en Java 57
TextComponent recibe eventos TextEvent, por lo que también los reciben sus clases derivadas. Este evento
se produce al modificar el texto del componente. TextField soporta también el evento ActionEvent,
producido al terminar de editar la única línea de texto pulsando Intro. Las cajas de texto pueden recibir
también los eventos de sus superclases, en concreto los de Component Focus, Mouse y KeyEvent.
Estos permiten capturar las teclas pulsadas por el usuario y tomar acción. Por ejemplo, si el usuario debe
teclear un número en un TextField, se puede crear una función que vaya capturando las pulsaciones y
rechace las no numéricas. Cuando se cambia desde programa el número de filas y columnas de un
TextField o TextArea, hay que llamar al método validate de Component, para que vuelva a aplicar el
LayoutManager correspondiente. Los tamaños fijados por el usuario tienen carácter de recomendaciones o
tamaños preferidos, que el LayoutManager puede cambiar si es necesario.
5.3. Clases EventObject y AWTEvent
Los métodos de las interfaces Listener relacionados con el AWT tienen como argumento único un objeto de
alguna clase derivada de java.awt.AWTEvent. La clase AWTEvent deriva de java.util.EventObject.
La clase AWTEvent no define métodos, pero hereda de EventObject el método getSource que devuelve una
referencia al objeto que generó el evento. Las clases de eventos que descienden de AWTEvent definen
métodos similares a getSource con valores de retorno menos genéricos. Así, la clase ComponentEvent
define el método getComponent, cuyo valor de retorno es un objeto de la clase Component.
5.3.1. Clases ActionEvent, Adjustment Event, ItemEvent y TextEvent
La clase ActionEvent engloba los eventos ActionEvent originados al pulsar con el ratón en un botón, elegir
un comando de un menú, hacer doble clic en un elemento de una lista y al pulsar Intro al introducir texto en
una caja de texto (Button, MenuItem, List, TextField). El método String getActionCommand devuelve el texto
asociado a la acción que provocó el evento.
El texto se puede fijar con el método setActionCommand(String str) de las clases Button y MenuItem. Si no
se hace, el método getActionCommand devuelve el texto mostrado por el componente (etiqueta). Para
objetos con varios items el valor devuelto es el nombre del item seleccionado.
El método getModifiers devuelve un entero representando una constante definida en ActionEvent
(SHIFT_MASK, CTRL_MASK, META_MASK y ALT_MASK). Estas constantes determinan si se pulsó una
de estas teclas al hacer clic. P. ej., si se estaba pulsando la tecla CTRL la siguiente expresión no es 0:
actionEvent.getModifiers() & ActionEvent.CTRL_MASK.
La clase AdjustmentEvent gestiona eventos AdjustementEvent, los generados al cambiar el valor (entero)
de un Scrollbar. Hay 5 tipos de AdjustementEvent: track (arrastrar el cursor del Scrollbar); unit increment,
unit decrement (clic en las flechas del Scrollbar) y block increment, block decrement (clic encima o debajo
del cursor). La tabla muestra algunos métodos de la clase. Las constantes UNIT_INCREMENT,
UNIT_DECREMENT, BLOCK_INCREMENT, BLOCK_DECREMENT, TRACK permiten saber el tipo de
acción producida, comparando con el valor de retorno de getAdjustementType.
Métodos de AdjustementEvent Función
Adjustable getAdjustable() Devuelve el Component que generó el evento (implementa Adjustable)
int getAdjustementType() Devuelve el tipo de adjustement
int getValue() Devuelve el valor de la Scrollbar después del cambio
La clase ItemEvent engloba los ItemEvent. Se dan si ciertos componentes (Checkbox, CheckboxMenuItem,
Choice y List) cambian de estado (on/off). Estos componentes implementan la interface ItemSelectable. La
clase ItemEvent define las constantes enteras SELECTED y DESELECTED, que se pueden usar para
comparar con el valor devuelto por el método getStateChange.
Métodos de ItemEvent Función
Object getItem() Devuelve el objeto donde se originó el evento
ItemSelectable getItemSelectable() Devuelve el objeto ItemSelectable donde se originó el evento
int getStateChange() Devuelve la constante SELECTED o DESELECTED de ItemEvent
La clase TextEvent. Se produce un TextEvent cada vez que cambia algo en un TextComponent (TextArea y
TextField). Si se desea evitar ciertos caracteres hay que gestionar los eventos correspondientes. La
interface TextListener tiene un único método: void textValueChanged(TextEvent te). No hay métodos
propios de TextEvent. Se puede usar el método Object getSource, heredado de EventObject.

Programación en Java 58
5.3.2. Clase ComponentEvent
Los eventos ComponentEvent se generan cuando un Component de cualquier tipo se muestra, oculta o
cambia de posición o tamaño. Los eventos mostrar u ocultar ocurren al llamar al método setVisible(boolean)
del Component, pero no cuando se minimiza la ventana. Otro método útil de la clase es Component
getComponent que devuelve el componente que generó el evento. Puede usarse como getSource.
5.3.2.1. Clases ContainerEvent, FocusEvent y WindowEvent
Clase ContainerEvent. Los ContainerEvents se generan cuando un Component se añade o elimina de un
Container. Sólo tienen un papel de aviso; no es necesario gestionarlos para realizar la operación. Los
métodos de esta clase son Component getChild, que devuelve el Component añadido o eliminado y
Container getContainer, que devuelve el Container que generó el evento.
Clase FocusEvent. El Focus está relacionado con la posibilidad de sustituir al ratón por el teclado en ciertas
operaciones. De los componentes que aparecen en pantalla, en un momento dado sólo uno puede recibir
acciones del teclado; el que tiene el Focus. Éste aparece diferente de los demás y el Focus se cambia con
la tecla Tab o el ratón. Se produce un FocusEvent cada vez que un componente gana o pierde el Focus. El
método requestFocus de Component permite hacer desde programa que un componente obtenga el Focus.
El método boolean isTemporary, de FocusEvent, indica si la pérdida del Focus es o no temporal. El método
Component getComponent es heredado de ComponentEvent, y permite conocer el componente que ha
ganado o perdido el Focus. Las constantes de esta clase FOCUS_GAINED y FOCUS_LOST permiten saber
el tipo de evento FocusEvent producido.
Clase WindowEvent. Se produce un WindowEvent al abrir, cerrar, iconizar, restaurar, activar o desactivar
una ventana. La interface WindowListener ofrece los 7 métodos siguientes, con los que responder al evento:
Métodos de WindowsListener Función
void windowOpened(WindowEvent we); antes de mostrarla por primera vez
void windowClosing(WindowEvent we); al recibir una solicitud de cierre
void windowClosed(WindowEvent we); después de cerrar la ventana
void windowIconified(WindowEvent we);
void windowDeiconified(WindowEvent we);
void windowActivated(WindowEvent we);
void windowDeactivated(WindowEvent we);
El uso frecuente de WindowEvent es cerrar ventanas (por defecto, los objetos de Frame sólo se cierran con
Ctrl+Alt+Supr). También se usa para detener threads y liberar recursos al iconizar una ventana (p. ej. con
animaciones) y comenzar de nuevo al restaurarla. WindowEvent define las constantes WINDOW_CLOSED,
WINDOW_OPENED, WINDOW_CLOSING, WINDOW_ACTIVATED, WINDOW_DEACTIVATED,
WINDOW_ICONIFIED y WINDOW_DEICONIFIED, para identificar el tipo de evento. El método Window
getWindow de WindowEvent devuelve la ventana que generó el evento. Se usa en vez de getSource.
5.3.2.2. Clases InputEvent, MouseEvent y KeyEvent
La clase InputEvent engloba los eventos de ratón y teclado. Dispone métodos para detectar botones del
ratón y teclas especiales pulsadas. Botones y teclas dan significado a las acciones del usuario. La clase
InputEvent define constantes (BUTTON1_MASK, BUTTON2_MASK, BUTTON3_MASK, SHIFT_MASK,
ALT_MASK, CTRL_MASK) para saber qué teclas o botones estaban pulsados al darse el evento.
Métodos de InputEvent a heredar Función
boolean isShiftDown(), boolean Devuelven un boolean con información sobre si esa tecla estaba
isAltDown(), boolean isControlDown() pulsada o no
int getModifiers() Da información con máscara de bits de teclas y botones pulsados
long getWhen() Devuelve la hora en que se produjo el evento
Se produce un MouseEvent cuando el cursor movido por el ratón entra o sale de un componente visible en
pantalla, clicar o al pulsar o soltar un botón del ratón. Los métodos de la interface MouseListener se
relacionan con estas acciones (mouseClicked, mouseEntered, mouseExited, mousePressed y
mouseReleased). Todos son void y reciben como argumento un objeto MouseEvent. La tabla muestra
algunos métodos de la clase MouseEvent.

Programación en Java 59
Métodos heredados de la clase MouseEvent Función
int getClickCount() Devuelve el número de clicks en ese evento
Point getPoint(), int getX(), int getY() Devuelven la posición del ratón al producirse el evento
boolean isPopupTrigger() Indica si este evento es el que dispara los menús popup
La clase MouseEvent define una serie de constantes enteras para identificar los tipos de eventos
producidos MOUSE_CLICKED, MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_MOVED,
MOUSE_ENTERED, MOUSE_EXITED, MOUSE_DRAGGED.
Además, el método Component getComponent, heredado de ComponentEvent, devuelve el componente
sobre el que se ha producido el evento. Los eventos MouseEvent disponen de una segunda interface para
su gestión, MouseMotionListener, cuyos métodos reciben como argumento un evento de MouseEvent.
Éstos se relacionan con el movimiento del ratón. Se llama a un método de MouseMotionListener cuando el
usuario utiliza el ratón (o dispositivo similar) para mover el cursor o arrastrarlo en la pantalla. Los métodos
de la interface MouseMotionListener son mouseMoved y mouseDragged.
Clase KeyEvent. Se produce un KeyEvent al pulsar una tecla. Como el teclado no es un componente del
AWT, es el objeto que tiene el focus en ese momento quien genera los eventos KeyEvent (el objeto que es
event source). Hay 2 tipos de KeyEvents, key-typed y key-pressed.
Key-typed, representa la introducción de un carácter Unicode y key-pressed y key-released, representan
pulsar o soltar una tecla. Son importantes para teclas que no representan caracteres (p.ej. F1).
Para estudiar estos eventos interesan las Virtual KeyCodes (VKC), constantes que se corresponden con las
teclas físicas del teclado, sin considerar minúsculas (que no tienen VKC). Se indican con el prefijo VK_,
como VK_SHIFT o VK_A. La clase KeyEvent (del package java.awt.event) define constantes VKC para las
teclas del teclado. P.ej. escribir la letra "A" genera 5 eventos: key-pressed VK_SHIFT, key-pressed VK_A,
key-typed "A", key-released VK_A, y key-released VK_SHIFT. Las constantes de InputEvent que permiten
identificar las teclas modificadoras y botones del ratón se pueden aplicar las operaciones lógicas de bits
para detectar combinaciones de teclas o pulsaciones múltiples.
Métodos de KeyEvent Función
int getKeyChar() Obtiene el carácter Unicode asociado con el evento
int getKeyCode() Obtiene el VKC de la tecla pulsada o soltada
boolean isActionKey() Indica si la tecla del evento es una ActionKey (HOME, END …)
String getKeyText(int keyCode) Devuelve un String que describe el VKC, tal como "HOME", "F1" o
"A". Se definen en el fichero awt.properties
String getKeyModifiersText(int Devuelve un String que describe las teclas modificadoras, tales
modifiers) como"Shift" o "Ctrl+Shift" (fichero awt.properties)
5.4. Menús
Los Menus Java derivan de MenuComponent y presentan
comportamiento similar a los componentes, pues aceptan Events. La
figura muestra la jerarquía de clases de los Menus Java 1.1.
Para crear un Menú se debe crear primero un MenuBar; después los
Menus y los MenuItem. Los MenuItems se añaden al Menu
correspondiente; los Menus a la MenuBar y la MenuBar a un Frame.
La MenuBar se añade a un Frame con el método setMenuBar, de
Frame. También puede añadirse un Menu a otro para crear un
submenú, razón por la que la clase Menu deriva de MenuItem.
5.4.1 Clase MenuShortcut, MenuComponent, MenuBar, MenuItem y Menu
La clase java.awt.MenuShortcut (derivada de Object) representa las teclas aceleradoras que pueden usarse
para activar menús desde teclado, sin ratón.
Métodos de MenuShortcut Función
MenuShortcut(int key) Constructor
int getKey() Obtiene el código virtual de la tecla utilizada como shortcut

Programación en Java 60
Se establece un Shortcut creando un objeto de esta clase con el constructor MenuShortcut(int vk_key) y
pasándoselo al constructor adecuado de MenuItem. Los MenuShortcut de Java están restringidos al uso la
tecla control (CTRL). Al definir el MenuShortcut no hace falta incluir dicha tecla.
Clase MenuComponent. Es la superclase derivada de Object que engloba los componentes de Menús.
Clase MenuBar. A una MenuBar sólo se pueden añadir objetos Menu. La tabla muestra algunos métodos.
Métodos de MenuBar Función
MenuBar() Constructor add(Menu), int Añade un manú, obtiene el número de menús y el menú
getMenuCount(), Menu getMenu(int i) en una posición determinada
MenuItem getShortcutMenuItem(MenuShortcut), Obtiene el objeto MenuItem relacionado con un
deleteShortcut(MenuShortcut) Shortcut y elimina el Shortcut especificado
remove(int index), remove(MenuComponent m) Elimina un objeto Menu a partir de un índice o
referencia
Enumeration shortcuts() Obtiene un objeto Enumeration con todos los Shortcuts
Clase MenuItem. Los objetos de MenuItem representan las opciones de un menú. Al seleccionar un objeto
MenuItem se generan eventos tipo ActionEvents. Se puede definir un ActionListener para cada item de un
Menu que define el método actionPerformed. El método getActionCommand, asociado al getSource del
evento dado no permite identificar correctamente al item cuando éste se ha activado mediante el
MenuShortcut (devuelve null).
Métodos de MenuItem Función
MenuItem(String lbl), MenuItem(String, MenuShortcut) Constructores. El carácter (-) es el label de
los separators
boolean isEnabled(), setEnabled(boolean) Pregunta y determina si en item está activo
String getLabel(), setLabel(String) Obtiene y establece la etiqueta del item
MenuShortcut getShortcut(), setShortcut(MenuShortcut), Obtienen, establecen y borran
deleteShortcut(MenuShortcut) MenuShortcuts
String getActionCommand(), setActionCommand(String) Obtienen y establecen un identificador
distinto del label
Clase Menu. El objeto Menu define las opciones de un menú de la barra de menús. En un Menu se pueden
introducir objetos MenuItem, otros objetos Menu (para submenús), objetos CheckboxMenuItem, y
separadores. Como Menu deriva de MenuItem, add(MenuItem) permite añadir objetos Menu a otro Menu.
Métodos de Menu Función
Menu(String) Constructor
int getItemCount() Obtener el número de items
MenuItem getItem(int) Obtener el MenuItem a partir de un índice
add(String), add(MenuItem), addSeparator(), Añadir un MenuItem o separador
insertSeparator(int index)
remove(int index), remove(MenuComponent), Eliminar uno o todos los componentes
removeAll()
insert(String lbl, int index), insert(MenuItem Insertar items en una posición dada
mnu, int index)
String getLabel(), setLabel(String) Obtener y establecer las etiquetas de los items
5.4.2 Clase CheckboxMenuItem y PopupMenu
Clase CheckboxMenuItem. Agrupa items de un Menu que pueden estar activados o no. Genera un
ItemEvent, de modo similar a la clase Checkbox. Esto registrará un ItemListener.
Métodos de CheckboxMenuItem Función
CheckboxMenuItem(String lbl), CheckboxMenuItem(String lbl, boolean state) Constructores
boolean getState(), setState(boolean) Obtienen y ponen estado

Programación en Java 61
Clase PopupMenu. Los menús popup son los contextuales del botón derecho del ratón (popup trigger) al
pulsar sobre un componente dado (parent Component). El menú popup se muestra en unas coordenadas
relativas al parent Component, que debe estar visible. Se pueden usar los métodos de la clase Menu, de la
que deriva. Para que aparezca el PopupMenu hay que registrar el MouseListener y definir el método
mouseClicked.
Métodos de PopupMenu Función
PopupMenu(), PopupMenu(String title) Constructores
show(Component origin, int x, int y) Muestra el pop-up menú en la posición indicada
5.5. Layout managers
Un Layout Manager es un objeto que controla cómo se sitúan los Components (Buttons, TextAreas…) en un
Container (Window, Panel…) para otorgar a Java de la portabilidad necesaria entre plataformas.
El AWT define 5 Layout Managers: FlowLayout, GridLayout (sencillos), BorderLayout, CardLayout
(especializados) y GridBagLayout (genérico). El usuario pueden definir sus Layout Manager, con la interface
LayoutManager y sus 5 métodos. Java permite posicionar los Components de modo absoluto, sin Layout
Manager, pero se pierde la portabilidad y otras características. Los Containers tienen un Layout Manager
por defecto, que se usa si no se indica otra cosa. El de Panel, por defecto, es un objeto de la clase
FlowLayout y para Window (Frame y Dialog) uno de BorderLayout. La figura muestra varios ejemplos.
En los FlowLayout los componentes
se añaden de izqda. a dcha. y de
arriba abajo. Se puede elegir
alineación por la izquierda, centrada
o por la derecha, respecto al
container.
En la figura del BorderLayout el
container se divide en 5 zonas
(North, South, East, West y Center
que ocupa el resto de espacio).
En el ejemplo de GridLayout se usa
una matriz de celdas que se
numeran de izqda. a dcha. y de
arriba abajo.
La imagen del GridBagLayout usa también una matriz de celdas, permitiendo que algunos componentes
ocupen más de una celda. Las imágenes de las CardLayout muestran cómo se permite que el mismo
espacio sea utilizado sucesivamente por contenidos diferentes. Como cada Container tiene un Layout
Manager por defecto, si se desea usarlo basta crear el Container y su constructor crea un objeto por
defecto e inicializa el Container. Para usar un Layout Manager distinto se crea un objeto de dicho Layout
Manager y se pasa al constructor del container o se le indica que lo use con el método setLayout, en la
forma unContainer.setLayout(new GridLayout()). La clase Container dispone métodos para manejar el
Layout Manager. Si se cambia de modo indirecto el tamaño de un Component (p.ej. cambiando el tamaño
del Font), hay que llamar al método invalidate del Component y luego al método validate del Container, lo
que hace que se ejecute el método doLayout para reajustar el espacio disponible.
Métodos de Container para LayoutManagers Función
add() Añade Components a un Container
remove() y removeAll() Permiten eliminar Components de un Container
doLayout(), validate() doLayout() se llama automáticamente al redibujar el Container y
sus Components. Se llama también al llamar al validate
5.5.1. FlowLayout, BorderLayout y GridLayout
FlowLayout. Layout Manager por defecto para Panel. Coloca los componentes uno tras otro, en una fila, de
izqda. a dcha. y de arriba abajo. Los componentes se añaden en orden en que se ejecutan los add. Si se
cambia el tamaño de la ventana se redistribuyen ocupando más filas si es necesario. La clase tiene 3
constructores FlowLayout, FlowLayout(int alignement) y FlowLayout(int alignement, int horizontalGap, int
verticalGap). Se puede establecer la alineación de los componentes (centrados, por defecto), con las
constantes FlowLayout.LEFT, FlowLayout.CENTER y FlowLayout.RIGHT. Es posible también establecer
una distancia horizontal y vertical entre componentes (gap, en pixels, 5 por defecto).

Programación en Java 62
BorderLayout. Es el Layout Manager por defecto para Window y Frame. Define 5 áreas, North, South, East,
West y Center. Si se aumenta el tamaño de la ventana las zonas se mantienen en su mínimo tamaño
posible excepto Center, que absorbe el crecimiento. Los componentes añadidos en cada zona tratan de
ocupar el espacio disponible. P.ej., si se añade un botón, el botón se hará tan grande como la celda, lo quel
puede generar efectos raros. Para evitarlo se puede introducir en la celda un panel con FlowLayout y añadir
el botón al panel y el panel a la celda.
Los constructores de BorderLayout son BorderLayout() y BorderLayout(int horizontalGap, int verticalGap).
Por defecto no se deja espacio entre componentes. Al añadir uno a un Container con BorderLayout se debe
especificar la zona como segundo argumento (miContainer.add(new Button("Norte"), "North")).
GridLayout. Coloca los componentes en una matriz de celdas del mismo tamaño. Cada componente usa
todo el espacio disponible en su celda, al igual que en BorderLayout. GridLayout tiene dos constructores
GridLayout(int nfil, int ncol) y GridLayout(int nfil, int ncol, int horizontalGap, int verticalGap). Al menos un
parámetro nfil o ncol debe ser no 0. El valor por defecto para el espacio entre filas y columnas es 0 pixels.
5.5.2. CardLayout y GridBagLayout
CardLayout. Dispone componentes (en general Panels) que comparten ventana para mostrarse
sucesivamente. Son similares a diapositivas o cartas que aparecen una tras otra. El orden se puede
establecer de la primera a la última, por orden de añadido al container; recorriendolas hacia delante o atrás,
de una en una; o mostrando una dada. Los constructores de esta clase son CardLayout() y CardLayout(int
horizGap, int vertGap). Para añadir componentes a un container con CardLayout se usa el método
Container.add(Component comp, int index) donde index indica la posición de la carta. Los métodos void
first(Container cont), void last(Container cont), void previous(Container cont), void next(Container cont) y
void show(Container cont, String nameCard) permiten controlar el orden en que aparecen las diapositivas.
GridBagLayout. Es el Layout Manager más completo y complicado. Parte de una matriz de celdas en que se
sitúan los componentes. Ahora las filas pueden tener distinta altura, las columnas distinta anchura y en el
GridBagLayout un componente puede ocupar varias celdas contiguas. La posición y tamaño de cada
componente se especifican por medio de restricciones o constraints que se establecen creando un objeto de
la clase GridBagConstraints, dando valor a sus propiedades (variables miembro) y asociandolo al
componente con el método setConstraints. Las variables miembro de GridBagConstraints son:
- gridx y gridy. Especifican fila y columna en que situar la esquina superior izqda. del componente
(empezando de 0). Con la constante GridBagConstraints.RELATIVE se indica que el componente
se sitúa relativamente al anterior componente situado (condición por defecto).
- gridwidth y gridheight. Determinan columnas y filas que ocupará el componente. El valor por defecto
es una columna y una fila. La constante GridBagConstraints.REMAINDER indica que el componente
es el último de la columna o fila. GridBagConstraints.RELATIVE indica que el componente se sitúa
respecto al anterior componente de la fila o columna.
- fill. En caso en que el componente sea más pequeño que el espacio reservado, indica si debe
ocupar o no todo el espacio disponible. Los posibles valores son: GridBagConstraints.NONE (no lo
ocupa; por defecto), GridBagConstraints.HORIZONTAL (en horizontal),
GridBagConstraints.VERTICAL (en vertical) y GridBagConstraints.BOTH (en ambas direcciones).
- ipadx y ipady. Especifican el espacio a añadir en cada dirección al tamaño interno del componente.
Los valores por defecto son 0. El tamaño del componente será el mínimo más dos veces el ipadx o
el ipady.
- insets. Indica el espacio mínimo entre componente y espacio disponible. Se establece con un objeto
de la clase java.awt.Insets. Por defecto es 0.
- anchor. Se usa para determinar dónde se coloca el componente, cuando es menor que el espacio
disponible. Sus posibles valores vienen dados por las constantes de la clase GridBagConstraints:
CENTER (valor por defecto), NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST,
WEST y NORTHWEST.
- weightx y weighty. Son coeficientes entre 0.0 y 1.0 para dar más o menos peso a filas y columnas. A
más peso más probabilidades de darles más anchura o altura.
El cuadro muestra una forma típica de crear un container con GridBagLayout y de añadir componentes:
Con las 3 primeras sentencias preparan GridBagLayout unGBL = new GridBagLayout();
para añadir los componentes. Primero se
GridBagConstraints unasConstr = new GridBagConstraints();
crea un componente unComp, se da valor
a las variables del objeto unasConstr y se unContainer.setLayout(unGBL);
asocian las restricciones al componente.
unGBL.setConstraints(unComp, unasConstr);
Por fin, la última sentencia añade el
componente al container. unContainer.add(unComp);

Programación en Java 63
5.6. Gráficos, texto e imágenes
La clase Component dispone los métodos paint, repaint y update importantes en relación a gráficos. Cuando
el usuario llama al repaint de un componente, el AWT llama al método update del componente, que por
defecto llama al método paint.
Método paint(Graphics g). Se define en la clase Component, pero no se implementa, se redefine en una
clase derivada. No hay que llamar a este método porque el SO lo llama al dibujar por primera vez una
ventana y lo vuelve a llamar cada vez que toda o una parte de la ventana debe redibujarse.
Método update(Graphics g). Su función es redibujar la ventana con el color de fondo y llamar al método
paint. Update también es llamado por el AWT y puede ser llamado por el programador si se realiza algún
cambio en la ventana y necesita que se redibuje.
La estructura de este método (comenzar con el color de fondo) hace que se produzca parpadeo (flicker) en
las animaciones. Una forma de evitarlo es redefinir el método de forma diferente, cambiando de una imagen
a otra sólo lo que cambia, en vez de redibujarlo todo. Este método no siempre proporciona los resultados
buscados y hay que recurrrir al método del doble buffer.
Método repaint(). Es que se usa con más frecuencia. Llama lo antes posible al método update del
componente. Permite especificar un número de ms, que una vez cumplidos, se llame al update. Repaint
presenta 4 formas: repaint(), repaint(long time), repaint(int x, int y, int w, int h), repaint(long time, int x, int y,
int w, int h). La tercera y cuarta forma permiten definir una zona de la ventana a la que aplicar el método.
Java dispone los métodos (o primitivas gráficas) de la tabla para realizar dibujos sencillos. Las coordenadas
se miden en pixels, empezando de 0. Excepto los polígonos y líneas, las formas geométricas se determinan
por el rectángulo que las comprende, de dimensiones w y h. Los polígonos admiten un argumento de la
clase java.awt.Polygon. Los métodos draw3DRect, fill3DRect, drawOval, fillOval, drawArc y fillArc dibujan
objetos de tamaño total (w+1, h+1) pixels.
Método gráfico Función
drawLine(int x1, int y1, int x2, int y2) Dibuja una línea entre dos puntos
drawRect(int x1, int y1, int w, int h) Dibuja un rectángulo (w-1, h-1)
fillRect(int x1, int y1, int w, int h) Dibuja un rectángulo y rellena con color actual
clearRect(int x1, int y1, int w, int h) Borra dibujando con el background color
draw3DRect(int x1, int y1, int w, int h, boolean raised) Dibuja un rectángulo resaltado (w+1, h+1)
fill3DRect(int x1, int y1, int w, int h, boolean raised) Rellena un rectángulo resaltado (w+1, h+1)
drawRoundRect(int x1, int y1, int w, int h, int arcw, int arch), Dibuja y rellena un rectángulo redondeado,
fillRoundRect(int x1, int y1, int w, int h, int arcw, int arch) respectivamente
drawOval(int x1, int y1, int w, int h), fillOval(int x1, int y1, int Dibuja y rellena de un color una elipse,
w, int h) respectivamente
drawArc(int x1, int y1, int w, int h, int startAngle, int Dibuja y rellena un arco de elipse (ángulos en
arcAngle), fillArc(int x1, int y1, int w, int h, int startAngle, int grados) respectivamente
arcAngle)
drawPolygon(int x[], int y[], int nPoints) Dibuja y cierra el polígono de modo
automático
drawPolyline(int x[], int y[], int nPoints) Dibuja un polígono pero no lo cierra
fillPolygon(int x[], int y[], int nPoints) Rellena un polígono
5.6.1. Clase Graphics y Font
El único argumento de los métodos update y paint es un objeto de clase
Graphics, que dispone métodos para soportar 2 tipos de gráficos el
dibujo de primitivas gráficas (texto, líneas, círculos…) y la presentación
de imágenes en formatos *.gif y *.jpeg. La clase Graphics mantiene un
contexto gráfico: un área de dibujo actual, color de dibujo del
background y foreground, un font con sus propiedades, etc. La figura
muestra el sistema de coordenadas usado en Java. Como es habitual
en Informática, los ejes se sitúan en la esquina superior izqda. y las
coordenadas se miden en pixels.

Programación en Java 64
La clase Graphics permite dibujar texto alternativo al texto de los componentes Label, TextField y TextArea.
Los métodos son drawBytes(byte data[], int offset, int length, int x, int y), drawChars(char data[], int offset, int
length, int x, int y) y drawString(String str, int x, int y). Los argumentos x e y representan las coordenadas de
la línea base. Offset indica el elemento que se empieza a imprimir. Cada tipo de letra se representa con un
objeto Font. Las clases Component y Graphics disponen los métodos setFont y getFont. El constructor de
Font es de la forma Font(String name, int style, int size), donde style se indica con las constantes
Font.PLAIN, Font.BOLD y Font.ITALIC, que se pueden combinar en la forma: Font.BOLD | Font.ITALIC.
La clase Font tiene 3 variables protected, name, style y size y 3 constantes enteras, PLAIN, BOLD e ITALIC.
Dispone los métodos String getName, int getStyle, int getSize, boolean isPlain, Boolean isBold y boolean
isItalic. Para mejorar la portabilidad se recomienda usar nombres lógicos de fonts.
5.6.2. Clase FontMetrics y Color
La clase FontMetrics es una clase abstract que permite
obtener información sobre una fuente y el espacio que
ocupa un char o String usando esa font. Es útil si se
pretende rotular algo de modo centrado y bien
dimensionado. La forma de trabajo es por tanto, crear
subclases. Java resuelve esta dificultad al proveer
como variable miembro un objeto de clase Font. Así, un
objeto de FontMetrics contiene información sobre la
fuente que se pasa como argumento al constructor.
La forma general para obtener esa información es a partir de un objeto Graphics que tenga un Font definido.
Esto permite obtener una referencia FontMetrics en la forma FontMetrics miFontMet = g.getFontMetrics().
Queda claro que se usa una referencia de FontMetrics para refererirse a un objeto de una clase derivada en
el API de Java. Con una referencia de tipo FontMetrics se pueden usar los métodos propios de la clase. La
tabla muestra algunos métodos FontMetrics, para los que tener en cuenta la figura anterior.
Método gráfico Función
FontMetrics(Font font) Constructor
int getAscent(), int getMaxAscent() Permiten obtener el Ascent actual y máximo para esa font
int getDescent(), int getMaxDescent() Permiten obtener el Descent actual y máximo para esa font
int getHeight(), int getLeading() Obtienen la distancia entre líneas y entre descender y
ascender entre líneas
int getMaxAdvance() Da la máxima anchura de un carácter de esa font, incluyendo
el espacio hasta el siguiente carácter
int charWidth(int ch), int charWidth(char Dan la anchura de un carácter (incluyendo el espacio hasta el
ch), int stringWidth(String str) siguiente carácter) o de toda una cadena de caracteres
int charsWidth(char data[], int start, int len), Anchura de un array de caracteres o bytes. Permiten definir la
int bytesWidth(byte data[], int start, int len) posición del comienzo y el número de caracteres
Clase java.awt.Color. Encapsula colores en formato RGB (Red, Green, Blue). Los componentes de cada
color primario en el color resultante se expresan con enteros entre 0 y 255, siendo 0 la intensidad mínima.
Métodos de Color Función
Color(int), Color(int,int,int), Constructores. 3 B en un int (del bit 0 al 23), con enteros entre 0
Color(float,float,float) y 255 y float entre 0.0 y 1.0
Color brighter(), Color darker() Obtienen una versión más o menos brillante de un color
Color getColor(), int getRGB() Obtiene un color en los tres primeros bytes de un int
int getGreen(), int getRed(), int getBlue() Componentes de un color
Color getHSBColor() Color a partir de los valores de “hue”, “saturation” y “brightness”
(entre 0.0 y 1.0)
float[] RGBtoHSB(int,int,int,float[]), int Métodos static para convertir colores entre sistemas
HSBtoRGB(float,float,float)
En la clase Color existen constantes para colores predeterminados de uso frecuente: black, white, green,
blue, red, yellow, magenta, cyan, orange, pink, gray, darkGray, lightGray.

Programación en Java 65
5.6.3. Imágenes
Java permite incorporar imágenes de tipo gif y jpeg definidas en ficheros con la clase java.awt.Image. Para
cargar una imagen hay que indicar la localización del fichero (URL) y cargarlo con los métodos Image
getImage(String) o Image getImage(URL, String) de las clases awt.Toolkit y Applet. El argumento de tipo
String representa una variable con el nombre del fichero.
Cuando las imágenes se cargan en applets, para obtener el URL pueden ser útiles getDocumentBase y
getCodeBase, que devuelven el URL del fichero HTML que llama al applet y el directorio que contiene el
applet (en un String). Para cargar una imagen hay que crear un objeto Image y llamar al método getImage,
pasándole como argumento el URL (p.ej. Image miImagen = getImage(getCodeBase(), "imagen.gif")).
Cargada la imagen, hay que representarla, con el método paint para llamar al método drawImage de
Graphics. Éste método admite varias formas, aunque casi siempre hay que incluir el nombre del objeto
imagen creado, dimensiones de imagen y un objeto ImageObserver.
ImageObserver es un interface que declara métodos para observar el estado de la carga y visualización. Si
se programa un applet, basta con poner como ImageObserver la referencia this, ya que en la mayoría de
casos, la implementación del interface en la clase Applet proporciona el comportamiento deseado. La clase
Image define constantes para controlar los algoritmos de cambio de escala: SCALE_DEFAULT,
SCALE_FAST, SCALE_SMOOTH, SCALE_REPLICATE, SCALE_AVERAGE.
Métodos de Image Función
Image() Constructor
int getWidth(ImageObserver), Determinan la anchura y la altura de la imagen. Si no se conocen,
int getHeight(ImageObserver) devuelve -1 y el ImageObserver especificado será notificado más tarde
Graphics getGraphics() Crea contexto gráfico para dibujar en una imagen no visible en pantalla.
Este método sólo se puede llamar para objetos no visibles en pantalla
Object getProperty(String, Obtiene una propiedad de una imagen a partir del nombre de la
ImageObserver) propiedad
Image getScaledInstance(int w, Crea una versión de la imagen a otra escala. Si w o h son negativas se
int h, int hints) usa la otra dimensión manteniendo la proporción. El último argumento es
información para el algoritmo de cambio de escala
5.7. Animaciones
Se pueden hacer animaciones de forma sencilla definiendo el método paint de forma que cada vez dibuje
algo diferente. Como una película. En general se llamará a repaint en un bucle que incluya una llamada a
sleep, que espere unos ms entre frames. Repaint llama a update; update borra redibuja con el color de
fondo y llama a paint. Es una forma válida con animaciones sencillas, pero produce parpadeo (flicker) con
gráficos complejos. La razón es el refresco de pantalla y la pesadez del proceso.
Para eliminar el flicker se puede redefinir update. Una posibilidad es no redibujar todo con el color de fondo,
no llamar a paint e introducir en update el código que dibuja, sólo los cambios. Así, es necesario redefinir
paint pues es al que se llama automáticamente cuando la ventana de Java es tapada con otra que se retira.
Una posible solución es hacer que paint llame a update, terminando por establecer un orden de llamadas
opuesto al de defecto. Al no borrar todo lo pintando con el color de fondo, hay que borrar de forma selectiva
entre frame y frame lo que sea. Los métodos setClip y clipRect de Graphics permiten hacer que las
operaciones gráficas no surtan efecto fuera de un área rectangular previamente dada. Al ser dependiente
del tipo de gráficos concretos de que se trate, el método no siempre es adecuado.
La técnica del doble buffer supone otra solución, aunque de más programación. La Image imgInv;
idea es realizar los dibujos en una imagen invisible, distinta a la mostrada y
Graphics graphInv;
mostrarla al terminarla para que aparezca rápido. El segundo buffer o imagen
invisible obliga a crear un objeto Image del mismo tamaño que la imagen mostrada Dimension dimInv;
y un contexto gráfico u objeto Graphics para dibujar sobre la imagen invisible. Esto
Dimension d = size();
se hace con las sentencias del cuadro en la clase que controle el dibujo.
En el update se modificará el código de forma quee primero se dibuje en la imagen invisible y luego se haga
visible. En el cuadro se muestra un código tipo.
public void update (Graphics.g) {
// se comprueba si existe el objeto invisible y si sus dimensiones son correctas

Programación en Java 66
if ((graphInv==null) || (d.width!=dimInv.width) || (d.height!=dimInv.height)) {
dimInv = d;
imgInv = createImage(d.width, d.height); // se llama al método createImage de Component
graphInv = imgInv.getGraphics(); // se llama al método getGraphics de la clase Image
}
graphInv.setColor(getBackground()); // propiedades del contexto invisible y se dibuja sobre él
...
g.drawImage(imgInv, 0, 0, this); // se hace visible la imagen invisible a partir de (0, 0)
} // fin de update

Programación en Java 67
6. THREADS
La multitarea es la realización aparentemente simultánea de 2 o más actividades. Los SO ejecutan varios
programas a la vez aunque sólo dispongan una CPU: reparten el tiempo entre las actividades, o usan los
tiempos de espera para adelantar otras tareas. En equipos con dos o más procesadores la multitarea es
real, ya que cada procesador ejecuta un hilo o thread distinto.
Un proceso es un programa ejecutándose de forma independiente con espacio propio de memoria. Un
thread o hilo es un flujo secuencial simple en un proceso. Un único proceso puede tener varios hilos
ejecutándose. Por ejemplo un navegador sería un proceso y cada ventana abierta estaría formada por al
menos un hilo.
Los threads o hilos de ejecución permiten organizar los recursos del ordenador entre varios programas en
paralelo. Un hilo puede realizar cualquier tarea de un programa normal. Basta con indicarlo en el método
run, el que define la actividad principal de los hilos. Los threads pueden ser daemon o no.
Son daemon los hilos que realizan en background (segundo plano) servicios generales; tareas que no
forman parte del núcleo del programa y se ejecutan mientras no finalice la aplicación. Por ejemplo el control
de la pulsación de un botón. Un programa Java finaliza cuando sólo quedan threads de tipo daemon.
En Java hay 2 formas de crear threads: crear una nueva clase que herede de java.lang.Thread y
sobrecargar el método run de dicha clase o declarar una clase que implemente la interface
java.lang.Runnable, que declare el método run. Así, se creará un objeto Thread pasándole como argumento
al constructor el objeto creado. Tanto la clase Thread como la interface Runnable pertenecen al package
java.lang, por lo que no es necesario importarlas.
A la derecha se muestra un ejemplo de creación de threads derivando de la clase Thread. Se crea la clase
SimpleThread donde su constructor usa un string (opcional) para nombrar el thread creado y con super
invocar el constructor de Thread. También redefine el método run, para que escriba 10 veces el nombre del
thread creado.
public class SimpleThread extends Thread { public class SimpleRunnable implements Runnable {
public SimpleThread (String str) { // constructor String nameThread; // se crea un nombre
super(str); public SimpleRunnable (String str) { // constructor
} nameThread = str;
public void run() { // redefinición del método run() }
for(int i=0;i<10;i++) public void run() { // definición del método run()
System.out.println("Nombre Thread: " + getName()); for(int i=0;i<10;i++)
} System.out.println("Nombre Thread: " +
nameThread);
}
}}
Para iniciar el nuevo thread se ha de crear un objeto de la clase SimpleThread y llamar al start heredado de
Thread (que llama a run). Por ejemplo:
SimpleThread miThread = new SimpleThread(“Hilo de prueba”);
miThread.start();
A la izquierda se muestra un ejemplo de creación de threads implementando la interface Runnable.
También requiere que se defina el método run y es necesario crear un objeto de la clase Thread para lanzar
la ejecución del nuevo hilo. Al constructor de la clase Thread hay que pasarle una referencia del objeto de la
clase que implementa la interface Runnable. Posteriormente, cuando se ejecute el start del thread, éste
llamará al run de la nueva clase.
El siguiente código de la derecha crea un nuevo thread y lo ejecuta con este procedimiento. Este método es
interesante con applets, que heredan de java.applet.Applet y por tanto no de Thread. En el código de la
izquierda, el argumento this del constructor de Thread hace referencia al objeto Runnable cuyo run debería
llamarse cuando el hilo ejecutado es un objeto ThreadRunnable.
La elección de una u otra forma (derivar de Thread o implementar Runnable) depende del tipo de clase a
crear. Si la clase hereda de otra (por ejemplo un applet) no hay más que implementar Runnable, aunque
suele ser más sencillo heredar de Thread.

Programación en Java 68
SimpleRunnable p = new SimpleRunnable("Hilo de class ThreadRunnable extends Applet implements
prueba"); Runnable {
// se crea un objeto Thread pasando el objeto private Thread runner=null;
Runnable como argumento
public void start() { // redefine el start de Applet
Thread miThread = new Thread(p);
if (runner == null) {
// se arranca el objeto de la clase Thread
runner = new Thread(this);
miThread.start();
runner.start(); // se llama al start de Thread
}}
public void stop(){ // redefine el stop de Applet
runner = null; // se libera el objeto runner
}}
6.1. Ciclo de vida de un thread
En la figura se muestran los estados por los que
puede pasar un thread en su vida. Son 4:
Nuevo (New). El thread se crea pero no se inicializa;
no se ha ejecutado el método start. Se producirá un
mensaje de error (IllegalThreadStateException) si se
intenta ejecutar cualquier método de la clase Thread
distinto de start.
Ejecutable (Runnable). El thread puede estar ejecutándose, siempre y cuando se le haya asignado tiempo
de CPU. En la práctica puede no estar siendo ejecutado en un instante dado en beneficio de otro thread.
Bloqueado (Blocked o Not Runnable). Podría estar ejecutándose, pero hay alguna actividad propia interna
que lo impide, como por ejemplo una espera producida una operación E/S de datos por teclado. A un thread
bloqueado no se le asigna tiempo de CPU.
Muerto (Dead). La forma habitual de que un thread muera es finalizando el método run. También puede
llamarse al método stop de Thread, aunque es considerado peligroso.
Estos estados dan lugar a 4 operaciones sobre threads: ejecución, detención y finalización.
La creación de un nuevo thread no implica que se empiece a ejecutar algo. Hace falta iniciarlo con el
método start, que se encarga de llamar al método run de Thread. Si el nuevo thread se ha creado
heredando de Thread la nueva clase deberá redefinirirlo. En caso de implementar la interface Runnable, el
método run de Thread llamará al de la nueva clase. Una vez que el método start se llama, el thread está
corriendo (running), que no es que se esté ejecutando en todo momento, pues el tiempo de CPU se divide
entre todos. Por eso se dice que el thread es runnable.
La detención de un thread temporalmente depende del SO. El tiempo de CPU asignado a cada thread en
estado runnable lo emplean en ejecutar su método run. Un thread puede renunciar a su tiempo de CPU
para que se asigne a otro. Esa renuncia se realiza con el método yield, importante para actividades que
tienden a monopolizar la CPU. Yield viene a indicar que en ese momento el thread puede liberar tiempo de
CPU. En caso que sobre tiempo se vuelve a asignar al thread generoso que usó yield: ser generoso no es
contraproducente. Parar o bloquear temporalmente un thread (estado Not Runnable), se puede hacer:
1. Ejecutando el método sleep de Thread. Esto detiene el thread un tiempo dado. En general sleep se llama
desde el run.
2. Ejecutando el método wait heredado de Object. Queda a la espera que suceda algo necesario para
continuar. El thread volverá a runnable con los métodos notify o notifyAll, que se ejecutarán al cesar la
condición que detiene el thread.
3. Cuando el thread está esperando a realizar operaciones de E/S.
4. Cuando el thread llama a un método synchronized de un objeto que está bloqueado por otro thread.
Un thread pasa automáticamente de estado Not Runnable a Runnable cuando cesa alguna condición
anterior o cuando se llama a notify o notifyAll. La clase Thread dispone también del método stop, pero no es
aconsejable, ya que puede provocar bloqueos del programa (deadlock). Otra posibilidad para detener un
thread, es ejecutar el método suspend. El thread volverá a ser ejecutable ejecutando el método resume.

Programación en Java 69
Esta última forma también se desaconseja, por razones similares a las de stop. El método sleep de Thread
recibe como argumento el tiempo en ms que ha de detenerse. Adicionalmente, se puede incluir un número
entero con un tiempo adicional en ns. Los prototipos de estos métodos son:
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanosecons) throws InterruptedException
Sea el ejemplo del cuadro. El método sleep lanza una System.out.println ("CuentaSegundos");
InterruptedException que se captura, aunque no se gestiona.
int count=0;
Para detener temporalmente un thread es preferible usar
conjuntamente wait y notifyAll. La ventaja del wait es que libera public void run () {
el bloqueo del objeto por lo que el resto de threads en espera
try {
para actuar sobre él pueden llamar a sus métodos. Hay 2
formas de llamar a wait: sleep(1000);
1. Indicando el tiempo máximo a parar. En ms y con la opción System.out.println(count++);
de indicarlo en ns, de forma análoga a sleep. A diferencia del
método sleep, que sólo detiene el thread el tiempo indicado, el } catch (InterruptedException e){}
wait establece el tiempo máximo que debe estar parado. }
Si en ese plazo se ejecutan notify o notifyAll que indican la liberación de los objetos bloqueados, el thread
continuará sin esperar a concluir el tiempo. Los 2 prototipos de wait son:
public final void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
2. Sin argumentos. En cuyo caso el thread permanece parado hasta que sea reinicializado explícitamente
con métodos notify o notifyAll: public final void wait() throws InterruptedException.
Los métodos wait y notify han de estar public class MyApplet extends Applet implements Runnable {
incluidos en un método synchronized, ya
private Thread AppletThread; // Se crea referencia Thread
que de otra forma se obtendrá una
excepción del tipo ...
IllegalMonitorStateException en tiempo de
public void start() { // método start() del Applet
ejecución. El uso típico de wait es esperar
a que se cumpla alguna condición, ajena al if(AppletThread == null){ // sin objeto Thread asociado
thread. Cuando ésta se cumple, se usará
el método notifyAll para avisar a los AppletThread = new Thread(this, "El propio Applet");
threads que pueden usar el objeto. AppletThread.start(); // arranca el thread y run
Un thread finaliza cuando el método run }
devuelve el control, por haber terminado o
dejar de cumplirse una condición (p. ej. un }
bucle while en el método run). Es habitual public void stop() { // método stop() del Applet
poner las sentencias del ejemplo de la
derecha en Applets Runnables. AppletThread = null; // iguala la referencia null

Se aprecia que AppletThread es el thread }


que ejecuta el método run MyApplet. Para // método run (implementa Runnable)
finalizar el thread basta poner la referencia
AppletThread a null. Esto se consigue en el public void run() {
ejemplo con el método stop del applet Thread myThread = Thread.currentThread();
(distinto al de la clase Thread, que no
conviene usar). while (myThread == AppletThread) { // hasta el stop de
Thread
Para saber si un thread está “vivo” o no, es
útil el método isAlive de Thread, que ... // código a ejecutar
devuelve true si el thread ha sido }
inicializado y no parado, y false si es
todavía nuevo (no ha sido inicializado) o ha }
finalizado. } // fin de la clase MyApplet
6.2. Sincronización y prioridad
La sincronización nace de la necesidad de evitar que 2 o más threads traten de acceder al mismo recurso
simultáneamente, como por ejemplo en una escritura de un fichero o datos que ofrece otro thread. El código
de un programa que accede a un recurso desde 2 threads distintos es una sección crítica (critical sections).

Programación en Java 70
Para sincronizar threads se usa synchronized en los public synchronized void metodoSincronizado() {
métodos del objeto recurso que puedan generar
... // accediendo p. ej. a variables de un objeto
situaciones conflictivas. Así, Java bloquea (asocia
un bloqueo o lock) con el recurso sincronizado. }
El cuadro derecho muestra un ejemplo de sintaxis. La sincronización previene interferencias sólo sobre un
tipo de recurso: la memoria. Cuando se prevé que variables de una clase den problemas de sincronización,
se deben declarar private (o protected). Así, sólo serán accesibles con métodos de la clase, que deben
sincronizarse. Si se sincronizan métodos de un objeto y otros no, el programa puede dar problemas al poder
acceder los métodos no sincronizados a las variables miembro, ignorando el bloqueo.
Sólo los métodos sincronizados comprueban si un objeto está bloqueado. Los métodos que accedan a un
recurso compartido deben declararse synchronized. Cuando un método accede a un recurso, Java lo
bloquea; el resto de threads no pueden acceder al mismo hasta que el primero en acceder termine su tarea.
Existen 2 niveles de bloqueo: a nivel de objeto y de clase. El primero se consigue declarando todos los
métodos de una clase synchronized. Cuando se ejecuta un método synchronized sobre un objeto el sistema
lo bloquea, de forma que si otro thread intenta ejecutar algún método de ese objeto, tendrá que esperar. Si
existen varios objetos de una clase, como el bloqueo es a nivel de objeto, es posible tener distintos threads
ejecutando métodos sobre diversos objetos de una misma clase.
El bloqueo a nivel de clase es el de los métodos de clase o static, y por tanto con variables de clase o static.
Si se desea que un método bloquee una clase entera hay que declararlo synchronized static. Así, ningún
método sincronizado accederá a ningún objeto de la clase. La sincronización puede ser problemática. Un
thread podría bloquear un recurso indefinidamente.
Si en un método sincronizado se usa el sleep de public synchronized int get() {
Thread, el objeto bloqueado permanecerá en ese
while (available == false) {
estado el tiempo indicado.
try {
Para evitarlo conviene sustituir sleep por el wait de
Object. Al llamar a wait (siempre desde un método o wait(); // Espera que put genere el dato
bloque synchronized) se libera el bloqueo del objeto
y puede continuar usándose con métodos } catch (InterruptedException e) { }
sincronizados. Wait detiene el thread hasta que se }
llame a notify, notifyAll o finalice el tiempo indicado.
El método unObjeto.notify lanza una señal indicando available = false;
al sistema que puede activar un thread bloqueado notifyAll(); // notifica que ha leído
esperando acceso al objeto.
return contents; // devuelve el valor
NotifyAll lanza una señal a todos los threads en
espera de la liberación del objeto. Los métodos }
notify y notifyAll deben llamarse desde el thread que public synchronized void put(int value) {
bloquea el objeto para activar el resto de threads
que están esperando. Un thread se convierte en while (available == true) {
propietario del bloqueo ejecutando un método try { // Espera que get lea el valor
sincronizado del objeto.
wait();
Los bloqueos de tipo clase, se consiguen ejecutando
un método sincronizado de clase (synchronized } catch (InterruptedException e) { }
static). El cuadro presenta 2 funciones, put que }
genera un dato y get que lo recoge. El bucle while
del get se ejecuta (avalaible==false) hasta que el put contents = value; // ofrece otro valor
suministre un nuevo valor y lo indique con avalaible available = true; // declara disponible
(= true). En cada iteración del while el wait hace que
el hilo que ejecuta el get se detenga hasta que se notifyAll(); // lo notifica
produzca un mensaje de cambio (en este caso con }
el notifAll del put). Put funciona de forma homóloga.
Existe la posibilidad de sincronizar una parte de un método sin bloquear el objeto desde el comienzo hasta
el fin del método, con syncronized y entre paréntesis el objeto a sincronizar. El siguiente código de la
derecha, muestra un ejemplo para sincronizar el propio thread en una parte del método run.
public void run() { public class VolverAAdquirir {
while(true) { public synchronized void a() {
... b();

Programación en Java 71
syncronized(this) { // El objeto a sincronizar es el thread System.out.println("En a()");
... // Código sincronizado }
} public synchronized void b() {
try { // Se detiene el thread 0.5 s pero el objeto es System.out.println("En b()");
sleep(500); // accesible a otros threads al no estar sincronizado }
} catch(InterruptedException e) {} }
}}
Un thread puede llamar a un método sincronizado de un objeto para el que ya posee el bloqueo, volviendo a
adquirirlo. Es lo que muestra el código de la derecha, que obtendría como resultado “En b()”, “En a()”. Se ha
podido acceder al objeto con el método b() al ser el thread que ejecuta el método a() propietario anterior del
bloqueo del objeto. La sincronización lleva bastante tiempo a la CPU, luego se debe intentar minimizar.
La prioridad intenta conseguir un reparto eficiente de recursos en la ejecución de los threads. Así, si en un
momento interesa que un proceso acabe lo antes posible, se le otorgarán más recursos (tiempo de CPU).
Cuando se crea un thread, éste hereda la prioridad del thread que lo inicializa. Las prioridades se marcan
con variables enteras miembro de la clase Thread. En general, la máxima prioridad es 10 (MAX_PRIORITY)
y la mínima 1 (MIN_PRIORITY). Por defecto se asigna 5 (NORM_PRIORITY). Para modificarla se usa el
método setPriority. El valor lo da getPriority. El algoritmo de distribución de recursos en Java suele escoger
el thread de prioridad mayor, aunque no siempre, para evitar que los procesos se duerman.
Entre 2 o más threads de la misma prioridad (y además la mayor), el sistema no establece prioridades; los
ejecuta alternativamente dependiendo del SO, que si soporta “time-slicing” (troceo temporal), los threads se
ejecutan alternativamente. Con yield, un thread renuncia a su tiempo de CPU en favor de otro de la misma o
mayor prioridad, nunca otro de menor prioridad.
6.3. Grupos de Threads
Todo hilo Java forma parte de un grupo de hilos (ThreadGroup).
Puede pertenecer al grupo por defecto o a uno creado por el
usuario. Los grupos de threads suponen una forma sencilla de
manejar múltiples threads como un solo objeto.
Por ejemplo es posible parar varios threads con una sola llamada.
Una vez un thread ha sido asociado a un threadgroup, no puede
cambiar. Cuando se arranca un programa, el sistema crea un
ThreadGroup llamado main.
Si en la creación de un nuevo thread no se especifica su grupo, automáticamente pertenece al threadgroup
del thread que lo crea (current thread group y current thread, respectivamente). Si en dicho programa no se
crea ningún ThreadGroup adicional, sus threads pertenecerán al grupo main (donde se encuentra el método
main). La figura presenta una posible distribución de threads en grupos.
Para conseguir que un thread public Thread (ThreadGroup grupo, Runnable destino)
pertenezca a un grupo dado, hay
public Thread (ThreadGroup grupo, String nombre)
que indicarlo al crear el nuevo
thread, con uno de los public Thread (ThreadGroup grupo, Runnable destino, String nombre)
constructores del cuadro.
A su vez, un ThreadGroup debe pertenecer a otro ThreadGroup. Como ocurría en el caso anterior, si no se
especifica ninguno, el nuevo grupo pertenecerá al ThreadGroup desde el que se crea (por defecto al grupo
main). La clase ThreadGroup los 2 posibles constructores del siguiente cuadro.
El segundo toma como parent el threadgroup al cual ThreadGroup(ThreadGroup parent, String nombre);
pertenezca el thread desde el que se crea,
ThreadGroup(String name);
Thread.currentThread().
En la práctica los ThreadGroups no se suelen usar demasiado. Su uso práctico se limita a efectuar algunas
operaciones de forma más simple que de forma individual. En el siguiente código se crea un grupo de
threads (miThreadGroup) y un thread que pertenece a dicho grupo (miThread).
ThreadGroup miThreadGroup = new ThreadGroup("Mi Grupo de Threads");
Thread miThread = new Thread(miThreadGroup, ”un thread para mi grupo");

Programación en Java 72
7. APPLETS
Un applet es un programa pequeño que se aloja en código de otro lenguaje (típicamente HTML; etiqueta
<APPLET>) y se ejecuta en el contexto de una aplicación anfitriona (típicamente un navegador).
Los ficheros Java compilados (.class) se descargan desde un servidor web o HTTP al navegador en cuya
JVM se ejecutan. Pueden incluir ficheros de imágenes y sonido. Los applets no tienen ventana propia. Se
ejecutan en la ventana del navegador (en un panel).
Por la naturaleza de Internet, tienen implicaciones de seguridad: sólo pueden leer y escribir ficheros en el
servidor del que se descargan; acceder sólo a información limitada del equipo en que se ejecutan, etc. Las
applets de confianza (trusted applets) pueden superar estas restricciones, con condiciones.
Aunque el entorno de ejecución es un navegador, pueden probarse sin necesidad del mismo, con la
aplicación appletviewer del JDK de Sun.
Las applets no tienen método main. Su ejecución la asumen otros métodos. Todas
derivan de la clase java.applet.Applet. La figura su jerarquía de clases. Las applets
deben redefinir métodos heredados de Applet como init, start, stop o destroy.
Otros métodos heredados son relativos a la GUI (AWT). Los métodos gráficos se
heredan de Component y la capacidad de añadir elementos de GUI de Container y
Panel. Las applets suelen redefinir ciertos métodos gráficos. Los más importantes
paint, update (de Component y Container) y repaint (de Component).
Las applets disponen de métodos relacionados con la obtención de información,
como getAppletInfo, getAppletContext, getParameterInfo, getParameter,
getCodeBase, getDocumentBase e isActive.
El método showStatus se usa para mostrar información en la barra de estado del navegador. Otros métodos
relacionados con imágenes y sonido son getImage, getAudioClip, play, etc.
Los métodos que controlan la ejecución de las applets suelen redefinirse, pero no se llaman, lo hace el
navegador. Los métodos que suelen redefinirse son cuatro.
Método init. Se llama automáticamente cuando el navegador carga el applet. Gestiona de las tareas de
inicialización, realizando las funciones del constructor (al que el navegador no llama).
Método start. Se llama automáticamente en cuanto el applet se hace visible, tras inicializarse. También cada
vez que el applet se hace de nuevo visible después de estar oculta (por dejar de estar activa la página del
navegador, cambiar su tamaño…). Es habitual crear threads en este método para las tareas que, por el
tiempo requerido, dejarían sin recursos al applet o al navegador (animaciones…).
Método stop. Se llama automáticamente al ocultar el applet. Para no consumir recursos se suelen parar los
threads en ejecución en el applet.
Método destroy. Se llama a este método para liberar los recursos reservados del applet (excepto la
memoria) cuando va a descargarse. No suele ser necesario redefinirlo, pues el que se hereda es completo.
7.1. Inclusión de un applet en una página html y paso de parámetros
Los applets son aplicaciones gráficas que aparecen en una zona de la ventana del navegador, lo que obliga
a redefinir los métodos gráficos paint y update. Paint se declara como public void paint(Graphics g).
El objeto gráfico g pertenece a java.awt.Graphics, que debe importar el applet. Define un contexto gráfico
para dibujar (métodos gráficos, colores, fonts…) y es creado por el navegador. Las tareas del applet (dibujo
de líneas, texto, etc.) se incluye en el método paint, porque es llamado cuando el applet se dibuja por
primera vez y de forma automática cada vez que el applet se debe redibujar.
En general se crea el método paint pero no se llama. Para solicitar al sistema que vuelva a dibujar el applet
(por algún cambio, p.ej.) se usa el método repaint, más fácil de usar, al no requerir argumentos. Repaint
llama a paint a través de update, que borra todo y pinta de nuevo con el color de fondo y luego llama a
paint. A veces produce parpadeo de pantalla (flickering).
Para evitarlo se puede redefinir update de forma que no borre toda la ventana, sólo lo necesario o se puede
redefinir paint y update para usar doble buffer.
Para incluir un applet en una página HTML se usa la etiqueta <APPLET>, de sintaxis la del cuadro. NAME
permite dar un nombre opcional al applet para comunicarse con otras applets o elementos que se ejecuten
en la misma página. Si las clases se comprimen en ficheros jar o zip, se pueden especificar entre comas
con otro indicador opcional, ARCHIVE. Los modificadores ALIGN, VSPACE y HSPACE tienen el mismo
significado que en el tag IMG de HTML.

Programación en Java 73
Las etiquetas PARAM <APPLET CODE="Applet.class" [CODEBASE="URL"] [NAME="Name"]
permiten pasar
WIDTH="wpixels" HEIGHT="hpixels"
parámetros del fichero
HTML al applet, de forma [ALT="TextoAlternativo para navegadores que no pueden ejecutar el applet"]>
análoga al paso de
[<PARAM NAME="Name1" VALUE="valueOfName1">]
argumentos a main. Cada
parámetro tiene un [<PARAM NAME="Name2" VALUE="valueOfName2">]
nombre y valor dados en
forma de String, aunque [texto alternativo para navegadores que no reconocen el tag <applet>]
el valor sea numérico. </APPLET>
El applet recupera los parámetros y si es necesario, convierte los strings a valor numérico. El valor de los
parámetros se obtienen con el método de Applet String getParameter(String name). La conversión de
strings a tipos primitivos se puede hacer con los métodos asociados a los wrappers que Java ofrece para
tipos fundamentales (Integer.parseInt(String), Double.valueOf(String)…).
En los nombres de los parámetros no se distingue entre mayúsculas y minúsculas, pero sí en los valores, ya
que serán interpretados por un programa Java. Es bueno prever valores por defecto para los parámetros del
applet, para el caso en que la página HTML que llama al applet no los defina. El método getParameterInfo
devuelve una matriz de Strings con información sobre cada parámetro soportado por el applet: nombre, tipo
y descripción, cada uno de ellos en un String. Este método debe ser redefinido y al confeccionar la página
que llama al applet. Supone una forma de que el programador del applet pase información al usuario.
7.2. Carga de applets
Para localizar los ficheros .class que contienen el applet se supone que residen en el mismo directorio que
el fichero HTML. Si el applet es de un package, el navegador usa su nombre para construir un path relativo
al directorio del fichero HTML. CODEBASE permite especificar un URL para los ficheros del código y demás
elementos del applet. Si el directorio ddado en el URL de CODEBASE es relativo, se interpreta respecto al
directorio del HTML; si es absoluto puede ser cualquiera.
Si un applet consta de varias clases, cada fichero .class requiere una conexión con el servidor web, lo que
puede tardar. En este caso conviene agrupar los ficheros en un archivo comprimido que se cargue en una
sola conexión. Los archivos JAR, basados en los ZIP, pueden crearse con el programa jar del JDK, en la
forma “jar cvf File.jar *.class *.gif”. El ejemplo crearía un fichero File.jar con todos los ficheros *.class y *.gif
del directorio actual. Si las clases pertenecieran a un package llamado es.mad.inf se usaría el comando: jar
cvf File.jar es\mad\inf\*.class *.gif
La comunicación entre el applet y navegador en que se ejecuta se puede controlar con la interface
AppletContext (package java.applet). El navegador implementa el interface, cuyos métodos puede usar el
applet para obtener información y realizar operaciones como mostrar mensajes en la barra de estado. La
barra de estado es compartida por navegador y applets, con la precaución que el mensaje sea sobreescrito
por el navegador u otras applets y el usuario no llegue a verlo. Los mensajes breves de la barra de estado
se generan con el método showStatus. Ejemplo:
getAppletContext().showStatus("Cargado del fichero " + filename);
Los mensajes importantes se deben dirigir a la salida estándar o a la de errores. Se pueden enviar con las
sentencias System.out.print, System.out.println, System.error.print o System.error.println().
Para mostrar documentos HTML en una ventana del navegador se pueden usar los métodos
showDocument(URL miUrl, [String target]), que muestra un documento HTML en el frame del navegador
que indica target (name, _top, _parent, _blank, _self) o showDocument(URL miUrl), que muestra un
documento HTML en la ventana actual del browser.
Un applet puede obtener información de otras applets en ejecución en la misma página del navegador,
enviarles mensajes y ejecutar sus métodos. El mensaje se envía invocando los métodos del otro applet con
sus argumentos. Algunos navegadores, para que las applets se puedan comunicar, obligan a que
provengan del mismo navegador o del mismo directorio (mismo codebase).
Para obtener información de otras applets se pueden usar los métodos getApplet(String name), que
devuelve el applet llamado name (null si no la encuentra) o getApplets, que devuelve una enumeración con
todas las applets de la página.
Para usar los métodos de un applet en ejecución en la misma página HTML, debe hacerse un cast del
objeto de la clase Applet obtenido como retorno de getApplet a la clase concreta. Para que pueda haber
respuesta (comunicación en ambos sentidos), el primer applet que envía un mensaje debe enviar una
referencia a sí mismo con el argumento this.

Programación en Java 74
7.3. Imagen y sonido en applets
La clase Applet y el interface AudioClips permiten usar sonidos en applets. La tabla muestra algunos
métodos al respecto. En general conviene cargar los sonidos en un thread distinto (creado en el método init)
que en el propio método init, que tardaría más. Si el sonido no ha terminado de cargarse (en el thread para
ello) y se interacciona con el applet para ejecutarlo, se puede avisar de que no se ha terminado de cargar.
Métodos de Applet Función
public AudioClip getAudioClip(URL url) Devuelve el objeto especificado por url, que
implementa la interface AudioClip
public AudioClip getAudioClip(URL url, String name) Devuelve el objeto especificado por url (dirección
base) y name (dirección relativa)
play(URL url), play(URL url, String name) Hace que suene el AudioClip de la dirección dada
Métodos del interface AudioClip (en java.applet) Función
void loop() Ejecuta el sonido repetidamente
void play() Ejecuta el sonido una sola vez
void stop() Detiene el sonido
Los applets admiten ficheros del servidor en formato jpeg y gif. Se pueden cargar con el método getImage
de Applet, en las formas public Image getImage(URL url) o public Image getImage(URL url, String name).
Estos métodos devuelven el control inmediatamente. Las imágenes se cargan al dar la orden de dibujarlas
en pantalla. El dibujo se realiza de forma incremental, a medida que el contenido llega. Para dibujar
imágenes se usa el método drawImage de la clase Graphics, en las formas:
public abstract boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)
public abstract boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver
observer)
El primero dibuja la imagen con su tamaño natural; el segundo realiza un cambio de escala. Los métodos
drawImage van dibujando la parte de la imagen que llega, con su tamaño, a partir de las coordenadas (x, y)
indicadas, usando bgcolor para pixels transparentes. Devuelven el control inmediatamente, aunque la
imagen no esté cargada del todo, caso en que se devuelve false. En cuanto se carga una parte adicional de
la imagen, el proceso que realiza el dibujo avisa al ImageObserver especificado, una interface de Applet que
permite seguir el proceso de carga de una imagen.
7.4. Obtención de las propiedades del sistema y uso de threads
Un applet puede obtener la información del sistema o el entorno en que se ejecuta que sea accesible. Para
acceder a las propiedades del sistema se usa un método static de la clase System: String salida =
System.getProperty("file.separator"). No se puede acceder a las propiedades del sistema: "java.class.path",
"java.home", "user.dir", "user.home", "user.name". Las propiedades del sistema accesibles son :
Método Significado de la propiedad del Método Significado de la propiedad
sistema del sistema
file.separator Separador de directorios (por java.class.version Número de version de las
ejemplo, "/" o "\") clases de Java
java.vendor Nombre específico del proveedor path.separator Separador en la variable Path
Java (por ejemplo, ":"
java.vendor.url URL del proveedor Java java.version Número de versión Java
line.separator Separador de líneas os.arch Arquitectura del SO
os.name Nombre del sistema operativo
Un applet puede ejecutarse con varios threads. Así, en las que se ejecutan los métodos mayores (init, start,
stop y destroy) dependen del navegador o el entorno de ejecución. Los métodos gráficos (paint, update y
repaint) se ejecutan siempre desde un thread especial del AWT.
Algunos navegadores dedican un thread para cada applet en una misma página; otros crean un grupo de
para cada applet (para poderlas matar al tiempo, por ejemplo). En cualquier caso se garantiza que todos los
threads creadas por métodos mayores pertenecen al mismo grupo.

Programación en Java 75
Se deben introducir threads en applets siempre que haya tareas pesadas, que si se incluyen en el init
bloquean la actividad del applet o la página html. Las tareas pesadas pueden ser de 2 tipos, las que sólo se
hacen una vez y las que se repiten. Un ejemplo de tarea que se repite puede ser una animación. La tarea se
pone en un bucle del thread, que debería crearse en el método start del applet y destruirse en stop. Así
cuando el applet no está visible se dejan de consumir recursos. Al crear el thread en el método start se pasa
una referencia al applet con this. El applet debe implementar la interface Runnable y por tanto definir el
método run, que es el centro del Thread.
Un ejemplo de tarea que se realiza una sola vez es la carga de imágenes *.gif o *.jpeg en un thread
especial. Pero los sonidos no se cargan en threads de forma automática; se deben crear. Es un caso típico
de programa producer-consumer: el thread es producer y el applet consumer. Los threads deben estar
sincronizados, para lo que se usan los métodos wait y notifyAll.
En el ejemplo de la derecha un thread con tarea repetitiva muestra como el método run se detendrá en
cuanto se ejecute el método stop, porque la referencia al thread está a null.
public void start() { public class MiApplet extends Applet {
if (repetitiveThread == null) { ...
repetitiveThread = new Thread(this); // lo crea public void init() {...}
} ...
repetitiveThread.start(); // arranca: start llama a // clase para cerrar la aplicación
run
static class WL extends WindowsAdapter {
}
public void windowClosing(WindowEvent e) {
public void stop() {
MiApplet.stop();
repetitiveThread = null; // para la ejecución
MiApplet.destroy();
}
System.exit(0);
public void run() {
}
...
} // fin de WindowAdapter
while (Thread.currentThread() ==
public static void main(String[] args) {
repetitiveThread) {
static MiApplet unApplet = new MiApplet();
... // tarea repetitiva.
Frame unFrame = new Frame("MiApplet");
}
unFrame.addWindowListener(new WL());
}
unFrame.add(unapplet, BorderLayout.CENTER);
Es interesante desarrollar aplicaciones que
puedan funcionar como applets y viceversa. En unFrame.setSize(400,400);
concreto, para que un applet se ejecute como
unApplet.init();
aplicación pueden seguirse los pasos:
unApplet.start();
1. Añadir un método main a la clase MiApplet
unFrame.setVisible(true);
2. Que el main cree un objeto de la clase
MiApplet e introducirlo en un Frame }
3. Que el main haga lo que haría el navegador: } // fin de la clase MiApplet
llamar a init y start de MiApplet.
4. Se puede añadir también una static inner class derivada de WindowAdapter que gestione el evento de
cerrar la ventana de la aplicación definiendo el método windowClosing. Este método llama a System.exit(0).
Según como sea el applet, el método windowClosing previamente debe llamar a MiApplet.stop y
MiApplet.destroy, lo que para applets hace el navegador. En este caso conviene que el objeto de MiApplet
creado por main sea static, en lugar de variable local. A la izquierda se ha presentado un ejemplo.

Programación en Java 76
8. EXCEPCIONES
Java incorpora gestión de errores. En general, los errores sintácticos se detectan en compilación y el resto
(semánticos) en ejecución. En Java una Exception es un tipo de error producido en ejecución. Algunas
excepciones son fatales; provocan el fin del programa, que conviene terminar ordenadamente y avisar del
tipo de error dado. Otras, como no encontrar un fichero, pueden ser tratadas, sin traumas.
Un buen programa debe gestionar la mayor cantidad
posible de errores. Se distinguen 2 formas de hacerlo.
Tradicional. Se devuelve un código de error, fruto de la
comprobación del entorno que ha llamado al método y
gestionando el resultado correcto o cada posible error.
Es complicado cuando hay varios niveles de llamadas
a métodos.
Con soporte del lenguaje. Consiste en usar los
recursos nativos del lenguaje para gestionar errores.
Es lo habitual en lenguajes como C++, VB y Java.
Las excepciones estándar de java representan los errores con 2 tipos de clases derivadas de Throwable,
Error y Exception. La figura muestra parcialmente su jerarquía de clases. La clase Error se refiere a errores
de compilación, sistema o JVM. Suelen ser irrecuperables, no dependen del programador ni los gestiona.
La clase Exception distingue RuntimeException, excepciones implícitas debidas a la programación. El resto
de clases derivadas de Exception son excepciones explícitas, que Java obliga a su gestión.
En el caso de RuntimeException, Java chequea y lanza automáticamente las excepciones durante la
ejecución. No se necesita establecer bloques try/catch. Representan 2 casos de errores de programación:
los que no suelen comprobarse (p.ej. recibir una referencia null en un método) y los que debieran
comprobarse (p.ej. sobrepasar el tamaño asignado a un array).
La razón de no comprobar estos tipos de errores, es la complicación que supone para el texto del código.
Las clases derivadas de Exception pueden pertenecer a distintos packages de Java: java.lang (Throwable,
Exception…); java.io (EOFException...) u otros. Por heredar de Throwable los tipos de excepciones pueden
usar los métodos String getMessage (extrae el mensaje asociado con la excepción), String toString
(devuelve un String que describe la excepción) y void printStackTrace (indica el método donde se lanzó).
8.1. Lanzamiento y captura de excepciones
Al darse una situación anómala es necesario lanzar una excepción. El proceso consiste en crear un objeto
Exception de la clase adecuada y lanzar la excepción con “throw” seguido del objeto Exception creado.
En el código de ejemplo la excepción // Código que lanza la excepción MyException al detectar el error
debe capturarse (catch) y gestionarse
MyException me = new MyException("MyException message");
en el método o en otro lugar del
programa (p.ej. un método anterior). throw me;
Al lanzar una excepción el método termina sin devolver ningún valor. Sólo en caso que el método incluya los
bloques try/catch/finally se ejecutará el bloque catch o finally. Todo método en que se produzcan uno o más
tipos de excepciones (y que no use directamente try/catch/finally) debe declararlas en el encabezamiento de
la función con la palabra throws. Si un método puede lanzar varias excepciones, se ponen detrás de throws
separadas por comas, en la forma de la primera línea.
public void leerFichero(String fich) throws EOFException, FileNotFoundException {…}
public void leerFichero(String fich) throws IOException {…}
La segunda línea especifica sólo una superclase de excepciones indicando que pueden lanzarse
excepciones de cualquiera de sus clases derivadas. Las excepciones pueden ser lanzadas por leerFichero
o algún método que él llame, ya que las clases EOFException y FileNotFoundException derivan de
IOException.
Ciertos métodos de packages y algunos creados producen excepciones. Si el usuario los llama sin tratarlas
se produce un error de compilación y un mensaje tipo: “Exception java.io.IOException must be caugth…”. El
programa no compilará si el usuario no gestiona la excepción con un bloque try-catch o relance la excepción
con throws en la cabecera del método. El compilador obliga a capturar las excepciones explícitas, aunque
no se traten. Es conveniente al menos indicar un mensaje con el tipo de excepción producida.
Para excepciones que no pertenecen a las void metodo1(){
RuntimeException, que hay que gestionar, deben
...
Programación en Java 77
usarse los bloques try, catch y finally. El código
try { … // Código que puede lanzar las excepciones
del bloque try está controlado. Al darse una
excepción el control salta o sale del try y pasa al } catch (IOException e1) { // Gestiona IOException
catch. Se pueden incluir tantos catch como sean
System.out.println(e1.getMessage());
necesarios. Cada uno trata un tipo de excepción.
Las excepciones se pueden capturar en grupo } catch (MyException e2) { // Gestiona MyException
con una superclase de la que deriven todas, o
individualmente. System.out.println(e2.getMessage()); return;

El bloque finally es opcional. Si se incluye sus } finally { // Se ejecuta siempre


sentencias se ejecutan siempre, sea cual sea la ...
excepción y se produzca o no. Se ejecuta aunque
en el try haya un continue, break o return. El } // Fin try
ejemplo a la derecha presenta un método que ...
controla una IOException de lectura de ficheros y
una MyException dada. } // Fin metodo1

En casos en que el código de un método genere una Exception y no se desee incluir su gestión, Java
permite que el método la pase o relance al método que lo llamó, sin incluir try/catch. Esto se consigue con la
adición de throws más el nombre de la excepción concreta tras la lista de argumentos del método. El
método llamante debe incluir los bloques try/catch o pasar también la excepción.
Así, se puede pasar la bola de un método a otro void metodo2() throws IOException, MyException {
hasta el último, el main. El ejemplo anterior
...
realizaba la gestión de las excepciones en el
propio método. En el código del metodo2 se } // Fin del metodo2
relanzan las excepciones.
Si un método llama a otros que pueden lanzar excepciones (p. ej. de un package de Java), puede capturar
las posibles excepciones y gestionarlas o desentenderse y remitirlas a otro método anterior en la pila. Si no
hace ninguna de las 2 cosas el compilador da un error, salvo que se trate de una RuntimeException.
El bloque finally debe ir tras los bloques catch. La try {
forma general de una sección donde se controlan
// Código que puede lanzar excepción tipo A, B o C
las excepciones es la mostrada en el cuadro.
Finally es útil en casos en que se necesite } catch (A a1) { // Gestiona la excepción A
recuperar o devolver a su situación original un
} catch (B b1) { // Gestiona la excepción B
elemento. No es liberar la memoria; eso lo hace
el garbage collector. Se puede pensar un bloque } catch (C c1) { // Gestiona la excepción C
try en que se abre un fichero para R/W de datos y
se cierra. El fichero abierto se debe cerrar tanto si } finally { // Sentencias que se ejecutan siempre
se produce una excepción como si no. }
De otro modo pueden darse problemas futuros. Así, se incluyen las sentencias de cierre en el bloque finally.
8.2. Creación de nuevas excepciones y trato con herencia
Se pueden crear excepciones heredando de la class MiExcepcion extends Exception {
clase Exception o una de sus clases derivadas,
public MiExcepcion() { // Constructor por defecto
eligiendo la que mejor se adapte al tipo de
excepción a definir. Las clases Exception suelen super();
tener 2 constructores, uno sin argumentos y otro
que recibe un String, en el que se suele definir un }
mensaje explicativo del tipo de excepción. public MiExcepción(String s) { // Con mensaje
Conviene que el constructor llame al de la clase super(s);
de la que deriva super(String). Al ser clases como
otras, se podrían incluir variables y métodos }
nuevos, como en el ejemplo del código. }
Si un método redefine otro de una superclase que usa throws, no tiene obligatoriamente que poder lanzar
todas las mismas excepciones de la clase superior. Podrá lanzar las mismas o menos, pero no más.
Tampoco podrá lanzar nuevas excepciones ni excepciones de una clase más general. Es una restricción útil
ya que el código que funciona con la clase base podrá trabajar de forma automática con referencias de
clases derivadas, incluyendo el trato de excepciones.

Programación en Java 78
9. ENTRADA/SALIDA DE DATOS
La manera de representar e/s en Java 1.1 es con streams (flujos de datos); conexiones entre el programa y
el origen o destino de los datos. La información se trasmite en serie a través del stream. Esto supone una
forma general de representar distintos tipos de comunicación.
Por ejemplo, al imprimir algo en pantalla, se usa un
stream que conecta el monitor al programa. Se
pasa al stream la orden de escribir y éste lo
muestra en pantalla. O para comunicaciones a
través de Internet o la lectura de datos del puerto
en serie.
Las clases para la comunicación del programa con
el entorno se disponen en el package java.io. En el
package existen 2 familias de jerarquías para E/S.
La diferencia estriba en que una opera con bytes y
la otra con caracteres (formados por 2 bytes al
usar Unicode). La estructura general es que para
un fin (entrada o salida) hay 2 clases que manejan
bytes y otras 2 que manejan caracteres.
Desde Java 1.0, la E/S de datos se podía hacer
con clases derivadas de InputStream (para lectura)
y OutputStream (para escritura). La figura muestra
sus clases derivadas.
Estas clases tienen los métodos básicos read y write que manejan bytes y que no se suelen usar
directamente. En Java 1.1 aparecieron 2 nuevas familias de clases, derivadas de Reader y Writer, que
manejan caracteres en vez de bytes y que resultan más prácticas para las aplicaciones en las que se
maneja texto. Las clases que heredan de Reader y Writer se incluyen en la siguiente figura.

Las clases con fondo gris definen de dónde o a dónde se envian datos; el dispositivo con que conecta el
stream. Las de fondo blanco añaden características particulares a la forma de enviarlos. La intención es que
se combinen para obtener el comportamiento deseado.
Por ejemplo: BufferedReader in = new BufferedReader(new FileReader("autoexec.bat")); crea un stream
que permite leer del archivo autoexec.bat. A partir de él se crea un objeto BufferedReader (que usa buffer).
Los caracteres que lleguen a través del FileReader pasan a través del BufferedReader; usan el buffer. Al
definir una comunicación con un dispositivo siempre se comienza indicando origen o destino de la
comunicación (clases en gris) y luego se añaden características (clases en blanco).
Se recomienda usar siempre que sea posible las clases Reader y Writer, dejando InputStream y
OutputStream para casos imprescindibles como la serialización y compresión.
9.1. Nomenclatura de las clases de java.io
Las clases java.io siguen Palabra Significado
una nomenclatura
InputStream, OutputStream Lectura/Escritura de bytes
sistemática para expresar
su función a partir del Reader, Writer Lectura/Escritura de caracteres
nombre. Se presentan en
File Archivos

Programación en Java 79
la tabla.
String, CharArray, ByteArray, Memoria (a través del tipo primitivo
Al leer un dato del disco se StringBuffer indicado)
lleva a memoria junto a los
Piped Tubo de datos
datos contiguos, de modo
que la siguiente vez que se Buffered Buffer
lea la probabilidad de que
esté en memoria se eleva. Filter Filtro
Para escritura lo mismo, Data Intercambio de datos en formato
intentando realizar en una propio de Java
sola operación de escritura
física varias sentencias Object Persistencia de objetos
individuales de escritura. Print Imprimir
La siguiente tabla muestra el uso de las clases que definen el lugar con que conecta el stream.
Clases Función que realizan
FileReader, FileWriter, FileInputStream y Leen y escriben en archivos de disco
FileOutputStream
StringReader, StringWriter, CharArrayReader, Se comunican con la memoria principal. En vez de
CharArrayWriter, ByteArrayInputStream, acceder de modo habitual al contenido, p. ej. de un String,
ByteArrayOutputStream, lo leen carácter a carácter. Útiles al usar un modo
StringBufferInputStream estándar de gestionar los dispositivos con un programa
PipedReader, PipedWriter, PipedInputStream, Se usan como un “tubo” o conexión bilateral. P. ej., en un
PipedOutputStream programa con dos threads permiten la comunicación entre
ellos. Un thread tiene el objeto PipedReader y el otro
PipedWriter. Si los streams están conectados, lo que se
escriba en el PipedWriter queda disponible para leerse del
PipedReader. Tambíen puede comunicar 2 programas
La siguiente tabla muestra clases que añaden características al comportamiento de un stream definido.
Clases Función que realizan
BufferedReader, BufferedWriter, Añaden un buffer al manejo de datos. Se reducen las operaciones
BufferedInputStream, directas sobre el dispositivo, por eficiencia. BufferedReader p. ej.
BufferedOutputStream dispone el método readLine que lee una línea y la devuelve como
String
InputStreamReader, Clases puente que permiten convertir streams que usan bytes en
OutputStreamWriter otros que manejan caracteres. Son la única relación entre ambas
jerarquías. No existen clases que realicen la transformación
inversa
ObjectInputStream, Pertenecen al mecanismo de serialización
ObjectOutputStream
FilterReader, FilterWriter, Clases base para aplicar filtros o procesos al stream de datos. Se
FilterInputStream, FilterOutputStream podrían extender para comportamientos a medida
DataInputStream, DataOutputStream Se usan para escribir y leer datos directamente en los formatos
propios de Java. Los convierten en algo ilegible , independiente
de la plataforma. Se usan por tanto para almacenamiento o
transmisiones entre máquinas con funcionamiento distinto
PrintWriter, PrintStream Tienen métodos adaptados para imprimir las variables de Java
con la apariencia normal. A partir de un boolean escriben “true” o
“false”, colocan la coma de un número decimal, etc.
9.2 E/S estándar
En Java, la entrada de teclado y la salida a pantalla se regulan con la clase System, del package java.lang.
Agrupa métodos y objetos en relación con el sistema local. Contiene, entre otros, tres objetos static:
System.in Objeto de la clase InputStream preparado para recibir datos desde la entrada estándar
System.out Objeto de la clase PrintStream que imprimirá los datos en la salida estándar del sistema

Programación en Java 80
System.err Objeto de la clase PrintStream. Usado para mensajes de error que por defecto se muestran
por pantalla
Estas clases permiten la comunicación alfanumérica con el programa a través de los métodos de la
siguiente tabla. Permiten la E/S a nivel muy elemental.
Métodos de System.in Función que realizan
int read() Lee un carácter y lo devuelve como int
Métodos de System.out y System.err Función que realizan
int print(cualquier tipo) Imprime en pantalla el argumento que se le pase. Puede recibir
cualquier tipo primitivo de variable de Java.
int println(cualquier tipo) Como el anterior, añadiendo '\n' (nueva línea) al final
Existen 3 métodos de System que permiten sustituir la E/S estándar. P. ej. para hacer que el programa lea
de un archivo y no del teclado: System.setIn(InputStream is); System.setOut(PrintStream ps); y
System.setErr(PrintStream ps);
El argumento de setIn no tiene que ser necesariamente de tipo InputStream. Es una referencia a la clase
base, y por tanto puede apuntar a objetos de cualquier clase derivadas (como FileInputStream). El
constructor de PrintStream acepta un OutputStream, luego se puede dirigir la salida estándar a cualquier
clase definidas para salida.
Si se usan estas sentencias con un compilador Java 1.1 se obtiene un mensaje de método obsoleto
(deprecated) al crear un objeto PrintStream. Esto pretendía generalizar el uso de PrintWriter, pero existen
casos en los que es imprescindible un elemento PrintStream. Java 1.2 vuelve a admitirlo.
9.2.1. Salida de texto y variables por pantalla
Para imprimir en pantalla se usan los métodos System.out.print y System.out.println. Sus características:
1. Pueden imprimir valores escritos directamente en el código System.out.println("Hola, Mundo!");
o cualquier tipo de variable primitiva de Java
System.out.println(57);
2. Se pueden imprimir varias variables en una llamada con el
double numeroPI = 3.141592654;
operador + (concatenación). Equivale a convertir a String las
variables que no lo sean y concatenar las cadenas de System.out.println(numeroPI);
caracteres (el primer argumento debe ser String):
String hola = new String("Hola");
System.out.println("Hola, Mundo! " + numeroPI);
System.out.println(hola);
Los objetos System.out y System.err son de clase PrintStream. Aunque imprimen las variables de modo
legible, no permiten dar a la salida un formato distinto al disponible por defecto, un formato a medida.
9.2.2. Lectura del teclado
Para leer de teclado se puede usar el método System.in.read de clase InputStream, que lee un carácter en
cada llamada. Su valor de retorno es entero. Si se espera otro tipo hay que hacer una conversión explícita
mediante un cast. System.in.read puede lanzar la excepción java.io.IOException que obliga a definirla.
Como en el código de la izquierda.
char c;
try { String frase = new String("");
c=(char)System.in.read(); try {
} while((c=System.in.read()) != '\n')
catch(java.io.IOException ioex) { frase = frase + c; // frase.append(c);
// operaciones para la excepción }
}
catch(java.io.IOException ioex) {}
Para leer datos más largos que un carácter es necesario un bucle while o for y concatenar los caracteres.
Por ejemplo, para leer una línea se podría usar un bucle while guardando los caracteres leídos en un String
o en un StringBuffer (más rápido que String), como se muestra en el código de la derecha. Una vez leida
una línea, ésta puede contener números de coma flotante, etc. Pero hay una manera más fácil de
conseguirlo, usando la biblioteca java.io.

Programación en Java 81
Para facilitar la lectura de teclado se puede leer una línea entera con una instrucción usando un objeto
BufferedReader. El método String readLine de BufferReader lee todos los caracteres hasta encontrar un '\n'
o '\r' y los devuelve como String (sin incluir '\n' ni '\r'). String readLine puede lanzar java.io.IOException.
System.in es un objeto de clase InputStream y BufferedReader pide un Reader en el constructor. La
relación entre ambos es InputStreamReader, que acepta un InputStream como argumento del constructor y
es clase derivada de Reader.
Por tanto si se desea leer una línea InputStreamReader isr = new InputStreamReader(System.in);
completa desde la entrada estándar se
BufferedReader br = new BufferedReader(isr);
podrá usar el código de la derecha, que
lee una línea completa del teclado. String frase = br.readLine(); // Se lee la línea con una llamada
El thread que ejecute este código se para en esta línea hasta que el usuario termine la línea (intro). Para
manejar una línea entera, la clase java.util.StringTokenizer posibilita separar una cadena de carácteres en
las palabras (tokens) que la forman, conjuntos de caracteres separados por espacio, '\t', '\r', o por '\n'.
Asimismo, cuando se precise se convertirán los tokens en números. La tabla muestra algunos métodos de
la clase StringTokenizer.
Métodos Función que realizan
StringTokenizer(String) Constructor a partir de la cadena que hay que separar
boolean hasMoreTokens() ¿Hay más palabras disponibles en la cadena?
String nextToken() Devuelve el siguiente token de la cadena
int countTokens() Devuelve el número de tokens que se pueden extraer de la frase
La clase StreamTokenizer de java.io aporta más posibilidades que StringTokenizer, pero es más compleja.
Separa en tokens lo que entra en un InputStream o Reader. Para convertir un String del tipo “3.141592654”
en el valor double correspondiente se crea un objeto Double a partir de él y luego se extrae su valor double:
double pi = (Double.valueOf("3.141592654")).doubleValue();
El uso de estas clases facilita el acceso desde teclado, resultando un código más legible. Además tiene la
ventaja de que se puede generalizar a la lectura de archivos.
9.3. Lectura y escritura de archivos
El manejo de archivos sigue el esquema de la E/S estándar. Java dispone las clases FileInputStream y
FileOutputStream para leer y escribir bytes en archivos. En archivos de texto son preferibles FileReader y
FileWriter. Se puede construir un objeto de cualquiera de estas clase a partir de un String con el nombre o
dirección en disco del archivo o con un objeto de la clase File que lo represente.
El código de la derecha muestra un ejemplo. Si no se FileReader fr1 = new FileReader("archivo.txt");
encuentra el archivo indicado, los constructores de
// es equivalente a:
FileReader y FileInputStream pueden lanzar la
excepción java.io.FileNotFoundException. Los File f = new File("archivo.txt");
constructores de FileWriter y FileOutputStream pueden
FileReader fr2 = new FileReader(f);
lanzar java.io.IOException.
Si no se encuentra el archivo indicado se crea. Por defecto, las 2 clases escriben al inicio del archivo. Para
escribir detrás del último registro (append), se usa un segundo argumento de tipo boolean con valor true:
FileWriter fw = new FileWriter("archivo.txt", true).
9.3.1. Clases file y filedialog
Un objeto de la clase File puede representar un archivo o un directorio. Tiene los constructors de la derecha.
File(String name) File f1 = new File("c:\\windows\\notepad.exe"); // La barra '\' se escribe '\\'
File(String dir, String name) File f2 = new File("c:\\windows"); // Un directorio
File(File dir, String name). File f3 = new File(f2, "notepad.exe"); // Es igual a f1
Se puede indicar el nombre de un archivo, nombre y directorio o sólo el directorio, como path absoluto y
relativo al directorio actual. Para saber si el archivo existe se puede llamar al método boolean exists(). Los
métodos de la siguiente tabla informan de las características del fichero o directorio y en su caso los
métodos para recuperar el path del archivo.

Programación en Java 82
Métodos Función que realizan Métodos Función que realizan
boolean isFile() True si el archivo existe boolean isDirectory() True si existe el directorio
long length() Tamaño del archivo en B mkdir() Crea directorio
long lastModified() Fecha última modificación String[] list() Lista archivos del directorio
boolean canRead() True si se puede leer String getPath() Path del objeto File
boolean canWrite() True si se puede escribir String getName() Nombre del archivo
delete() Borrar archivo / directorio String getAbsolutePath() Path absoluto
RenameTo(File) Cambiar el nombre String getParent() Devuelve el directorio padre
Para preguntar por un archivo, típicamente se usa una caja de diálogo. La clase java.awt.FileDialog la
presenta según cada SO para guardar o abrir ficheros. Sus constructores son los de la derecha.
Type puede ser FileDialog.LOAD o FileDialog.SAVE según la FileDialog(Frame fr)
operación a realizar. Es fácil conectar este diálogo con un File,
FileDialog(Frame fr, String title)
con los métodos String getFile y String getDirectory. P. ej.:
FileDialog(Frame fr, String title, int type)
FileDialog fd = new FileDialog(f, "Elija un archivo");
fd.show();
File f = new File(fd.getDirectory(), fd.getFile());
9.3.2. Lectura y escritura de archivos de texto
Se puede crear un objeto BufferedReader para leer de un archivo de texto en la forma:
BufferedReader br = new BufferedReader(new FileReader("archivo.txt"));
Con el objeto de tipo BufferedReader se puede conseguir lo mismo que con el método readLine y la clase
StringTokenizer. En caso de archivos es importante usar el buffer porque escribir en disco es lento y leer
directamente y no de uno en uno hace más eficiente el acceso. Es el ejemplo de la izquierda.
// Lee un archivo entero como con el teclado try {
String texto = new String(); FileWriter fw = new FileWriter("escribeme.txt");
try { BufferedWriter bw = new BufferedWriter(fw);
FileReader fr = new FileReader("archivo.txt"); PrintWriter salida = new PrintWriter(bw);
entrada = new BufferedReader(fr); salida.println("Esta es la primera línea");
String s; salida.close();
while((s = entrada.readLine()) != null) bw = new BufferedWriter(new FileWriter("escribeme.txt",
true)); // Modo append
texto += s;
salida = new PrintWriter(bw);
entrada.close();
salida.print("Y esta la segunda. ");
}
double b = 123.45;
catch(java.io.FileNotFoundException fnfex) {
salida.println(b);
System.out.println("File not found: " + fnfex);}
salida.close();
catch(java.io.IOException ioex) {}
}
cacth(java.io.IOException ioex) { }
La clase PrintWriter es más práctica para escribir un archivo de texto. Dispone los métodos print y println,
idénticos a los de System.out de PrintStream. Un objeto PrintWriter se puede crear a partir de un
BufferedWriter (para disponer de buffer), que se crea a partir del FileWriter al que se pasa el nombre del
archivo. Así, escribir en el archivo es tan fácil como en pantalla. El de la izquierda es un ejemplo de uso.
9.3.3. Archivos no de texto
DataInputStream y DataOutputStream son // Escritura de una variable double
clases de Java 1.0 no alteradas. Para leer y
DataOutputStream dos = new DataOutputStream(

Programación en Java 83
escribir datos primitivos directamente (sin
new BufferedOutputStream(
convertir a String) siguen siendo más útiles.
new FileOutputStream("prueba.dat")));
Son clases diseñadas para trabajar de
manera conjunta. Una puede leer lo que la double d1 = 17/7;
otra escribe, como una secuencia de bytes.
dos.writeDouble(d1);
Se usan para guardar datos de forma
dos.close();
independiente de la plataforma (o enviarse
por red entre máquinas de distinta // Lectura de la variable double
naturaleza).
DataInputStream dis = new DataInputStream(
Pero obligan a usar clases hijas de
InputStream y OutputStream y por tanto más new BufferedInputStream(
complicadas de usar. El código del ejemplo new FileInputStream("prueba.dat")));
primero escribe en el fichero prueba.dat y
después lee los datos. double d2 = dis.readDouble();

9.4 Serialización
Es el proceso por el que un objeto se puede convertir a secuencia de bytes, permitiendo, en el futuro,
reconstruirlo. Esto es útil para guardar un objeto en un archivo o enviarlo por red. Para que una clase use la
serialización, debe implementar la interface Serializable, que no define métodos. Casi todas las clases
estándar de Java son serializables. Una clase podría serializarse declarándola como en el prototipo.
public class [nombre de la clase] implements Serializable { }
Para escribir y leer objetos se usan las clases ObjectInputStream y ObjectOutputStream, que cuentan con
los métodos writeObject() y readObject(). Por ejemplo:
Debe tenerse en cuenta que readObject ObjectOutputStream objout = new ObjectOutputStream(
devuelve un Object sobre el que se hará un
new FileOutputStream("archivo.x"));
casting para que el objeto sea útil. La
reconstrucción necesita que el archivo *.class String s = new String("Me van a serializar");
esté al alcance del programa.
objout.writeObject(s);
Al serializar un objeto, automáticamente se
serializan sus variables y objetos miembro. A su ObjectInputStream objin = new ObjectInputStream(
vez se serializan los que esos objetos miembro new FileInputStream("archivo.x"));
puedan tener (todos deben ser serializables).
También se reconstruyen de igual manera. String s2 = (String) objin.readObject();

Si se serializa un Vector con varios Strings, todo se convierte en una serie de bytes. Al recuperarlo la
reconstrucción deja todo en el lugar en que se guardó. Si 2 objetos contienen una referencia a otro, éste no
se duplica si se escriben o leen ambos del mismo stream. Es decir, si el mismo String está contenido 2
veces en el Vector, sólo se guardaría una vez y al recuperarlo sólo se crearía un objeto con 2 referencias en
el vector.
Aunque la serialización es automática, puede especificarse cómo hacer las cosas. La palabra clave
transient permite indicar que un objeto o variable miembro no sea serializada con el resto del objeto. Al
recuperarlo, lo que esté marcado como transient será 0, null o false (no se llama a ningún constructor) hasta
que se le dé un nuevo valor. Es el típico caso de una password que no se guarda por seguridad.
Las variables y objetos static no son serializados. Para ello hay que escribir el código. P. ej. programar un
método que serialice objetos estáticos al que se llamará tras serializar el resto de elementos. Así, habría
que recuperarlos explícitamente tras recuperar el resto de objetos. Las clases que implementan Serializable
pueden definir 2 métodos con los que controlar la serialización. No están obligadas porque estos métodos
obtiene directamente el comportamiento por defecto. Si se definen serán los que se usen al serializar.
private void writeObject(ObjectOutputStream stream) throws IOException
private void readObject(ObjectInputStream stream) throws IOException
El primero permite indicar qué se escribe o añadir otras static double g = 9.8;
instrucciones al comportamiento por defecto.
private void writeObject(ObjectOutputStream
El segundo debe poder leer lo que escribe writeObject. stream) throws IOException {
Puede usarse para poner al día las variables que lo
stream.defaultWriteObject();
necesiten al ser recuperado un objeto. Hay que leer en el
orden en que se escriben los objetos. stream.writeDouble(g);

Programación en Java 84
Se puede obtener el comportamiento por defecto en los }
métodos llamando a stream.defaultWriteObject y
private void readObject(ObjectInputStream
stream.defaultReadObject.
stream) throws IOException {
Para guardar explícitamente los tipos primitivos se
stream.defaultReadObject();
pueden usar los métodos de ObjectInputStream y
ObjectOutputStream, idénticos a los de DataInputStream g = stream.readDouble(g);
y DataOutputStream (writeInt, readDouble...) o guardar
objetos de sus clases equivalentes (Integer, Double...). }

Por ejemplo, si en una clase llamada Tierra se necesita que al serializar un objeto siempre le acompañe la
constante g (9,8) definida static el código podría ser el siguiente.
La interface Externalizable extiende interface Externalizable {
Serializable, con el mismo objetivo,
public void writeExternal(ObjectOutput out) throws IOException;
pero sin comportamiento automático,
debe programarse. Presenta 2 public void readExternal(ObjectInput in) throws IOException,
métodos a implementar. Cuando se
transforma un objeto, writeExternal es ClassNotFoundException;
responsable de lo que se hace. }
Sólo se guardará lo que se indique en el método. El método readExternal debe ser capaz de recuperar lo
guardado por writeExternal. La lectura debe ser en el mismo orden que la escritura. Debe saberse que
antes de llamar a este método se llama al constructor por defecto de la clase.
9.5. Lectura de un archivo en un servidor de Internet
Con la dirección de Internet de un //Lectura del archivo (texto HTML)
archivo, la biblioteca Java
URL direccion = new URL("http://www1.ceit.es/subdir/MiPagina.htm");
permite leerlo con un stream.
String s = new String();
Es una aplicación sencilla que
muestra la polivalencia del String html = new String();
concepto de stream. En el
try {
package java.net existe la clase
URL, que representa una BufferedReader br = new BufferedReader(
dirección de Internet.
new InputStreamReader(
La clase URL tiene el método
InputStream openStream(URL direccion.openStream()));
dir) que abre un stream con while((s = br.readLine()) != null)
origen en la dirección de Internet.
html += s + '\n';
A partir de ahí, se trata como
cualquier elemento InputStream, br.close();
como se muestra en el ejemplo }
del cuadro.
catch(Exception e) {
System.err.println(e);
}

Programación en Java 85
10. OTRAS CAPACIDADES DE JAVA
Java incorpora muchos conceptos de la informática moderna. Desarrollarlos todos es difícil por motivos de
espacio, pero se puede hacer, al menos, mención de las capacidades más interesantes.
10.1. Java Foundation Classes (JFC) y Java 2D
Las JFC, Java™ Foundation Classes son un conjunto de componentes y características de ayuda en la
construcción de GUIs. Incluye todo tipo de elementos gráficos como botones, paneles, menús y ventanas,
con ventajas sobre AWT. Swing es una parte de las JFC que permite incorporar en las aplicaciones
elementos gráficos de forma más versátil y con más capacidades que AWT. Algunas características son:
1. Cualquier programa que usa componentes Swing puede elegir el aspecto de sus ventanas y elementos
gráficos: entorno WS, entorno Motif (entornos Unix) o Metal (aspecto propio de Java)
2. Cualquier componente gráfico de Swing presenta más propiedades que el correspondiente elemento del
AWT: Los botones pueden incorporan imágenes, nuevos layouts y paneles, menús…
3. Posibilidad de Drag & Drop; seleccionar componentes con el ratón y arrastrar a otro lugar de la pantalla
En la versión JDK 1.2 se incorpora como parte de las JFC Java 2D, que permite a los desarrolladores
incorporar texto, imágenes y gráficos de calidad en 2D. Además da soporte para imprimir documentos
complejos. A partir de la versión 1.2 de Java las JFC forman parte del propio JDK.
10.2. Java Media Framework (JMF) y Java 3D
El API JMF (Java Media FrameWork) especifica una arquitectura, un protocolo de transmisión de datos y
unos elementos gráficos simples y unificados para la reproducción de contenidos multimedia.
El API de Java 3D™ es un conjunto de clases para crear aplicaciones y applets con elementos 3D. Ofrece
la posibilidad de manipular geometrías complejas en 3D. La ventaja frente a otros entornos de programación
3D es que permite crear aplicaciones gráficas 3D independientes del sistema. Java 3D es un conjunto de
clases, interfaces y bibliotecas de alto nivel que permiten aprovechar la aceleración gráfica hw que
incorporan muchas tarjetas, ya que las llamadas a los métodos de Java 3D son transformadas en llamadas
a funciones de OpenGL o Direct3D. Conceptualmente y oficialmente Java 3D forma parte del API JMF, pero
las bibliotecas se instalan de forma independiente al JMF.
10.3. Javabeans
El API de JavaBeans hace posible escribir componentes sw en Java. Los componentes son elementos
reutilizables que poder incorporar gráficamente a otros componentes como applets y aplicaciones usando
herramientas gráficas de desarrollo. Cada componente ofrece sus características concretas (por ej. sus
métodos públicos y eventos) a los entornos gráficos de desarrollo permitiendo su manipulación visual. Son
análogos a otros componentes de algunos entornos visuales, como por ejemplo los controles de VB. El BDK
(Beans Developer Kit) es un conjunto de herramientas para desarrollar JavaBeans. Se trata de un kit no
incorporado en los distintos JDK de Java.
10.4. Java en la red. Servlets, RMI y Java IDL
Java se presenta de forma estándar a todas las plataformas y SO, un conjunto de clases que permiten la
comunicación entre aplicaciones que se ejecutan en distintas máquinas. El package java.net del API de
Java incluye las clases para establecer conexiones, crear servidores, enviar y recibir datos, y para el resto
de operaciones usadas en las comunicaciones en red. Existen otros APIs independientes preparados
especialmente para realizar tareas, como Servlets, RMI y Java IDL. Muchos de estos APIs usan
internamente las clases presentes en java.net.
Los Servlets son módulos que permiten sustituir o usar Java en lugar de programas CGI escritos en otros
lenguajes como C/C++ o Perl. Los programas CGI son aplicaciones que se ejecutan en un servidor Web en
respuesta a una acción de un navegador remoto (petición de página HTML, envío de los datos de un
formulario, etc.). Permiten generar páginas HTML dinámicas, en las que el contenido puede variar y que por
tanto no pueden almacenarse en un fichero en el servidor.
Los Servlets no tienen entorno gráfico ya que se ejecutan en el servidor. Reciben datos y su salida son
principalmente ficheros de texto HTML. Los servlets son desarrollados usando el API Java Servlet, que es
una extensión de Java. Es necesario instalar el sw específico de Java Servlet.
Tanto RMI (Remote Method Invocation) como Java IDL (Java Interface Definition Language) son
herramientas para desarrollar aplicaciones distribuidas. Estas aplicaciones presentan la característica de
que una aplicación puede ejecutar funciones y métodos en varios ordenadores distintos. Usando una
referencia a un objeto que se encuentra en un equipo remoto, es posible ejecutar métodos de ese objeto
desde una aplicación de un equipo distinto.

Programación en Java 86
RMI y Java IDL proporcionan los mecanismos con los que distintos objetos distribuidos se comunican y
transmiten información. Son por tanto tecnologías que permiten la creación y uso de objetos distribuidos;
objetos o programas que interactúan en diferentes plataformas y ordenadores a través de una red.
RMI es una solución basada íntegramente en Java. Incorpora métodos para localizar los objetos remotos,
comunicarse con ellos e incluso enviar un objeto de Java, "por valor", de un objeto distribuido a otro. Java
IDL permite la conectividad entre objetos distribuidos utilizando CORBA.
CORBA (Common Object Request Broker Architecture) es un estándar para la interconexión entre objetos
distribuidos. Existen implementaciones de CORBA en varios lenguajes, lo que posibilita la comunicación de
objetos en Java, C/C++, COBOL… RMI y Java IDL están incluidos en el JDK 1.2 de Sun. En el caso de
Java IDL se precisa de una utilidad adicional (idltojava) que genera el código necesario para comunicarse
con cualquier implementación CORBA.
10.5. Seguridad en Java
El entorno natural de trabajo es en red y en especial Internet. Entonces, la seguridad adquiere una
importancia vital. Desde su aparición Java ha ido incorporando elementos para proporcionar un mayor
control sobre la seguridad de los datos y programas enviados a través de una red. Los distintos JDK
incorporan herramientas para añadir seguridad a las aplicaciones Java: firmas digitales, transmisión segura
de datos... Java permite establecer distintos niveles de seguridad, lo que ofrece flexibilidad para asignar o
denegar permisos.
10.6. Acceso a bases de datos (JDBC)
JDBC (Java DataBase Connectivity) es el estándar de Java para conectarse con BBDD. Se estima que
aproximadamente la mitad del sw que se crea incorpora operaciones de R/W con BBDD. JDBC está
diseñado para ser independiente de la plataforma y de la BBDD sobre la que se actúe.
Para conseguir esta independencia, JDBC ofrece un sistema estándar de interconexión con las BBDD, muy
similar al SQL. Las BBDD comerciales crean los elementos necesarios que actúan como puente entre JDBC
y su BBDD. La versión JDBC 1.0 forma parte del JDK 1.1. Después de distintas revisiones la versión JDBC
2.0 se incluye en el JDK 1.2.
10.7. Java Native Interface (JNI)
JNI (Java Native Interface) es el interface de programación de Java para ejecutar código nativo, es decir
código compilado propio de una plataforma o equipo. Se incluye en el JDK las herramientas necesarias para
su uso. JNI permite al código Java que se ejecuta en la JVM interactuar con aplicaciones y bibliotecas
escritas en otros lenguajes, como C/C++ o incluso ensamblador. Incorpora a su vez las herramientas para
ejecutar código Java desde aplicaciones desarrolladas en otros lenguajes.
El entorno JNI ofrece a los métodos nativos usar objetos de Java de igual forma que el código Java. Tanto
la parte de Java como la parte nativa de una aplicación pueden crear, actualizar y acceder a los objetos
programados en Java y compartirlos.

Programación en Java 87

También podría gustarte