Está en la página 1de 124

See discussions, stats, and author profiles for this publication at: https://www.researchgate.

net/publication/287646094

Introducción a la programación en Java

Technical Report · January 2000


DOI: 10.13140/RG.2.1.1701.6408

CITATIONS READS
0 4,522

1 author:

Hector Antonio Villa-Martinez


Universidad de Sonora (Unison)
19 PUBLICATIONS   15 CITATIONS   

SEE PROFILE

Some of the authors of this publication are also working on these related projects:

Development of statistical tools for mobile devices. Fourth Stage. View project

Statistics to Go View project

All content following this page was uploaded by Hector Antonio Villa-Martinez on 21 December 2015.

The user has requested enhancement of the downloaded file.


Introducción a la Programación en Java

Héctor Antonio Villa Martínez


Departamento de Matemáticas
Universidad de Sonora
2
Presentación
Estos apuntes representan un esfuerzo por reunir en un solo volumen el material de Java
conforme se enseña en la materia de Introducción a la Computación I en la Universidad de
Sonora. La materia es obligatoria para alumnos de tercer semestre de la Licenciatura en
Ciencias de la Computación y es requisito para poderla cursar haber aprobado el curso de
Programación de Computadoras.

Hay muchos libros sobre Java, los hay para principiantes y para avanzados, la mayoría
están en inglés y los pocos en español no son fáciles de encontrar y menos a buen precio.
En general estos libros tratan de abarcar mucho más material (y a veces sin la profundidad
necesaria) que el requerido para la asignatura.

Los objetivos de la materia de Introducción a la Computación I son introducir al estudiante


en la resolución de problemas algorítmicos y proporcionarle estrategias de solución,
estructuras básicas de datos, así como análisis de complejidad algorítmica. Para lograr estos
objetivos se utiliza una metodología conocida como programación orientada a objetos y
para aplicar esta metodología se utiliza el lenguaje Java por ser orientado a objetos y
porque el ambiente de desarrollo (compilador y manuales) es gratis.

Los apuntes están distribuidos de la siguiente forma: En el capítulo 1 se presenta una breve
historia y semblanza de Java y la forma de obtener el ambiente de desarrollo. El capítulo 2
es la única parte teórica en esta obra y muestra los principales conceptos de la
programación orientada a objetos. En el capítulo 3 se describe la sintaxis de Java. El
capítulo 4 muestra la forma de implementar en Java los conceptos de orientación a objetos
aprendidos en el capítulo 2. El capítulo 5 se ocupa de los applets que son programas en Java
que pueden correr en el Internet. El capítulo 6 presenta la forma de programar interfaces
gráficas de usuario usando una librería llamada AWT. Por último, el capítulo 7 muestra el
manejo básico de archivos en Java.

Disponibilidad del código


Todos los programas que se presentan están disponibles en línea en la página web del
curso. La dirección es http://148.225.83.24/ic1/index.html

3
4
Índice

1 Introducción al lenguaje Java 7


1.1 Historia 7
1.2 Características 7
1.3 El ambiente de desarrollo 8
1.4 Applets y aplicaciones 8
2 Conceptos básicos de objetos 13
2.1 Introducción 13
3 Elementos del lenguaje 17
3.1 Introducción 17
3.2 Declaración de variables 17
3.3 Comentarios 18
3.4 Literales 18
3.5 Operadores 19
3.6 Instrucciones 21
4 Clases y objetos en Java 27
4.1 Introducción 27
4.2 Declaración de clases 27
4.3 Creación de objetos 27
4.4 Constructores 29
4.5 Asignación de objetos 29
4.6 Comparación de objetos 30
4.7 Métodos 31
4.8 Paso de parámetros a los métodos 33
4.9 Alcance de variables 36
4.10 Sobrecarga de métodos 37
4.11 Variables y métodos de clase 39
4.12 Protección de variables y métodos 41
4.13 Herencia 41
4.14 Sobreposición de métodos 42
5 Applets 45
5.1 Introducción 45
5.2 Ciclo de vida 45
5.3 Argumentos 47
5.4 Gráficas 49
5.5 Tipo de letra 52
5.6 Color 53
5.7 Imágenes 55
6 AWT 59
6.1 Introducción 59
6.2 Componentes básicos 60
6.3 Diseños 68
6.4 Eventos 77
6.5 Componentes avanzados 86

5
6.6 Aplicaciones con AWT 105
7 Archivos 109
7.1 Definición 109
7.2 Principales clases lectoras 109
7.3 Principales clases escritoras 113
7.4 Archivos de datos 118
7.5 Archivos remotos 121

6
Capítulo 1 Introducción al lenguaje Java
1.1 Historia
Java es un lenguaje de propósito general, orientado a objetos y concurrente[1]. Tuvo sus
inicios a principios de los 90s cuando Sun Microsystems decidió incursionar en el mercado
de la electrónica de consumo y desarrollar programas para controlar dispositivos
electrónicos y fundó una compañía subsidiaria llamada FirstPerson[2]. El primer proyecto
de FirstPerson fue desarrollar una interface interactiva para controlar los aparatos
eléctricos. Como parte de este objetivo un ingeniero de Sun llamado James Gosling definió
el lenguaje Oak basándose en la sintaxis de C++ pero eliminando algunas características,
como los apuntadores, que a su juicio hacen que la programación en C++ sea propensa a
errores y agregando algunas otras, como la recolección automática de basura, para hacer la
vida de los programadores más fácil.

FirstPerson no tuvo el éxito esperado y cerró en 1994. Parecía que Oak también estaba
condenado a desaparecer, sin embargo, Bill Joy cofundador de Sun, se dio cuenta que el
Internet podía ser usado para disputar el monopolio de software de Microsoft. Después de
un cambio de nombre, Java fue liberado en agosto de 1995.

1.2 Características
Java es neutral a la arquitectura. El compilador toma un archivo fuente y genera un archivo
objeto con códigos byte para una máquina virtual conocida como JVM (Java Virtual
Machine). El intérprete ejecuta los códigos byte del archivo objeto. Por lo tanto es portable
al nivel de código objeto.

Java es totalmente orientado a objetos. La tecnología de objetos se presenta como el


siguiente paso lógico de la programación procedural[3]. La programación procedural (por
ejemplo en Fortran o C standard) enfatiza la especificación de tareas (procedimientos) para
realizar una tarea. Cuando los sistemas se vuelven grandes y distribuidos, ya no es tan fácil
definir estas tareas. Un método más natural es definir objetos y acciones entre objetos. Java
soporta los mecanismos standard de la orientación a objetos tales como encapsulación,
herencia y polimorfismo.

Java es seguro. El chequeo de datos es muy estricto. Antes de que un código byte sea
ejecutado es revisado por la JVM para detectar accesos no autorizados a la memoria. El
manejo de la memoria es sencillo. Cada objeto se crea con el operador new y el
programador no es responsable de liberar la memoria. Cuando un objeto ya no se utiliza, el
recolector de basura automáticamente reclama ese espacio.

Java es distribuido. Tiene librerías para programación con TCP/IP (el protocolo de
comunicaciones standard de Internet).

Java es concurrente. Tiene librerías para hacer programas con múltiples hilos de ejecución
simultánea.

7
1.3 El ambiente de desarrollo
Java se presenta en 3 distribuciones: la edición empresarial (enterprise edition), la edición
standard (standard edition) y la edición micro (micro edition). La versión standard es la
versión original de Java. La edición empresarial es igual de poderosa que la versión
standard pero incluye librerías para programar aplicaciones grandes, distribuidas y con uso
intensivo de bases de datos. Por último, la edición micro esta orientada a la programación
de dispositivos con poca memoria, por ejemplo, teléfonos celulares o aparatos
electrodomésticos inteligentes, un poco siguiendo el objetivo inicial de Oak.

Para una introducción al lenguaje es suficiente trabajar con la edición standard. La versión
actual de Java es la 1.3, también conocida como Java 2, versión 1.3, edición standard (J2SE
1.3). Sun Microsystems regala el ambiente de desarrollo conocido como Java 2 SDK
(System Development Kit, kit de desarrollo de sistemas) o JDK (Java Development Kit, kit
de desarrollo de Java) para Windows, Solaris y Linux en la dirección
http://java.sun.com/j2se/1.3/

El ambiente de desarrollo consta del compilador, el intérprete de la máquina virtual también


conocido como JRE (Java Run-time Environment) que se puede instalar por separado, si se
desea, un programa para correr applets, el manual en línea y otras herramientas.

Para instalar el ambiente de desarrollo en Windows hay que bajar el archivo auto-extraíble
(extensión EXE) correspondiente al J2SE y el archivo comprimido (extensión ZIP) que
corresponde a la documentación. El archivo EXE se ejecuta y por default Java se instalará
en el directorio C:\jdk1.3. Luego hay que editar el archivo C:\Autoexec.bat y modificar las
variables de ambiente PATH y CLASSPATH agregándoles los caminos C:\jdk1.3\bin y
C:\jdk1.3\lib\classes.zip;C:\jdk1.3\lib\tools.jar respectivamente, por último el archivo
ZIP de la documentación se descomprime en el directorio C:\jdk1.3. El manual se puede
leer usando un navegador.

1.4 Applets y aplicaciones

1.4.1 Definición

Java tiene dos formas de hacer un programa: como applet o como aplicación.

Un applet es un programa que está diseñado para ser invocado por una página HTML,
agregándole así funcionalidad. La característica principal de un applet es que, aunque reside
en la misma computadora que la página HTML que lo invoca, al ser accesada esta página
desde una computadora remota, el applet viaja a través de Internet y se ejecuta en el
navegador de la computadora que hizo el acceso. Por esta razón y como seguridad contra
programadores maliciosos, un applet no puede accesar ningún recurso de la computadora
donde se está ejecutando y tampoco puede comunicarse con ninguna otra computadora que
no sea la computadora donde estaba originalmente. Para correr applets no es necesario tener
instalado el JDK porque ya los principales navegadores comerciales (Internet Explorer y
Netscape) traen integrada una máquina virtual de Java.

8
Una aplicación es un programa que corre localmente en la computadora donde está
instalada. No tiene las restricciones de seguridad de los applets y tiene acceso a todos los
recursos locales pero para correr necesita que por lo menos el JRE esté instalado.

Hay que aclarar que fuera de la restricción que tienen los applets de estar incrustados en
una página HTML y de que no pueden accesar recursos en la computadora donde corren, la
programación de applets y aplicaciones es parecida y pueden usar cualquier librería del
lenguaje por igual.

1.4.2 Mi primera aplicación

Parar mostrar las diferencias y coincidencias entre applets y aplicaciones se verá un


ejemplo sencillo. En el capítulo 2 comenzaremos a ver con detalle la sintaxis, por ahora
basta con copiar el código y seguir los pasos para compilar y correr los programas.

Paso 1. Abrir una ventana de MS-DOS.


Paso 2. Usando un editor de texto (Edit o Notepad) crear un archivo con el nombre
HolaMundo.java, e introducir el código que se muestra en el listado 1-1 teniendo cuidado
de respetar las mayúsculas y minúsculas, tanto en el nombre del archivo como en el código.

Listado 1-1
public class HolaMundo {

public static void main (String args [])


{
System.out.println ("Hola mundo\n");
}
}

Paso 3. Compilar el programa con el comando javac HolaMundo.java. El compilador


genera el archivo HolaMundo.class. Este es el archivo con los códigos byte que será
ejecutado por el intérprete de la máquina virtual.
Paso 4. Ejecutar el programa con el comando java HolaMundo. Ver figura 1-1.

9
Figura 1-1. Compilación y ejecución de una aplicación

1.4.3 Mi primer applet

Paso 1. Abrir una ventana de MS-DOS.


Paso 2. Usando un editor de texto (Edit o Notepad) crear un archivo con el nombre
HolaMundoApplet.java, e introducir el código que se muestra en el listado 1-2 teniendo
cuidado de respetar las mayúsculas y minúsculas, tanto en el nombre del archivo como en
el código.

Listado 1-2
import java.awt.Graphics;
import java.applet.*;

public class HolaMundoApplet extends Applet {

public void paint (Graphics g) {


g.drawString ("Hola mundo !!!", 5, 25);
g.drawString ("Mi tamano es ancho: " + getWidth () + ", alto: " + getHeight (), 5, 35);
}
}

Paso 3. Editar un archivo de texto que se llame HolaMundo.html y copiar el código del
listado 1-3.

10
Listado 1-3
<HTML>

<HEAD>
<TITLE>Mi primer applet</TITLE>
</HEAD>

<BODY>
<P><H2>Este es mi primer applet</H2><P>
<APPLET CODE="HolaMundoApplet.class" WIDTH=250 HEIGHT=150>
</APPLET>
</BODY>

</HTML>

Paso 4. Compilar el archivo Java con el comando javac HolaMundoApplet.java.

Paso 5. Accesar la página HolaMundo.html usando un navegador o el visor de applets del


JDK (comando appletviewer). El resultado debe verse como la figura 1-2.

Figura 1-2. Ejecutando el applet con Netscape.

11
Referencias
[1] The Java Language Specification. Second Edition. James Gosling, Bill Joy, Guy Steele
and Gilad Bracha. Sun Microsystems Inc. 2000

[2] Tutorial de Java. Agustín Froufe. Disponible en http://server2.ok.cl/java/

[3] Understanding Object-Oriented: A Unifying Paradigm. Tim Korson and John


McGregor. Communications of the ACM. (33), 9, septiembre 2000

12
Capítulo 2. Conceptos básicos de objetos
2.1 Introducción
Es un paradigma de programación donde los elementos de los lenguajes son objetos y
clases. Otros paradigmas de programación son:

1. Programación estructurada
• Los elementos son estructuras o bloques de instrucciones de decisión y repetición,
evitando el uso del goto.
• Uso de procedimientos y funciones.
• Principales exponentes: Pascal y C.
2. Programación funcional.
• Los elementos son funciones.
• Principales exponentes: LISP y Scheme.
3. Programación lógica.
• Los elementos son hechos y reglas para derivar nuevos hechos.
• Principal exponente: Prolog.

Java no es el único lenguaje orientado a objetos. Otros lenguajes que también manejan este
paradigma son C++, Smalltalk, Eiffel y varios más.

La ventaja del paradigma orientado a objetos es que, en general, aumenta la productividad


de los programadores haciendo que los sistemas sean modulares y promueve la
reutilización del software y el ocultamiento de la información. Cada uno de estos conceptos
se explica a continuación.

Un objeto es una entidad del mundo real. Por ejemplo, mi automóvil es un objeto. La
factura de mi computadora también es un objeto. Intuitivamente los objetos del mismo tipo
se pueden agrupar en clases. Mi automóvil y el automóvil del vecino son dos instancias de
algo que puede llamarse la clase de los automóviles. Se distinguen entre sí por que
posiblemente son de distinto modelo, color y fabricante y porque tienen placa, número de
serie y dueño diferente. Esas características que distinguen un objeto de otro se llaman
atributos. Las clases definen no solo los atributos que los objetos de esa clase pueden
tener, sino también su comportamiento esperado. Un automóvil puede estar prendido o
apagado, puede estar acelerando o frenando, o puede tener una velocidad x en este
momento. Todos los objetos de una misma clase tienen el mismo comportamiento y el
mismo número de atributos, sin embargo, cada objeto tendrá valores distintos para esos
atributos, lo que se conoce formalmente como estado del objeto y así podrá diferenciarse
de otros objetos de la misma clase. En resumen, una clase es un prototipo que define las
variables y los métodos comunes para los objetos de un mismo tipo.

Se pueden representar objetos del mundo real usando objetos de software. Puede que nos
interese representar un automóvil de la vida real como un objeto de software en un
programa que juegue a las carreras. Sin embargo, también se pueden usar objetos de

13
software para representar objetos abstractos. Por ejemplo, un evento es un objeto que se
utiliza para representar la acción que ocurre cuando un usuario oprime un botón dentro de
un programa que tenga interface gráfica.

Los lenguajes orientados a objetos implementan el estado de un objeto de software usando


variables y el comportamiento por métodos. Un objeto de software podría modelar mi
automóvil y decir que su velocidad actual es de 0Km/h y apagado. Estas variables y
métodos se conocen como variables y métodos de instancia a diferencia de las variables y
métodos de clase (también conocidos como variables y métodos estáticos) y que se
explican abajo. Los métodos se pueden sobrecargar, es decir, se pueden definir varios
métodos con el mismo nombre pero con distintos tipos de argumentos. La sobrecarga
simplifica la programación, no es necesario definir un método para sumar enteros y otro
para sumar reales.

Al programar en un lenguaje orientado a objetos, lo primero es definir la clase, es decir, la


plantilla o prototipo que tendrán los objetos. Para usar un objeto es necesario crearlo, en
lenguaje técnico se dice instanciar la clase o crear una instancia de ella. Cuando se crea una
instancia de una clase, se crea un objeto de ese tipo y se le asigna memoria para las
variables de instancia. Luego se pueden invocar los métodos de instancia del objeto para
hacer algo. Las instancias de la misma clase comparten la misma implementación de los
métodos.

Además de las variables y métodos de instancia se pueden definir variables y métodos de


clase. Las variables y métodos de clase se pueden accesar desde una instancia de la clase o
desde la misma clase. No es necesario crear una instancia de la clase para poder usar sus
variables y métodos de clase. Los métodos de clase solo pueden accesar las variables de
clase, no pueden usar las variables de instancia. Todas las instancias de una clase tienen la
misma copia de las variables y métodos de clase, si un objeto cambia el valor de una
variable de clase, el valor cambia para todo los objetos de ese tipo. Las variables de clase se
utilizan en situaciones donde una variable tiene el mismo valor para todos los objetos que
pertenecen a esa clase. Por ejemplo, supongamos que todos los automóviles tienen 4
ruedas. En este caso definir una variable de instancia para el número de ruedas sería
ineficiente porque cada objeto tendría su copia de la variable y el valor sería el mismo en
cada copia. En este caso es mejor declarar una variable de clase para el número de ruedas.

Las clases promueven la reutilización del software. Los programadores pueden usar clases
creadas por otros programadores para crear objetos.

Otro concepto importante es la encapsulación. Este mecanismo permite esconder las


variables de un objeto del mundo externo. Cuando usamos la palanca de cambios para
cambiar de velocidad, no nos interesa el mecanismo interior, ni saber como funciona, lo
único que tenemos que saber es a que velocidad vamos a pasar. De la misma manera, si
usamos un objeto de software no debe interesarnos la forma en que está implementado sino
solo conocer que método vamos a invocar.

La interface de un objeto es la lista de métodos públicos que se pueden invocar. Así la


implementación de un objeto puede cambiar sin afectar otras partes del programa, a esto se

14
le conoce como ocultamiento de información. La encapsulación también fomenta la
modularidad, el código fuente de un objeto se puede desarrollar y mantener
independientemente del código fuente de otros objetos.

El último concepto del paradigma orientado a objetos que se revisará en este capítulo es la
herencia. La herencia es un mecanismo que permite definir clases en función de otras
clases. Por ejemplo, los pickups y los sedanes son tipos distintos de automóviles. En
lenguaje técnico se dice que los pickups y los sedanes son subclases de la clase automóvil.
De igual manera, se dice que la clase automóvil es la superclase de los pickups y sedanes.

Cada subclase hereda el estado de la superclase en forma de variables. Los pickups y los
sedanes comparten los estados, tienen número de serie, número de placa, color, etc.
También, cada subclase hereda los métodos de la superclase. Los pickups y los sedanes
comparten el comportamiento, pueden estar prendidos o apagados, tener una velocidad, etc.

El estado y el comportamiento de las subclases no están limitados por el estado y el


comportamiento de la superclase. Las subclases pueden agregar variables y métodos a los
heredados de la superclase. Un sedan tiene 2 filas de asientos y una camioneta puede tener
hasta 3. Las subclases pueden sobreponer métodos heredados y tener implementaciones
especializadas para esos métodos. Si un pickup tiene sobremarcha (overdrive) la clase
pickup puede sobreponer el método para cambiar velocidades y poder usarla.

La herencia también ayuda a la reutilización del software. Un programador puede


aprovechar clases programadas por otra persona para definir las suyas.

15
16
Capítulo 3 Elementos del lenguaje
3.1 Introducción
En este capítulo vamos a revisar brevemente los elementos de la sintaxis de Java. El
recuento no intenta ser exhaustivo, se asume que el alumno ha tomado ya un curso de
programación en lenguaje C o C++.

3.2 Declaración de variables


Las variables se declaran igual que en C++, es decir, primero se pone el tipo y luego la
variable o lista de variables. Cada variable deberá tener un tipo. Los tipos pueden ser
primitivos, de clase o de arreglo.

3.2.1 Tipos primitivos

Los tipos primitivos son los números enteros, números reales, booleanos y caracteres.

Los enteros siempre tienen signo. Hay cuatro tipos de enteros. La diferencia está en el
tamaño que ocupan y el rango que pueden guardar.

Tipo Tamaño (bytes) Rango


byte 1 -128 a 127
short 2 -32,768 a 32,767
int 4 -231 a 231 – 1
long 8 -263 a 263 – 1

Los números reales pueden almacenar números con parte decimal. Hay dos tipos de
números reales. La diferencia es la cantidad de decimales que pueden representar.

Tipo Tamaño (bytes) Precisión


float 4 sencilla
double 8 doble

El tipo boolean representa variables que pueden tener uno de dos valores: true o false.

El tipo char ocupa 2 bytes y puede guardar un carácter Unicode.

3.2.2 Tipos de clase

Cada vez que se define una clase se está creando un nuevo tipo y se pueden declarar
variables de ese tipo. Por ejemplo, es posible declarar que una variable es de tipo String
porque en la librería standard ya está definida una clase llamada String.

17
3.2.3 Arreglos

En Java los arreglos se declaran sin tamaño. Para poder usarlos es necesario asignarles un
tamaño. Se puede lograr esto dándoles valores iniciales:

int vector[] = {4, 2, 3, 10, 2};

o usando el operado new:

int vector[];
vector = new int[10];

o en un solo paso:

int vector[] = new int[10];

Los elementos de un arreglo se accesan mediante un índice que puede valer entre 0 y s – 1,
donde s es el tamaño del arreglo.

Usando cualquiera de las declaraciones anteriores, las siguientes instrucciones son válidas.

vector[1] = 20;
vector[2] = vector[0] + 10;
vector[0]++;

Los arreglos tienen asociada la variable length que indica el tamaño del arreglo. La forma
de accesarla es nombre-del-arreglo.length. En el caso del arreglo vector se puede usar así:

t = vector.length;

3.3 Comentarios
Si un comentario ocupa varias líneas se puede delimitar usando los símbolos /* y */. Si solo
se desea un comentario que ocupe una línea se usa el símbolo // para indicar al compilador
que ignore lo que sigue hasta el fin de línea.

3.4 Literales
El manejo de literales es igual que en C++. Las literales de string se delimitan entre
comillas dobles:

"hola mundo"

Si se desea que el string tenga comillas dobles es necesario poner el carácter de escape '\'.

"Juan dijo: \"hola mundo\""

18
Las literales de carácter se delimitan entre comillas sencillas: 'h', '$', '7'. Java maneja las
mismas literales para caracteres no imprimibles que C++, por ejemplo '\n' para el retorno de
carro y '\t' para el tabulador.

Las literales enteras se pueden especificar en base 10, por ejemplo 259, pero también se
pueden especificar en base 8 colocando un cero antes, por ejemplo 064 o en base 16
poniendo 0x, por ejemplo 0xA9.

Las literales reales se pueden escribir en formato normal, por ejemplo 2.943 o en formato
científico, por ejemplo 756.51E10 o 4.938E-2.

Las literales booleanas son las palabras reservadas true y false.

3.5 Operadores
Un operador es un símbolo especial que representa una operación y que se usa en una
expresión.

Una expresión es una instrucción que devuelve un valor.

3.5.1 Operadores aritméticos

Java tiene los mismos operadores aritméticos que C++. El + para la suma, el – para la resta,
el * para la multiplicación, el '/' para la división y el '%' para el módulo.

Si todos los operandos en una expresión son enteros, la expresión regresa un entero. Basta
que uno de los operandos sea real, para que el resultado sea real. Así la división 2/3 regresa
0 y 2/3.0 regresa 0.6666.

3.5.2 Operadores de asignación

Java tiene los mismos operadores de asignación que C++. El símbolo = representa la
asignación sencilla:

a = 2;

Los símbolos ++ y -- incrementan y decrementan en 1 el valor de la variable:

a = 2;
a++; // a vale ahora 3

También existen los operadores aritméticos con asignación como muestra la siguiente tabla:

19
Expresión Significado
x += y x=x+y
x -= y x=x–y
x *= y x=x*y
x /= y x=x/y

3.5.3 Operadores de comparación

Java maneja los mismos operadores de comparación que C++: menor que (<), menor o
igual que (<=), mayor que (>), mayor o igual que (>=), igual que (==) y distinto que (!=).

3.5.4 Operadores lógicos

Java utiliza 2 símbolos para la operación AND (Y), el & y el &&. La diferencia es que el
operador & evalúa ambos lados de la expresión y el && puede que no evalúe la parte
derecha si la parte izquierda es falsa (y por lo tanto ya sabe que toda la expresión será
falsa). A este comportamiento se le conoce como evaluación en cortocircuito. El uso de
uno u otro operador solo es relevante si la parte derecha de la expresión tiene efectos
laterales como una asignación.

Por ejemplo, si tenemos este segmento de código:

int x = 2, a = 10;
if ((a < 0) & (x++ > 0))
instrucciones

El operador & evalúa la expresión de la izquierda (a < 0) y aunque es falsa evalúa la parte
derecha y por lo tanto incremente el valor de x a 3.

En cambio si usamos el operador &&:

int x = 2, a = 10;
if ((a < 0) && (x++ > 0))
instrucciones

Al notar que la expresión izquierda es falsa, la evaluación hacer cortocircuito y no se evalúa


la parte derecha. Por lo tanto el valor de x se mantiene en 2.

La mayor parte de los autores recomienda usar el operador && y no poner asignaciones en
las condiciones.

Para la operación OR (O) también hay 2 operadores, el | y el ||. Al igual que en el AND, el |
evalúa las dos partes de la expresión y el || no evalúa la parte derecha si la parte izquierda es
verdadera y por lo tanto sabe que toda la expresión es verdadera.

Para la operación XOR (OR exclusivo) el operador es el ^.

20
Para la operación NOT (no) el operador es el !.

3.5.5 Concatenación de strings

En Java el operador + está sobrecargado, es decir, tiene más de un significado. Aplicado a


números representa la suma, aplicado a strings representa la concatenación.
4 + 3 // es una suma
"hola " + "mundo" // concatena los strings

Si se mezcla un string y un número, el número se convierte a string. Un truco para convertir


números a strings es concatenar el número a un string vacío.

String s = "" + 3.14 // s tiene ahora el string "3.14"

3.5.6 Operador ? :

El operador ? : funciona igual que en C++. La expresión:

(z > 0 ? –1 : 1)

Toma el valor –1 si z es mayor que 0 o 1 si z es menor o igual que 0.

3.5.7 Operador instanceof

El operador instanceof sirve para preguntar si una variable es o no de un tipo en particular.


El operador regresa verdadero o falso según el caso.

int x;
x instanceof int // regresa verdadero
x instanceof double // regresa falso

3.5.8 Operador new

El operador new crea nuevas instancias de clases. Esto se verá con más detalle en el
capítulo siguiente.

3.6 Instrucciones
Java tiene las mismas instrucciones y sintaxis que C++. Las instrucciones van separadas
por punto y coma. Los bloques de instrucciones de agrupan con corchetes { y }. Vamos a
ver un breve repaso de la sintaxis de cada una de las instrucciones.

21
3.6.1 Instrucción if

Sintaxis:
if (condición)
instrucción;

La condición se evalúa, si es verdadera se ejecuta la instrucción, si es falsa continúa con la


siguiente instrucción.

La palabra reservada else sirve para ejecutar una instrucción alterna si la condición es falsa.

Sintaxis:
if (condición)
instrucción 1;
else
instrucción 2;

La condición se evalúa, si es verdadera se ejecuta la instrucción 1, si es falsa se ejecuta la


instrucción 2.

El segmento de código siguiente muestra el uso de un if para decidir si un número entero es


par o impar.

if ((n % 2) == 0)
System.out.println (n + " es par");
else
System.out.println (n + " es impar");

3.6.2 Instrucción for

Sintaxis:
for (inicio; condición; postcondición)
instrucción;

El flujo de control es el siguiente:

1. Se evalúa la expresión de inicio


2. Se evalúa la condición
3. Si la condición es falsa el ciclo termina. Si es verdadera ejecuta la instrucción (que
puede ser un bloque de instrucciones encerrado entre corchetes), evalúa la
postcondición y regresa al paso 2.

El segmento de código imprime 10 veces el letrero en la pantalla:

for (i = 0; i < 10; i++)


System.out.println ("Hola mundo");

22
3.6.3 Instrucción while

Sintaxis:
while (condición)
instrucción;

La instrucción se ejecuta mientras la condición sea verdadera.

Este segmento de código muestra el uso de un while para escribir el índice del primer
elemento negativo de un arreglo o un mensaje de error si no existe ninguno.

int x[] = {...};


int i = 0;
boolean exito = false;
while (!exito && (i < x.length))
if (x[i] < 0)
exito = true;
else
i++;
if (exito)
System.out.println ("El primer numero negativo esta en" + i);
else
System.out.println ("No existe ningún numero negativo");

3.6.4 Instrucción do while

Sintaxis:
do {
instrucción;
} while (condición);

La instrucción se ejecuta mientras la condición sea verdadera. La diferencia con el ciclo


while es que el do while primero ejecuta la instrucción y luego revisa la condición. Por lo
tanto el do while siempre se hace por lo menos una vez.

Este segmento de código también imprime 10 veces el letrero en la pantalla:

i = 0;
do {
System.out.println ("Hola mundo");
i++;
} while (i < 10);

23
3.6.5 Instrucción switch

Sintaxis
switch (expresión) {
case valor 1 : instrucción 1;
case valor 2 : instrucción 2;
...
[default : instrucción n;] // el corchete cuadrado indica que es opcional
}

La expresión se evalúa y se compara con cada uno de los valores de la parte case. Si
encuentra una coincidencia ejecuta las instrucciones a partir de ese caso. Si no encuentra
ninguna coincidencia ejecuta la instrucción correspondiente a la parte default si es que está
presente o continua con la siguiente instrucción al switch si no hay default.

Se puede indicar que solo ejecute un caso usando la instrucción break (ver sección 3.6.7)
como en el siguiente segmento de código, donde dado un número entero que representa el
mes (1 es enero, 2 es febrero, ..., 12 es diciembre) asigna en la variable día el número de
días del mes o –1 si el número de mes está fuera de rango.

switch (mes) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: dia = 31;
break;
case 4:
case 6:
case 9:
case 11: dia = 30;
break;
case 2: dia = 28;
break;
default: dia = -1;
}

3.6.6 Instrucción return

La instrucción return sirve para salir de un método. Si el método regresa un valor el return
va seguido de una expresión que se convertirá en el valor del método. En el capítulo
siguiente se explicarán con más detalle los métodos que regresan valores y se verán
ejemplos del uso del return.

24
3.6.7 Instrucción break

La instrucción break sirve para salir de un switch. El break también se puede utilizar para
terminar un ciclo for, while o do while como en el ejemplo siguiente que escribe 10 veces
el letrero "Hola mundo" en la pantalla:

int i = 0;
while (true) {
System.out.println ("Hola mundo");
i++;
if (i == 10)
break;
}

Si el ciclo está anidado dentro de otro ciclo, la ejecución continúa con el ciclo externo.

3.6.8 Instrucción continue

La instrucción continue suspende la ejecución de un ciclo y continúa con la siguiente


iteración.

Este segmento de código muestra el uso del continue dentro de un ciclo para contar los
elementos positivos de un arreglo.

int cuenta = 0;
for (int i = 0; i < vector.length; i++) {
if (vector[i] < 0)
continue;
cuenta++;
}
System.out.println ("El arreglo tiene " + cuenta + "elementos positivos");

25
26
Capítulo 4 Clases y objetos en Java.
4.1 Introducción
Al programar en Java siempre se tienen que definir por lo menos una clase. El archivo que
contiene esa clase debe llamarse exactamente igual que la clase (con todo y mayúsculas) y
debe tener la extensión .java. Es posible programar varias clases en un archivo, pero solo
una de ellas podrá ser pública (eso por ahora no tiene mayor inconveniente) y el archivo
deberá llamarse igual que la clase pública.

Cada clase tiene variables y métodos definidos. Los métodos se programan de una manera
muy semejante a los procedimientos y funciones de C++.

Las aplicaciones deben tener un método llamado main (excepto en los applets) de tipo
estático (static), público (public) y void y con un argumento de tipo arreglo de strings
(String[]).

En el caso de los applets, como se verá en el capítulo correspondiente, deben definir, ya sea
un método llamado init o bien uno llamado paint.

4.2 Declaración de clases


El listado 4-1 muestra el código para una clase llamada Punto que representa un punto en
un plano.

Listado 4-1
public class Punto {
public int x = 0;
public int y = 0;
}

Este código está creando una clase, y a la vez un nuevo tipo, llamada Punto. La clase Punto
tiene dos variables enteras de instancia x y y. La palabra public significa que cualquier otra
clase puede accesar libremente esas variables.

4.3 Creación de objetos


Se puede crear un objeto de la clase Punto instanciando la clase con el operador new.
Cuando se crea un objeto de tipo Punto se asigna memoria para el objeto y para sus
variables x y y. Además, dentro del objeto x y y se inicializan a 0.

El listado 4-2 muestra como otra clase, llamada pruebaPunto utiliza la clase Punto que se
acaba de declarar para dos objetos de tipo Punto que representan a los puntos en el plano
(20, 10) y (5, 7).

27
Listado 4-2
public class pruebaPunto {
public static void main (String args[])
{
Punto p1 = new Punto ();
p1.x = 20; p1.y = 10;
Punto p2 = new Punto ();
p2.x = 5; p2.y = 7;
System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y);
System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y);
}
}

La clase Punto no tiene ningún método main por lo tanto no puede correr por sí sola. La
clase pruebaPunto si lo tiene y por lo tanto es la que puede correr. Para poder correr este
ejemplo hay que copiar el código en 2 archivos llamado Punto.java y pruebaPunto.java
respetando las mayúsculas y minúsculas. Por ahora los 2 archivos deben estar en el mismo
directorio, se compilan por separado, usando el comando javac y se corre la clase
pruebaPunto (es la que tiene el main) invocando al intérprete con el comando java como se
muestra en la figura 4-1.

Figura 4-2. Ejecución de la clase pruebaPunto.

Las variables de instancia y los métodos se accesan usando lo que se conoce como la
notación punto. En este ejemplo, si tenemos un objeto asignado a la variable p1 y ese
objeto tiene una variable llamada x, entonces el valor de esa variable se accesa mediante
p1.x.

28
4.4 Constructores.
Los constructores son métodos que se utilizan para inicializar objetos cuando son creados.
Los constructores siempre tienen el mismo nombre que la clase y se invocan de manera
automática al usar el operador new. Puede haber varios constructores con el mismo nombre
pero con distintos argumentos (sobrecarga del constructor). Al momento de crear un objeto
nuevo Java sabe que constructor invocar, si hay más de uno, por el número y el tipo de
argumentos que se pasen en el new.

El listado 4-3 muestra la clase Punto con un constructor. El listado 4-4 muestra como la
clase pruebaPunto invoca al constructor de Punto cuando crea los objetos.

Listado 4-3
public class Punto {
public int x;
public int y;

Punto (int x, int y)


{
this.x = y;
this.y = y;
}
}

Listado 4-4
public class pruebaPunto {
public static void main (String args[])
{
Punto p1 = new Punto (20, 10);
Punto p2 = new Punto (5, 7);
System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y);
System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y);
}
}

4.5 Asignación de objetos


El operador de asignación (=) usado sobre objetos, hace que los dos objetos apunten a la
misma área de memoria. Si un objeto cambia un atributo, el cambio se reflejará también en
el otro objeto. Revisemos el ejemplo que se muestra en el listado 4-5.

Listado 4-5
public class asigna {

29
public static void main (String args[])
{
Punto p1 = new Punto (20, 10);
Punto p2 = p1;
p1.x = 2; p1.y = 8;
System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y);
System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y);
}
}

La salida del programa es:

El punto 1 es x = 2 y = 8
El punto 2 es x = 2 y = 8

Como vemos el valor de las variables de instancia de p2 cambiaron sin haberlo indicado en
forma explícita. Al momento de asignar p1 a p2 las dos variables se convirtieron en el
mismo objeto. La solución a este problema es crear un nuevo objeto para p2 (hay que
recordar que al crear un nuevo objeto se le asigna memoria) y luego copiar las variables de
instancia de p1. El listado 4-6 muestra una forma de hacerlo.

Listado 4-6
public class asigna {
public static void main (String args[])
{
Punto p1 = new Punto (20, 10);
Punto p2 = new Punto (p1.x, p1.y);
p1.x = 2; p1.y = 8;
System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y);
System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y);
}
}

La salida de este programa es:

El punto 1 es x = 2 y = 8
El punto 2 es x = 20 y = 10

4.6 Comparación de objetos


El operador de comparación (==) usado sobre objetos, solo checa si dos objetos ocupan la
misma área de memoria o no. No revisa si tienen el mismo contenido o no. Veamos el
programa que se presenta en el listado 4-7.

Listado 4-7
public class compara {

30
public static void main (String args[])
{
Punto p1 = new Punto (20, 10);
Punto p2 = new Punto (20, 10);
if (p1 == p2)
System.out.println ("Son iguales");
else
System.out.println ("Son diferentes");
}
}

La salida de este programa siempre es:

Son diferentes

La razón es que el operador == checa si p1 y p2 ocupan la misma área de memoria no si


tienen los mismos valores para los atributos. La solución para comparar dos objetos es
comparar atributo por atributo y se muestra en el listado 4-8. Después, cuando se vea la
forma de agregarle métodos a las clases, veremos que una solución más sencilla y elegante
sería agregarle un método que compare a la clase Punto.

Listado 4-8
public class compara {
public static void main (String args[])
{
Punto p1 = new Punto (20, 10);
Punto p2 = new Punto (20, 10);
if ((p1.x == p2.x) && (p1.y == p2.y))
System.out.println ("Son iguales");
else
System.out.println ("Son diferentes");
}
}

La salida de este programa es:

Son iguales

4.7 Métodos
Los métodos tienen 5 partes básicas: un modificador public o private para indicar si el
método es visible o no fuera de la clase, el tipo que regresa, el nombre del método, una lista
de parámetros y el cuerpo del método. La forma de invocar un método es con la notación
punto.

31
Veamos ahora la declaración de una clase llamada Rectángulo que representa un rectángulo
en el plano. La clase tiene el constructor sobrecargado y se ha programado un método que
calcula el área del rectángulo.

Listado 4-9
public class Rectangulo {
public int ancho;
public int alto;
public Punto origen;

Rectangulo (int w, int h, Punto x)


{
ancho = w;
alto = h;
origen = x;
}

Rectangulo (int w, int h)


{
this (w, h, new Punto (0, 0));
}

public int area ()


{
return alto * ancho;
}
}

Este código declara otra clase, y por lo tanto otro tipo, llamada Rectángulo que contiene
dos variables de instancia de tipo entero ancho y alto y una tercera variable origen de tipo
Punto. Hay que notar como el nombre de la clase Punto se utiliza en la declaración de una
variable como el tipo de la variable. Se puede usar el nombre de una clase donde sea que se
pueda usar un tipo primitivo.

Así como ancho es un entero y alto también es un entero, origen es un Punto. Por otro
lado, Rectángulo tiene un Punto. Esta distinción entre es un y tiene un es importante
porque solo un objeto que es un punto puede ser usado cuando se necesita un Punto.

La clase Rectángulo tiene dos constructores. Tienen el mismo nombre pero distinta firma.
La firma de un método es el nombre del método y la lista de los tipos de los argumentos
que recibe.

La clase define también un método llamado área. Este método tiene un modificador public
para indicar que puede ser llamado desde otra clase, tiene un tipo de regreso int para indicar
que regresa un valor entero, luego viene el nombre y entre paréntesis la lista de argumentos
que en este caso está vacía. El cuerpo del método se encierra entre corchetes { y }. Como el

32
método regresa un valor entero debe tener una instrucción return seguida de una expresión
entera.

El listado 4-10 muestra la forma de utilizar la clase Rectángulo. El programa crea dos
instancias de la clase Rectángulo y cada objeto tiene su propia copia de las variables de
instancia y acceso al método área. Hay que notar como el método área se invoca usando la
notación punto y que es necesario crear un objeto para poder llamar a un método de
instancia.

Listado 4-10
public class pruebaRectangulo {
public static void main (String args[])
{
Rectangulo r1 = new Rectangulo (10, 5);
Rectangulo r2 = new Rectangulo (7, 9);
System.out.println ("El área de r1 es: " + r1.area ());
System.out.println ("El área de r2 es: " + r2.area ());
}
}

La salida del programa es:

El área de r1 es: 50
El área de r2 es: 63

4.8 Paso de parámetros a los métodos


Los parámetros de tipos primitivos (int, float, char, etc), se pasan por valor. El método hace
una copia del parámetro y cualquier cambio de valor se hace sobre la copia y no sobre el
original. Analicemos el programa del listado 4-11.

Listado 4-11
public class Foo {
public void cambia (int x)
{
x = 4;
System.out.println ("En cambia x es: " + x);
}
public static void main (String args[])
{
int x = 2;
Foo f = new Foo ();
System.out.println ("Antes x es: " + x);
f.cambia (x);
System.out.println ("Despues x es: " + x);
}
}

33
La salida del programa es:

Antes x es: 2
En cambia x es: 4
Despues x es: 2

El método cambia hizo una copia de x. Cuando le asignó el valor 4 lo hizo a la copia y no al
original, al momento de terminar el método la copia de x desaparece y solo queda el
original. Esto quedará más claro en la sección denominada alcance de variables.

Los parámetros de tipo no primitivos (arreglos y clases), se pasan por dirección. El método
tiene la dirección del parámetro y cualquier cambio de valor se hace sobre el original. En el
ejemplo del listado 4-12 se muestra como el método cambia tiene acceso al argumento
original sin importar si tiene o no el mismo nombre que dentro del método.

Listado 4-12
public class Foo2 {

public void cambia (int x[])


{
x[0] = 10;
x[1] = 5;
}

public static void main (String args[])


{
int z[] = {1, 2, 3, 4, 5};
System.out.print ("Antes z es: ");
for (int i = 0; i < z.length; i++)
System.out.print (z[i] + " ");
System.out.println ();
cambia (z);
System.out.print ("Despues z es: ");
for (int i = 0; i < z.length; i++)
System.out.print (z[i] + " ");
}
}

La salida del programa es:

Antes z es: 1 2 3 4 5
Despues z es: 10 5 3 4 5

Si se necesita cambiar un parámetro se puede utilizar un método que regrese un valor y


asignarlo a la variable original como se muestra en el listado 4-13.

34
Listado 4-13
public class Foo3 {

public int cambia (int x)


{
x = 4;
return x;
}

public static void main (String args[])


{
int x = 2;
Foo3 f = new Foo3 ();
System.out.println ("Antes x es: " + x);
x = f.cambia (x);
System.out.println ("Despues x es: " + x);
}
}

La salida del programa es:

Antes x es: 2
Despues x es: 4

El ejemplo anterior funciona si solamente se necesita cambiar un parámetro, si se necesita


que cambien 2 o más argumentos entonces se puede usar una clase auxiliar que envuelva a
esas variables y pasar como argumento una instancia de esa clase. En el listado 4-14 se
muestra una forma de hacerlo. El listado 4-15 muestra la clase auxiliar.

Listado 4-14
public class Foo4 {
public void cambia2 (FooAux x)
{
x.a = 4;
x.b = 10;
}

public static void main (String args[])


{
FooAux z = new FooAux (100, 200);
Foo4 f = new Foo4 ();
System.out.println ("Antes a es: " + z.a + " b es: " + z.b);
f.cambia2 (z);
System.out.println ("Despues a es: " + z.a + " b es: " + z.b);
}
}

35
Listado 4-15
public class FooAux {
int a;
int b;

FooAux (int a, int b)


{
this.a = a;
this.b = b;
}
}

La salida del programa es:

Antes a es: 100 b es: 200


Despues a es: 4 b es: 10

4.9 Alcance de variables


El alcance de una variable es el lugar del programa donde esa variable es conocida y puede
usarse. Por su alcance en Java las variables se puede clasificar en 3 tipos:

• globales a la clase.
• locales a un método.
• locales a un bloque.

Puede haber variables con el mismo nombre, siempre y cuando tengan distintos alcances.
Las variables con alcances más locales inhiben el acceso a las variables más globales.
Cuando se hace referencia a una variable, se busca primero entre las locales al bloque,
luego entre las locales al método y al final entre las globales a la clase. El listado 4-16
muestra un ejemplo de un programa con variables de los 3 tipos.

La salida del programa es:

antes: a es 1, b es 2, c es 3
en el for, b es 0
en el for, b es 1
en el for, b es 2
despues: a es 1, b es 2, c es 10

El objetivo del ejercicio es mostrar como la variable global b no cambió su valor a pesar de
haber sido usada en el ciclo for y como el valor de c si se alteró.

36
Listado 4-16
public class Scope {
int a, b, c; // a, b y c son globales a la clase y accesibles en todos los metodos

void foo1 (boolean f)


{
double a;
// a y f son locales al método
a = 22.0 / 7;
// este cambio se hace sobre la a local, no la global
for (int b = 0; b < 5; i++)
System.out.println ("en el for, b es: " + b);
// esta b es local al bloque for, fuera de el no existe
c = 10;
// este cambio se hace sobre la c global
while (f) {
int i, c;
// la i y la c son locales al ciclo while
i = 22;
c = 0;
// el cambio es sobre la c local, no sobre la global
f = false;
}
}
public static void main (String args[])
{
Scope s = new Scope ();
s.a = 1; s.b = 2; s.c = 3;
System.out.println ("antes: a es " + s.a + ", b es " + s.b + ", c es " + s.c);
s.foo1 (true);
System.out.println ("despues: a es " + s.a + ", b es " + s.b + ", c es " + s.c);
}
}

4.10 Sobrecarga de métodos


Java permite sobrecargar métodos. Puede haber más de un método con el mismo nombre,
siempre y cuando tengan distinta firma. El listado 4-17 la forma en que la clase Fecha
sobrecarga el constructor y el método cambiaYear.

37
Listado 4-17
public class Fecha {
int dia;
int mes;
int year;

// sobrecarga del constructor

Fecha (int d, int m, int y)


{
dia = d;
mes = m;
year = y;
}

Fecha (int d, int m)


{
this (d, m, 2002);
}

Fecha (int d)
{
this (d, 10, 2002);
}

void cambiaDia (int d)


{
dia = d;
}

void cambiaMes (int m)


{
mes = m;
}

// sobrecarga del metodo

void cambiaYear (int y)


{
year = y;
}

void cambiaYear ()
{
year = 2002;
}

38
Listado 4-17 Continuación
void pintaCorto ()
{
System.out.println (dia + "/" + mes + "/" + year);
}

public static void main (String args[])


{
Fecha f1 = new Fecha (20, 2, 2005);
System.out.print ("Fecha:"); f1.pintaCorto ();
f1.cambiaYear (2100);
System.out.print ("Fecha:"); f1.pintaCorto ();
f1.cambiaYear ();
System.out.print ("Fecha:"); f1.pintaCorto ();
}
}

La salida del programa es:

Fecha: 20/2/2005
Fecha: 20/2/2100
Fecha: 20/2/2002

4.11 Variables y métodos de clase


Hasta ahora hemos trabajado con variables y métodos de instancia. Como ya se dijo antes,
al crear instancias de una clase a cada objeto se le asigna memoria para almacenar su propia
copia de las variables de instancia. Puede cambiar su copia sin alterar las copias que tienen
los demás objetos. Sin embargo, a veces una variable de instancia tiene el mismo valor en
cada instancia de una clase. Por ejemplo, si representamos una familia mediante una clase y
necesitamos guardar el apellido familiar, lo más conveniente sería definir una variable de
clase. No tiene caso guardar el apellido en una variable de instancia si todos los miembros
de la familia tienen el mismo apellido. Lo único que hay que recordar es que si un objeto
cambia el valor de una variable de clase, el valor cambiará para los demás objetos de esa
clase. Las variables de clase también se les llama estáticas porque se declaran con la
palabra reservada static.

El listado 4.18 muestra un ejemplo del uso de una variable de clase. El objetivo es mostrar
como si un objeto cambia el valor de la variable de clase, esta cambia para todos los demás
objetos.

39
Listado 4-18
public class FamPerez {
String nombre;
static String apellido = "Perez";
int edad;

FamPerez (String n, int e)


{
nombre = n;
edad = e;
}

public static void main (String args[])


{
FamPerez papa, mama, hijo1, hijo2;
papa = new FamPerez ("Ramon", 40);
mama = new FamPerez ("Maria", 35);
hijo1 = new FamPerez ("Monchito", 10);
hijo2 = new FamPerez ("Lupita", 7);
System.out.println ("antes: nombre: " + papa.nombre + ", apellido: " +
papa.apellido + ", edad: " + papa.edad);
hijo1.apellido = "Lopez";
System.out.println ("despues: nombre: " + papa.nombre + ", apellido: " +
papa.apellido + ", edad: " + papa.edad);
}
}

La salida del programa es:

antes: nombre: Ramon, apellido: Perez, edad: 40


despues: nombre: Ramon, apellido: Lopez, edad: 40

Los métodos de clase están disponibles sin necesidad de crear una instancia de la clase.
Para invocarlos se usa la notación punto, pero en lugar del objeto se pone la clase. Por
ejemplo, la librería de Java tiene una clase Math que define un conjunto de operaciones
matemáticas que se pueden utilizar desde cualquier programa. Los métodos de la clase
Math son métodos de clase, están declarados con la palabra reservada static. Por esa razón,
si se desea calcular el coseno de un número basta con invocar así el método.

double y = Math.cos (76);

Los métodos de clase no pueden accesar variables de instancia, por lo tanto, una regla para
decidir si un método debe ser de instancia o de clase es analizar si queremos que el método
tenga acceso o no a las variables de instancia. Por lo general los métodos de utilería (como
el que se presenta en el listado 4-19) que no modifican el estado del objeto son los que se
programan como métodos de clase.

40
Listado 4-19
public class Conversion {

static double gra2rad (double grados)


{
return (Math.PI * grados) / 180;
}

public static void main (String args[])


{
System.out.println ("120 grados son " + Conversion.gra2rad (120) + " radianes");
}
}

4.12 Protección de variables y métodos


En el capítulo 2 aprendimos que una de las tareas fundamentales de un objeto es encapsular
sus datos para ocultarlos de la vista de los demás y limitar su manipulación. El
encapsulamiento separa el diseño de la implementación y minimiza la cantidad de
información que una clase necesita conocer de otra para poder hacer su trabajo. Además
reduce la cantidad de cambios que se necesitarán de hacer si la implementación interna
cambia.

Como regla general, es conveniente ocultar las variables de instancia crear métodos
especiales para accesar o modificar esas variables. Los métodos que dependan de la forma
en que está implementada una clase también deberán estar ocultos. Es cuestión de diseño el
decidir que métodos son parte de la interface que la clase presenta al mundo externo y
cuales no.

En Java es muy sencillo proteger variables y métodos. Lo único que hay que hacer es poner
en la declaración la palabra reservada private. Por ejemplo:

public class Foo {


private int foo;
....
private float calculaFoo ()
...

4.13 Herencia
En Java todas las clases están organizadas en una jerarquía. Cada clase en la jerarquía
(excepto la clase Object) tiene superclases (las clases que están arriba en la jerarquía) y
subclases (las clases que están abajo en la jerarquía). Las subclases heredan los atributos y
métodos de sus superclases.

41
Por ejemplo, un applet es una subclase de la clase java.applet.Applet. Por eso, al programar
un applet ponemos la línea:

public class fooApplet extends java.applet.Applet {

en la definición de la clase.

Es recomendable que en el constructor de la subclase se invoque al constructor de la


superclase. Esto se logra llamando al método super.

El listado 4.20 muestra la implementación de la clase Cuadrado que extiende a la clase


Rectángulo vista en el listado 4.9. La clase Cuadrado hereda el método área de Rectángulo
y por lo tanto no necesita programarlo. Además agrega un nuevo método.

Listado 4-20
public class Cuadrado extends Rectangulo {
private int lado;

Cuadrado (int lado)


{
super (lado, lado);
this.lado = lado;
}
public double diagonal ()
{
return Math.sqrt (lado * lado + lado * lado);
}

public static void main (String args[])


{
Cuadrado c = new Cuadrado (2);
System.out.println ("Area: " + c.area ());
System.out.println ("Diagonal: " + c.diagonal ());
}
}

La salida del programa es:

Area: 4
Diagonal: 2.828427

4.14 Sobreposición de métodos


Es el mecanismo por el cual la subclase redefine un método de la superclase. Si el método
programado en la subclase tiene la misma firma que el método de la superclase, entonces
este método queda anulado, y cada vez que se invoque al método desde una instancia de la
subclase, se invocará el método de la subclase y no el de la superclase.

42
Como ejemplo supongamos que estamos programando una nómina para una empresa donde
los empleados tienen un sueldo base y aquellos empleados que tienen personas a su cargo
se les da una prima en el salario. En el programa que se presenta en el listado 4-21 la clase
Empleado tiene un método getSueldo para calcular el salario del empleado. La clase Jefe
extiende a la clase Empleado porque un jefe es un caso especial de empleado y define
(sobrepone) su propio método getSueldo. Cuando creamos una instancia de la clase Jefe e
invocamos el método getSueldo, en realidad estamos invocando el método de la subclase
(en este caso Jefe). Si se necesita invocar el método de la superclase se puede hacer
poniéndole la palabra super.

Listado 4-21
public class Empleado {
private String nombre;
private int sueldo;

Empleado (String n, int s)


{
nombre = n;
sueldo = s;
}

public String getNombre ()


{
return nombre;
}

public int getSueldo ()


{
return sueldo;
}
}

class Jefe extends Empleado {


private int prima;

Jefe (String n, int s, int p)


{
super (n, s);
prima = p;
}

public int getSueldo ()


{
return super.getSueldo () + prima;
}

43
Listado 4-21 Continuación

public static void main (String args[])


{
Empleado juan = new Empleado ("Juan", 5000);
Jefe luis = new Jefe ("Luis", 5500, 1500);
System.out.println ("Nombre: " + juan.getNombre () + " Sueldo: " + juan.getSueldo ());
System.out.println ("Nombre: " + luis.getNombre () + " Sueldo: " + luis.getSueldo ());
}
}

La salida del programa es:

Nombre: Juan Sueldo: 5000


Nombre: Luis Sueldo: 7000

44
Capítulo 5 Applets
5.1 Introducción
Como vimos en la sección 1.4.1 un applet es un programa en Java que se incrusta en una
página HTML para agregarle funcionalidad.

Como por seguridad los applets están limitados a no poder accesar el medio ambiente de la
computadora donde corren su principal utilidad es para hacer interfaces gráficas de
aplicaciones de bases de datos. Por supuesto la base de datos debe residir en la
computadora de donde vino el applet. También se pueden programar juegos, programas
interactivos que complementen un texto en línea o que ilustren un concepto en particular,
simulaciones, etc.

5.2 Ciclo de vida


Los applets tienen un ciclo de vida durante el cual realizan algunas actividades. Cada
actividad tiene asignado un método que el navegador llama en el momento adecuado.

Las principales actividades son:

• Inicio. Se hace cuando se carga el applet por primera vez y corresponde al método
init().
• Comienzo. Se hace cuando el applet comienza a correr, ya sea después del inicio o si
fué detenido. Corresponde al método start().
• Detención. Un applet se detiene cuando el usuario accesa otra página en su navegador.
Corresponde al método stop().
• Destrucción. Permite liberar recursos al finalizar el applet. Corresponde al método
destroy().
• Dibujo. Actualiza la salida del applet en la pantalla. Corresponde al método
paint(Graphics g). Se invoca cada vez que se necesita que el applet sea redibujado, ya
sea al saltar al primer plano, al moverse la pantalla o cuando vuelve a correr después de
estar detenido.

Es importante recalcar que ninguno de estos métodos es invocado en forma explícita. El


navegador los invoca en forma automática dependiendo de si el applet necesita inicializarse
o si está comenzando o si necesita volver a pintar lo que el applet haya dibujado.

Un applet debe extender a la clase Applet. Esta clase proporciona implementaciones vacías
de los 5 métodos. Es responsabilidad del programador de applets sobreponer los métodos
que vaya a utilizar. No es necesario sobreponer todos, en la práctica casi siempre solo se
sobreponen o el método init() o el paint(), rara vez los dos a la vez.

45
El listado 5-1 muestra el código de un applet sencillo que solo sobrepone el método paint().
El listado 5-2 tiene el código de la página HTML que invoca al applet y la figura 5-1
muestra la salida que se genera al cargar la página HTML en un navegador.

Listado 5-1
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import java.applet.*;

public class HolaAgain extends Applet {


Font f = new Font ("TimesRoman", Font.BOLD, 36);

public void paint (Graphics g) {


g.setFont (f);
g.setColor (Color.red);
g.drawString ("Hola mundo !!!", 5, 40);
}
}

Listado 5-2
<HTML>
<HEAD>
<TITLE>Hola Mundo !!!</TITLE>
</HEAD>
<BODY>
<P><H2>Saludos desde Sonora:</H2><P>
<APPLET CODE="HolaAgain.class" WIDTH=350 HEIGHT=325 ALIGN="Left">
</APPLET>
</BODY>
</HTML>

Como se puede ver en el listado 5-2 la etiqueta <APPLET> es la que incrusta el applet en la
página HTML. Esta etiqueta puede tener varios argumentos, los principales son:

• CODE – Indica el nombre del archivo compilado. Si el archivo de clase está en la


misma carpeta que la página HTML (lo más recomendable) basta con poner el nombre,
pero se pueden ejecutar applets remotos poniendo la dirección http completa.
• WIDTH – Indica el ancho en pixeles del área dentro de la página HTML que el
navegador apartará para uso del applet.
• HEIGHT – Es la altura de dicha área en pixeles.
• ALIGN – Permite alinear el applet a la izquierda (Left), al centro (Center) o a la
derecha (Right).

46
Figura 5-1

5.3 Argumentos
Los applets pueden recibir argumentos desde el archivo HTML con la etiqueta <PARAM>,
la cual tiene un atributo para el nombre del argumento y otro para el valor. La etiqueta
<PARAM> va dentro de la etiqueta <APPLET>.

Los argumentos se pasan cuando el applet se inicializa. Dentro del método init() se puede
recuperar los valores de los argumentos usando el método getParameter(). Este método
recibe como argumento un string con el nombre del argumento y devuelve un string con el
valor correspondiente a ese argumento o el valor null si el argumento no existe.

El listado 5-3 muestra el código de un applet que lee tres argumentos y escribe un mensaje
en la pantalla. El listado 5-4 presenta la página HTML que carga al applet y le pasa los
valores de los tres argumentos y la figura 5-2 la salida cuando se carga la página HTML.

Listado 5-3
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import java.applet.*;

public class HolaAgainP extends Applet {


Font f = new Font ("TimesRoman", Font.BOLD, 36);
String desde, para, mssg;

47
Listado 5-3 Continuación
public void init ()
{
desde = getParameter ("desde");
if (desde == null)
desde = "aqui";
para = getParameter ("para");
if (para == null)
para = "alla";
mssg = getParameter ("mensaje");
if (mssg == null)
mssg = "no hay mensaje";
}

public void paint (Graphics g)


{
g.setFont (f);
g.setColor (Color.red);
g.drawString ("Desde " + desde + " para " + para, 5, 40);
g.setColor (Color.green);
g.drawString ("El mensaje es: " + mssg, 5, 80);
}
}

Listado 5-4
<HTML>
<HEAD>
<TITLE>Hola Mundo !!!</TITLE>
</HEAD>
<BODY>
<P><H2>Saludos desde Sonora:</H2><P>
<APPLET CODE="HolaAgainP.class" WIDTH=600 HEIGHT=400>
<PARAM NAME=desde VALUE="Hermosillo">
<PARAM NAME=para VALUE="el mundo">
<PARAM NAME=mensaje VALUE="Java Rules !!!">
</APPLET>
</BODY>
</HTML>

48
Figura 5-2

5.4 Gráficas
La librería de Java incluye la clase Graphics para hacer dibujos sencillos. Esta clase tiene
métodos para dibujar líneas, rectángulos, círculos, polígonos y arcos.

En los applets el método paint recibe un objeto de tipo Graphics. Al dibujar sobre este
objeto se dibuja dentro del applet y los resultados se desplegarán en la pantalla.

El sistema de coordenadas está en pixeles. El origen, es decir, el punto (0,0) está en la


esquina superior izquierda del área reservada para el applet en la página HTML. La X crece
hacia la derecha en forma horizontal y la Y crece hacia abajo en forma vertical.

La clase Graphics tiene los siguientes métodos. Hay que aclarar que todos los argumentos
son de tipo int.

• drawString (s, x, y) – Escribe el texto almacenado en el string s. El punto (x, y) indica la


coordenada donde comienza el texto.
• drawLine (x1, y1, x2, y2) – Dibuja una línea recta entre los puntos (x1, y1) y (x2, y2).
• drawRect (x, y, ancho, alto) – Dibuja un rectángulo hueco. La esquina superior
izquierda está dada por el punto (x, y) y la anchura y altura por ancho y alto
respectivamente.
• fillRect (x, y, ancho, alto) - Dibuja un rectángulo lleno con color actual (en la sección 5-
6 veremos como cambiar el color actual). Los argumentos son los mismos que en el
método anterior

49
• drawRoundRect (x, y, ancho, alto, r_hor, r_ver) – Dibuja un rectángulo hueco con las
esquinas redondeadas. Los primeros 4 argumentos tienen el mismo sentido que en los
dos métodos anteriores. Los argumentos r_hor y r_ver determinan que tan lejos de los
limites del rectángulo debe comenzar el redondeo en las esquinas.
• fillRoundRect (x, y, ancho, alto, r_hor, r_ver) – Dibuja un rectángulo lleno con el color
actual y redondeado en las esquinas. Los argumentos son los mismos que en el método
anterior.
• drawPolygon (x[], y[], n) – Dibuja un polígono hueco. Los arreglos x y y guardan
respectivamente las coordenadas x y y de cada una de las esquinas del polígono. El
argumento n es el número de puntos (el tamaño de los arreglos x y y). El polígono se
cierra en forma automática porque siempre se dibuja una línea entre el último punto y el
primero.
• fillPolygon (x[], y[], n) – Dibuja un polígono lleno con el color actual. Los argumentos
son los mismos que en el método anterior.
• drawPolyline (x[], y[], n) – Dibuja una línea quebrada. El resultado se puede pensar que
es un polígono sin cerrar.
• drawOval (x, y, ancho, alto) – Dibuja un óvalo hueco. La forma más fácil de entender el
resultado de este método es imaginar que se dibuja un rectángulo imaginario con una
anchura de ancho pixeles y una altura de alto pixeles, con la esquina superior izquierda
en el punto (x, y) y que el óvalo se dibuja inscrito en el rectángulo. Por lo tanto, el
punto (x, y) está fuera del óvalo. Si ancho es igual que alto, el rectángulo se convierte
en cuadrado y el óvalo se convierte en círculo.
• fillOval (x, y, ancho, alto) Dibuja un óvalo lleno con el color actual. Los argumentos
son los mismos que en el método anterior.
• drawArc (x, y, ancho, alto, grados_ini, largo_grados) – Dibuja un arco hueco. Los
primeros 4 argumentos tienen el mismo sentido que en los dos métodos anteriores. El
argumento grados_ini indica desde que grado se comienza a dibujar el arco. Si
pensamos en los puntos cardinales el grado 0 está al este, el 90 al norte, el 180 al oeste
y el 270 al sur. Por último largo_grados indica la longitud del arco y la dirección de
dibujo. Un valor de 90 significa que se dibujará un cuarto de arco en contra de las
manecillas del reloj, en cambio un valor de –90 también indica un cuarto de arco pero
en el sentido de las manecillas del reloj. Igualmente un valor de 180 significa medio
arco y dependiendo del signo es la dirección.
• fillArc (x, y, base, altura, grados_ini, largo_grados) – Dibuja un arco lleno con el color
actual. Los argumentos son los mismos que en el método anterior.

El listado 5-5 muestra un applet que realiza un dibujo utilizando los métodos de la clase
Graphics. La figura 5-3 presenta la salida del applet.

Listado 5-5
import java.awt.Graphics;
import java.applet.*;

public class Lampara extends Applet {


public void paint (Graphics g) {

50
Listado 5-5 Continuación
// la plataforma
g.fillRect (0, 250, 290, 290);
// La base
g.drawLine (125, 250, 125, 160);
g.drawLine (175, 250, 175, 160);
// La cubierta
g.drawArc (85, 157, 130, 50, -65, 312);
g.drawArc (85, 87, 130, 50, 62, 58);
g.drawLine (85, 177, 119, 89);
g.drawLine (215, 177, 181, 89);
// Las motas
g.fillArc (78, 120, 40, 40, 63, -174);
g.fillOval (120, 96, 40, 40);
g.fillArc (173, 100, 40, 40, 110, 180);
}
}

Figura 5-3

51
5.5 Tipo de letra
Para cambiar el tipo de letra hay que crear una instancia de la clase Font.

Font f = new Font ("TimesRoman", Font.BOLD, 24);

El primer argumento es el nombre del tipo de letra. Los tipos de letra disponibles se pueden
saber usando esta instrucción:

String fontlist[] =
GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames ();

o esta para versiones atrasadas de Netscape y Explorer que usan el JDK 1.1

String fontlist[] = this.getToolkit ().getFontList ();

El segundo argumento es el estilo, puede valer Font.PLAIN (estilo normal), Font.BOLD


(estilo en negritas) o Font.ITALIC (estilo en cursiva o itálico). Se pueden mezclar dos o
más estilos con un el signo +, por ejemplo se puede indicar Font.BOLD + Font.ITALIC.

El tercer argumento es el tamaño en puntos del tipo de letra y depende de cada tipo de letra.

Para cambiar el tipo de letra se invoca al método:

setFont (f)

donde f es un objeto de tipo Font.

El listado 5-6 muestra un ejemplo del uso de los tipos de letras y la figura 5-4 la salida del
applet.

Listado 5-6
import java.awt.Font;
import java.awt.Graphics;
import java.applet.*;

public class ManyFonts extends Applet {


public void paint (Graphics g)
{
Font f1 = new Font ("Times Roman", Font.PLAIN, 18);
Font f2 = new Font ("Times Roman", Font.BOLD, 18);
Font f3 = new Font ("Times Roman", Font.ITALIC, 18);
Font f4 = new Font ("Times Roman", Font.BOLD + Font.ITALIC, 18);
g.setFont (f1);
g.drawString ("Este es texto normal", 10, 25);

52
Listado 5-6 Continuación
g.setFont (f2);
g.drawString ("Este es texto en negrita", 10, 50);
g.setFont (f3);
g.drawString ("Este es texto en cursiva", 10, 75);
g.setFont (f4);
g.drawString ("Este es texto en negrita y cursiva", 10, 100);
}
}

Figura 5-4

5.6 Color
Para cambiar el color hay que crear una instancia de la clase Color:

Color c = new Color (r, g, b);

donde r, g y b es la cantidad de rojo, verde y azul respectivamente, en el rango de 0 a 255.


El (0,0,0) representa el negro y el (255, 255, 255) el blanco. En total hay 2563 = 16,777,216
colores distintos.

Se puede usar alguno de los colores ya definidos en la clase Color como Color.white,
Color.red, Color.black, Color.orange, Color.blue, Color.gray, Color.yellow, etc.

53
Hay 3 funciones para cambio de color:

• setColor (c). Cambia el color actual para las operaciones de dibujo a partir de este
momento. El argumento c es un objeto de tipo Color.
• setBackground (c). Cambia el color del fondo al color c.
• setForeground (c). Cambia el color de lo que se haya dibujado sin importar el color en
el que se dibujó.

El listado 5-3 muestra la forma de cambiar el color al texto usando el método setColor. El
listado 5-7 presenta un ejemplo donde se usa el método setBackground para cambiar el
color del fondo y setColor para dibujar figuras de distinto color. La figura 5-5 muestra la
salida del applet.

Listado 5-7
import java.awt.Graphics;
import java.applet.*;
import java.awt.Color;

public class pruebaColor extends Applet {

public void paint (Graphics g) {


Color c1 = new Color (120, 30, 220);
Color c2 = new Color (40, 240, 200);
setBackground (Color.lightGray);
g.setColor (c1);
g.drawString ("Ovalo hueco", 5, 20);
g.drawOval (5, 30, 100, 60);
g.drawString ("Circulo hueco", 150, 20);
g.drawOval (150, 30, 60, 60);
g.setColor (c2);
g.drawString ("Ovalo lleno", 5, 120);
g.fillOval (5, 130, 100, 60);
g.setColor (Color.red);
g.drawString ("Circulo lleno", 150, 120);
g.fillOval (150, 130, 60, 60);
}
}

54
Figura 5-5

5.7 Imágenes
Un applet pueden desplegar imágenes en formato GIF y JPEG.

Lo primero es crear una instancia de la clase Image.

Image mi_imagen = getImage (URL, nombre);

donde URL es la dirección donde está la imagen y nombre es el nombre del archivo que
contiene la imagen. Para la variable URL hay 3 opciones:

a) Poner la dirección absoluta. Si la imagen esta en: http://www.foo.com/images/foo.gif,


entonces poner:

Image imagen = getImage (new URL ("http://www.foo.com/images", "foo.gif");

b) Usar la dirección relativa con respecto a donde está la página HTML. Si el directorio
images está abajo de donde está la página, entonces poner:

Image imagen = getImage (getDocumentBase (), "images/foo.gif");

55
c) Usar la dirección relativa a donde está el applet compilado (archivo .class). Si el
directorio images está abajo de donde está el código objeto, entonces poner:

Image imagen = getImage (getCodeBase (), "images/foo.gif");

Para desplegar la imagen:

drawImage (imagen, x, y, ancho, alto, donde);

donde imagen es un objeto de tipo Image, x y y son las coordenadas de la esquina superior
izquierda de la imagen. Las variables ancho y alto indica el ancho y el alto en pixeles con
que se va a desplegar la imagen. Por último, la variable donde es el nombre del objeto que
va a actuar como observador de la imagen que en el caso de los applets es el mismo applet.

El listado 5-8 presenta un applet que despliega una imagen en la pantalla, los métodos
getWidth y getHeight regresan el ancho y el alto con los que se generó la imagen
originalmente. Luego dibuja la imagen con un tamaño de 25%, 50%, 100% y 150% con
respecto al tamaño original. Por último, distorsiona la imagen haciendo que tenga la mitad
del ancho original y una vez y media la altura original. La figura 5-6 muestra la salida de
este applet.

Listado 5-8
import java.awt.Graphics;
import java.applet.*;
import java.awt.Image;

public class Bart extends Applet {


Image bart;

public void init ()


{
bart = getImage (getCodeBase (), "bart_id.gif");
}

public void paint (Graphics g)


{
int ancho = bart.getWidth (this);
int alto = bart.getHeight (this);
int xpos = 10, ypos = 10;
// 25 %
g.drawImage (bart, xpos, ypos, ancho / 4, alto / 4, this);
// 50 %
xpos += (ancho / 4) + 10;
g.drawImage (bart, xpos, ypos, ancho / 2, alto / 2, this);

56
Listado 5-8 Continuación
// 100 %
xpos += (ancho / 2) + 10;
g.drawImage (bart, xpos, ypos, this);
// 150 %
xpos += ancho + 10;
g.drawImage (bart, xpos, ypos, (int) (ancho * 1.5), (int) (alto * 1.5), this);
// distorsionado
xpos += (int) (ancho * 1.5 + 10);
g.drawImage (bart, xpos, ypos, ancho / 2, (int) (alto * 1.5), this);
}
}

Figura 5-6

57
58
Capítulo 6 AWT
6.1 Introducción
El AWT (Abstract Windowing Toolkit, herramienta abstracta de ventanas), proporciona
componentes para desarrollar interfaces gráficas, incluyendo etiquetas, botones, menús,
checkboxes, campos de texto, choices, etc. Tiene contenedores como ventanas, applets,
paneles, lienzos y diálogos que pueden contener a otros contenedores o a otros
componentes. Puede manejar eventos de ratón y hacer que un componente responda a un
click. La ventaja de AWT es que es independiente del sistema operativo y asegura que la
interface gráfica se verá igual en cualquier computadora que tenga instalado el JDK.

La figura 6-1 muestra un applet que utiliza AWT para programar su interface gráfica. Se
pueden ver botones, áreas de texto y etiquetas.

Figura 6-1

De entrada los applets tienen disponible una ventana, la del navegador, para poder agregar
los componentes que utilicen, en cambio las aplicaciones necesitan crear una ventana base
antes de comenzar a usar los componentes. Fuera de esto, y de las restricciones naturales de

59
los applets, la programación de interfaces gráficas de usuario usando AWT es igual en
applets que en aplicaciones.

Cada componente corresponde a una clase. Para utilizar un componente primero hay que
crear un objeto de ese tipo y luego agregarlo al contenedor usando el método add.

6.2 Componentes básicos

6.2.1 Etiquetas

Una etiqueta es un texto no editable que despliega un letrero en la pantalla. Para definir una
etiqueta hay que crear un objeto de tipo Label indicando el texto de la etiqueta y
opcionalmente una alineación:

Label etiqueta1 = new Label ("Hola");


Label etiqueta2 = new Label ("Hola", Label.CENTER);

Si en algún momento se desea cambiar el texto de la etiqueta se puede hacer con el método
setText.

etiqueta1.setText ("Ahora valgo esto");

El listado 6-1 presenta un ejemplo de un applet que despliega 2 etiquetas. La figura 6-2
muestra la salida del programa.

Listado 6-1
import java.awt.*;
import java.applet.*;

public class lbl extends Applet {


Label lbl = new Label ("Soy una etiqueta");
Label lbl2 = new Label ("Soy otra etiqueta", Label.RIGHT);

public void init ()


{
add (lbl);
add (lbl2);
}
}

60
Figura 6-2

6.2.2 Botones

Un botón es un componente gráfico que activa algún evento cuando se le oprime usando el
ratón. Para definir un botón hay que crear un objeto de tipo Button indicando el texto que
desplegará el botón:

Button btn = new Button ("Continuar");

Luego habría que indicarle la acción que realizará al ser oprimido pero eso se verá más
adelante en la sección 6.4.

Si en algún momento se desea cambiar el texto del botón se puede invocar al método
setLabel.

btn.setLabel ("No continúo");

El listado 6-2 presenta un applet que despliega dos botones. La figura 6-3 muestra la salida
de este applet.

61
Listado 6-2
import java.awt.*;
import java.applet.*;

public class btn extends Applet {


Button btn1 = new Button ("Soy un botón");
Button btn2 = new Button ("Soy otro botón");

public void init ()


{
add (btn1);
add (btn2);
}
}

Figura 6-3

6.2.3 Checkboxes

Un checkbox es un componente que tiene dos estados: marcado o no marcado. Los


checkboxes se pueden agrupar de tal forma que solo un checkbox de ese grupo pueda estar
activado a la vez.

Para definir un checkbox hay que crear un objeto de tipo Checkbox indicando un texto
como etiqueta y opcionalmente un booleano para indicar si de entrada está marcada o no:

Checkbox cb1 = new Checkbox ("Fútbol");


Checkbox cb2 = new Checkbox ("Hockey", true); // marcado de inicio

62
Si se desea agrupar varios checkboxes, primero hay que crear un objeto de tipo
CheckboxGroup y luego al momento de crear los checkboxes se indica que pertenecen al
mismo grupo.

CheckboxGroup cbg = new CheckboxGroup ();


Checkbox cb1 = new Checkbox ("Fútbol", true, cbg);
Checkbox cb2 = new Checkbox ("Hockey", false, cbg);
Checkbox cb3 = new Checkbox ("Béisbol", false, cbg);

Para saber si un checkbox está marcado o no se utiliza el método getState.

if (cb2.getState ())
...

Para marcar (o desmarcar) desde un programa un checkbox se usa el método setState


pasando un argumento booleano que indique el nuevo estado.

cb1.setState (false); // desmarca el checkbox cb1

El listado 6-3 presenta un applet que despliega 5 checkboxes no agrupados y por lo tanto
más de uno puede estar activado. La figura 6-4 muestra la salida. En cambio el applet del
listado 6-4 declara 2 grupos de checkboxes. En cada grupo solo uno de ellos puede estar
activado. La figura 6-5 muestra la salida de este último applet.

Listado 6-3
import java.awt.*;
import java.applet.*;

public class cb extends Applet {


Checkbox cb1 = new Checkbox ("opcion 1");
Checkbox cb2 = new Checkbox ("opcion 2");
Checkbox cb3 = new Checkbox ("opcion 3");
Checkbox cb4 = new Checkbox ("opcion 4", true);
Checkbox cb5 = new Checkbox ("opcion 5");

public void init ()


{
add (cb1);
add (cb2);
add (cb3);
add (cb4);
add (cb5);
}
}

63
Figura 6-4

Listado 6-4
import java.awt.*;
import java.applet.*;

public class cbg extends Applet {


Label lbl_c = new Label ("Estado civil");
Label lbl_n = new Label ("Nacionalidad");
CheckboxGroup cbg1 = new CheckboxGroup ();
CheckboxGroup cbg2 = new CheckboxGroup ();
Checkbox cb_s = new Checkbox ("soltero", false, cbg1);
Checkbox cb_c = new Checkbox ("casado", true, cbg1);
Checkbox cb_v = new Checkbox ("viudo", false, cbg1);
Checkbox cb_m = new Checkbox ("mexicano", true, cbg2);
Checkbox cb_e = new Checkbox ("extranjero", false, cbg2);
public void init ()
{
add (lbl_c);
add (cb_s);
add (cb_c);
add (cb_v);
add (lbl_n);
add (cb_m);
add (cb_e);
}
}

64
Figura 6-5

6.2.4 Choices

Un choice es un menú del cual se puede escoger solo una opción. Las listas (que se verán
sección 6.5.4 permiten seleccionar más de una opción). Para usar un choice hay que
declarar un objeto de tipo Choice y luego usar el método add para agregar la lista de
opciones.

Choice ch_deportes = new Choice ();


ch_deportes.add ("Fútbol");
ch_deportes.add ("Béisbol");
ch_deportes.add ("Hockey");
ch_deportes.add ("Fútbol Americano");

Para saber que opción fue seleccionada se usa el método getSelectedIndex que regresa un
número entre 0 y n – 1, donde n es el número de elementos del choice, indicando el índice
del elemento seleccionado.

if (ch_deportes.getSelectedIndex () == 0)
...

Si se desea seleccionar una opción desde un programa se utiliza el método select pasando
como argumento el índice de la opción que se desea quede seleccionada.

ch_deportes.select (2);

65
El listado 6-5 presenta un applet que despliega un choice de 5 opciones. La figura 6-6
muestra la salida.

Listado 6-5
import java.awt.*;
import java.applet.*;

public class chs extends Applet {


Choice ch_deportes = new Choice ();

public void init ()


{
ch_deportes.add ("Fútbol");
ch_deportes.add ("Beísbol");
ch_deportes.add ("Hockey");
ch_deportes.add ("Fútbol Americano");
ch_deportes.add ("Lacrosse");
add (ch_deportes);
}
}

Figura 6-6

66
6.2.5 Campos de texto

Un campo de texto es un componente que permite capturar y editar una línea de texto. Para
usar un campo de texto hay que crear un objeto de tipo TextField indicando el número de
caracteres de ancho y opcionalmente un texto inicial.

TextField tf1 = new TextField (20); // tamaño aproximado del campo


TextField tf2 = new TextField ("Pon tu nombre");
TextField tf3 = new TextField ("Escribe algo", 10);

Se puede hacer que un campo de texto oculte el texto mientras se escribe en él usando el
método setEchoChar, pasando como argumento el carácter que se desea desplegar en lugar
del texto que se escribe.

tf2.setEchoChar ('*'); // desplegará asteriscos

Para obtener el texto que se escribió en un campo de texto se usa el método getText.

String texto = tf1.getText ();

Para cambiar el texto desde el programa se usa el método setText indicando en el


argumento el nuevo texto.

tf3.setText ("Ahora despliego esto");

El listado 6-6 presenta un applet con 3 campos de texto. El tercero oculta con asteriscos los
caracteres que se le escriban. La figura 6-7 muestra la salida del programa.

Listado 6-6
import java.awt.*;
import java.applet.*;

public class tf extends Applet {


TextField tf1 = new TextField (10);
TextField tf2 = new TextField ("Nombre de la ciudad",30);
TextField tf3 = new TextField (15);

public void init ()


{
add (tf1);
add (tf2);
tf3.setEchoChar ('*');
add (tf3);
}
}

67
Figura 6-7

6.3 Diseños

6.3.1 Definición

Los diseños determinan la forma en que los componentes se distribuyen en la pantalla.


Cada elemento contenedor (ventanas, diálogos, paneles, lienzos, applet) puede (y debe)
tener su propio diseño. Solo puede haber un diseño activo a la vez por contenedor.

AWT tiene cuatro diseños básicos. Cada diseño corresponde a una clase. Para usar un
diseño primero hay que instanciar una de las clases y luego invocar el método setLayout
con pasando como argumento el objeto del tipo de diseño.

6.3.2 Diseño de flujo

El diseño de flujo distribuye los componentes de izquierda a derecha por renglones. Los
renglones se pueden alinear a la izquierda, al centro o a la derecha.

Para usar un diseño de flujo es necesario crear un objeto de la clase FlowLayout.


Opcionalmente se le puede indicar una alineación y espacios verticales y horizontales entre
componentes.

FlowLayout fl1 = new FlowLayout (); // alineado al centro por default


FlowLayout fl2 = new FlowLayout (FlowLayout.CENTER);
FlowLayout fl3 = new FlowLayout (FlowLayout.LEFT, 30, 10);

68
El listado 6-7 se presenta un applet que agrega 6 botones de acuerdo a un diseño de flujo
centrado y con un espacio entre componentes de 30 pixeles en forma horizontal y de 10
verticales. La figura 6-8 muestra la salida del applet, es importante notar como los botones
se colocan por líneas en el área reservada para el applet mientras quepan. Si el tamaño de la
ventana cambia, los botones se acomodan al nuevo tamaño y puede que ocupen más o
menos renglones.

Listado 6-7
import java.awt.*;
import java.applet.*;

public class fl extends Applet {


FlowLayout fl = new FlowLayout (FlowLayout.CENTER, 30, 10);
Button btn1 = new Button ("Uno");
Button btn2 = new Button ("Dos");
Button btn3 = new Button ("Tres");
Button btn4 = new Button ("Cuatro");
Button btn5 = new Button ("Cinco");
Button btn6 = new Button ("Seis");

public void init ()


{
setBackground (Color.lightGray);
setLayout (fl);
add (btn1);
add (btn2);
add (btn3);
add (btn4);
add (btn5);
add (btn6);
}
}

69
Figura 6-8

6.3.3 Diseño de parrilla

El diseño de parrilla divide el contenedor en una cuadrícula de m renglones y n columnas.


En cada celda cabe un componente. Los componentes se van agregando por renglones de
izquierda a derecha.

Para usar un diseño de parrilla es necesario crear un objeto de la clase GridLayout


indicando el número de renglones y de columnas. En forma opcional se puede indicar el
espacio en pixeles entre componentes.

GridLayout gl1 = new GridLayout (4, 2); // 4 renglones 2 columnas


GridLayout gl2 = new GridLayout (4, 2, 30, 10);

El listado 6-8 presenta un applet que especifica una parrilla de 4 columnas y 2 renglones,
con un espacio entre componentes de 40 pixeles en forma horizontal y 30 en forma vertical.
La figura 6-9 muestra la salida del programa.

Listado 6-8
import java.awt.*;
import java.applet.*;

public class gl2 extends Applet {


GridLayout gl = new GridLayout (4, 2, 40, 30);
Label lbl_nombre = new Label ("Introduzca su nombre:");

70
Figura 6-8 Continuación
Label lbl_telefono = new Label ("Introduzca su telefono:");
Label lbl_passwd = new Label ("Introduzca su password:");
TextField tf_nombre = new TextField (20);
TextField tf_telefono = new TextField (10);
TextField tf_passwd = new TextField (20);
Button btn_continuar = new Button ("Continuar");
Button btn_cancelar = new Button ("Cancelar");

public void init ()


{
setBackground (Color.lightGray);
setLayout (gl);
tf_passwd.setEchoChar ('*');
add (lbl_nombre); add (tf_nombre);
add (lbl_telefono); add (tf_telefono);
add (lbl_passwd); add (tf_passwd);
add (btn_continuar); add (btn_cancelar);
}
}

Figura 6-9

71
6.3.4 Diseño de borde

El diseño de borde divide el contenedor en 5 zonas, norte, sur, centro, oeste y este (ver
figura 6-10). En cada zona cabe un componente.

Figura 6-10

Para usar un diseño de borde hay que crear un objeto de tipo BorderLayout especificando
opcionalmente el espacio horizontal y vertical entre pixeles

BorderLayout bl1 = new BorderLayout ();


BorderLayout bl2 = new BorderLayout (20, 40);

Al momento de agregar el componente se agrega un argumento extra al método add para


indicar la zona en donde se colocará el componente.

add ("North", componente); // coloca el componente al norte

El listado 6-9 presenta el applet que generó la pantalla que se ilustra en la figura 6-10.

72
Listado 6-9
import java.awt.*;
import java.applet.*;

public class bl extends Applet {


BorderLayout bl = new BorderLayout ();
Button btn1 = new Button ("Norte");
Button btn2 = new Button ("Centro");
Button btn3 = new Button ("Sur");
Button btn4 = new Button ("Oeste");
Button btn5 = new Button ("Este");

public void init ()


{
setLayout (bl);
add ("North", btn1);
add ("Center", btn2);
add ("South", btn3);
add ("West", btn4);
add ("East", btn5);
}
}

6.3.5 Diseño de parrilla global

Al igual que el diseño de parrilla normal, el diseño de parrilla global divide el contenedor
en una cuadrícula de m renglones y n columnas, y en cada celda cabe un componente. Las
diferencias son que una celda puede ocupar más de un renglón o más de una columna y que
los componentes no se agregan por renglones sino que hay que indicar la columna y el
renglón de la celda donde va a colocarse el componente.

Para usar un diseño de parrilla global se usan dos clases: la clase GridBagLayout para el
diseño en general y la clase GridBagConstraints para definir las restricciones de cada
componente.

GridBagLayout gbl = new GridBagLayout ();


GridBagConstraints gbc = new GridBagConstraints ();

Para cada componente que se vaya a agregar primero se definen sus restricciones, luego se
le avisa al diseño cuáles son esas restricciones y por último se agrega el componente.

La clase GridBagConstraints define 10 variables para especificar las restricciones:

• gridx – Indica la columna de la celda donde el componente va a colocarse.


• gridy – Indica el renglón de la celda donde el componente va a colocarse.
• gridwidth – Indica el número de columnas que ocupa la celda.

73
• gridheight – Indica el número de renglones que ocupa la celda.
• weightx – Indica el tamaño horizontal relativo de la celda con respecto a la suma de
todos los tamaños horizontales de las celdas en el renglón. Esta restricción sólo tiene
sentido para las celdas en el primer renglón.
• weighty – Indica el tamaño vertical relativo de la celda con respecto a la suma de todos
los tamaños verticales de las celdas en el renglón. Esta restricción sólo tiene sentido
para las celdas en la primera columna.
• fill – Indica hacia donde va a extenderse el componente si el componente se hace más
grande. Puede valer NONE (no se extiende), VERTICAL, HORIZONTAL o BOTH
(vertical y horizontal).
• anchor – Indica hacia donde, dentro de la celda, se coloca el componente. Puede valer
NORTH (se pega hacia arriba, pero dentro de la celda), SOUTH (abajo), WEST
(izquierda), EAST (derecha), NORTHWEST (esquina superior izquierda),
NORTHEAST (esquina superior derecha), SOUTHWEST (esquina inferior izquierda) o
SOUTHEAST (esquina inferior derecha).
• ipadx – Indica el espacio extra horizontal dentro de la celda.
• ipady – Indica el espacio extra vertical dentro de la celda.

Una vez que se especifica cada restricción se le avisa al diseño con el método
setConstraints pasando como parámetro el componente y el objeto de tipo
GridBagConstraints.

gbl.setConstraints (componente, gbc);

Por último se agrega el componente con el método add.

add (componente);

El listado 6-10 presenta un applet que utiliza el diseño de parrilla global para colocar los
componentes en 3 renglones y 2 columnas. El componente del tercer renglón ocupa las dos
columnas porque su restricción gridwidth vale 2. La figura 6-11 muestra la salida.

Listado 6-10
import java.awt.*;
import java.applet.*;

public class gbl extends Applet {


GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
Label lbl_nombre = new Label ("Introduzca su nombre:");
Label lbl_telefono = new Label ("Introduzca su telefono:");
Label lbl_passwd = new Label ("Introduzca su password:");
TextField tf_nombre = new TextField (20);
TextField tf_telefono = new TextField (10);
TextField tf_passwd = new TextField (20);

74
Listado 6-10 Continuación
Button btn_continuar = new Button ("Continuar");

public void init ()


{
setLayout (gbl);

// Etiqueta de nombre
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (lbl_nombre, gbc);
add (lbl_nombre);

// Textfield de nombre
gbc.gridx = 1; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_nombre, gbc);
add (tf_nombre);

// Etiqueta de telefono
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (lbl_telefono, gbc);
add (lbl_telefono);

// Textfield de telefono
gbc.gridx = 1; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_telefono, gbc);
add (tf_telefono);
// Etiqueta de password
gbc.gridx = 0; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;

75
Listado 6-10 Continuación
gbc.weightx = 0; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (lbl_passwd, gbc);
add (lbl_passwd);

// Textfield de password
gbc.gridx = 1; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_passwd, gbc);
tf_passwd.setEchoChar ('*');
add (tf_passwd);

// Boton de continuar
gbc.gridx = 0; gbc.gridy = 3;
gbc.gridwidth = 2; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 10;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (btn_continuar, gbc);
add (btn_continuar);
}
}

76
Figura 6-11

6.4 Eventos
6.4.1 Introducción

Un evento es una acción que se genera al cambiar el estado de un componente de AWT.


Los eventos es la forma que tiene un programa para enterarse de que ocurrió algo y
reaccionar conforme al suceso. Un evento puede ser una entrada de usuario (movimientos o
clicks del ratón, pulsaciones de teclas), cambios en el medio ambiente del sistema (abrir,
cerrar o mover una ventana) o cualquier otra actividad que pudiera afectar la operación del
programa.

Java soporta varios tipos de eventos. Cada vez que ocurre alguna acción de las que se listan
a continuación se genera un evento que puede ser atrapado por un programa:

• Eventos de ratón. El ratón esta sobre un componente. El ratón salió de un componente.


El botón del ratón está oprimido o fue liberado. El ratón se movió. El ratón está siendo
arrastrado.
• Eventos de teclado. La tecla está oprimida o fue liberada.
• Eventos de botón. El botón fue oprimido.
• Eventos de choice. Alguna de las opciones del choice fue seleccionada.
• Eventos de checkbox. El checkbox fue seleccionado.

77
• Eventos de campo de texto. Se está escribiendo sobre el campo de texto. Se dio un enter
sobre el campo de texto.
• Eventos de foco. El componente obtuvo o perdió el foco. El foco se obtiene cuando se
da un click al ratón sobre un componente.

Para que un programa atienda un evento se necesita asignar al componente un oidor o


escuchador (listener en la terminología de Java). Dentro del oidor se escribe el código que
se desea ejecutar cuando ocurra el evento.

Hay dos estrategias para definir oidores: la primera es usar una clase que tenga un solo
método por cada tipo de evento que atienda todos los eventos que se generen de ese tipo. La
segunda estrategia consiste en declarar oidores anónimos, uno por cada componente que
pueda generar el evento en particular. La ventaja de la primera estrategia es que el manejo
de eventos se centraliza en un solo método. La desventaja es que ese método debe ser capaz
de distinguir cuál componente fue el que generó el evento. Respecto a la segunda estrategia,
la ventaja es que no es necesario distinguir nada, cada componente tiene su propio oidor y
el código se vuelve más legible y elegante. La desventaja es que la cantidad de código
generado se vuelve mayor. En mi opinión la ventaja de los oidores anónimos sobrepasa la
desventaja y por eso en este curso se definen oidores anónimos para el manejo de eventos.

En las secciones siguientes vamos a revisar los eventos más utilizados que son los de botón,
de choice y de checkbox.

6.4.2 Eventos de botón

Los botones generan un evento de acción al ser oprimidos con el ratón. El programa debe
registrar un oidor por cada botón en que esté interesado. A esta acción se le conoce como
registrar el oidor y se hace con el método addActionListener.

Button b1 = new Button ("Uno");


Button b2 = new Button ("Dos");
b1.addActionListener (
new java.awt.event.ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
aquí va el código que se ejecutará cuando el botón b1 se oprima
}
});
b2.addActionListener (
new java.awt.event.ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
aquí va el código que se ejecutará cuando el botón b2 se oprima
}
});

78
Lo que está haciendo el código anterior es crear una instancia anónima de la clase
ActionListener para cada botón y registrarla como oidor de ese botón usando su método
addActionListener. La instancia anónima está sobreponiendo el método actionPerformed
que por definición es el método que la máquina virtual de Java invoca cuando se genera un
evento de acción que en este caso se genera al oprimir el botón.

El listado 6-11 presenta el código de un applet que tiene dos campos de texto y dos botones,
uno tiene la etiqueta de Continuar y otro de Limpiar. El botón de Continuar copia el texto
que se haya tecleado en el campo de texto superior al inferior. El botón de Limpiar borra el
texto de ambos campos de texto. La figura 6-12 muestra la salida del programa.

Listado 6-11
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class eventos_btn extends Applet {


GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
Label lbl_algo = new Label ("Escribe algo:");
TextField tf_algo = new TextField (20);
Label lbl_esto = new Label ("Esto escribiste:");
TextField tf_esto = new TextField (20);
Button btn_continuar = new Button ("Continuar");
Button btn_limpiar = new Button ("Limpiar");

public void init ()


{
setLayout (gbl);
// Etiqueta de algo
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (lbl_algo, gbc);
add (lbl_algo);
// Textfield de algo
gbc.gridx = 1; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_algo, gbc);
add (tf_algo);

79
Listado 6-11 Continuación
// Boton de continuar
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 10;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (btn_continuar, gbc);
add (btn_continuar);
// Boton de limpiar
gbc.gridx = 1; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (btn_limpiar, gbc);
add (btn_limpiar);
// Etiqueta de esto
gbc.gridx = 0; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (lbl_esto, gbc);
add (lbl_esto);
// Textfield de esto
gbc.gridx = 1; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_esto, gbc);
add (tf_esto);
// Agrega los oidores
btn_continuar.addActionListener (
new java.awt.event.ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
tf_esto.setText (tf_algo.getText ());
}
});
btn_limpiar.addActionListener (
new java.awt.event.ActionListener ()
{

80
Listado 6-11 Continuación
public void actionPerformed (ActionEvent e)
{
tf_algo.setText ("");
tf_esto.setText ("");
}
});
}
}

Figura 6-12

6.4.3 Eventos de choice

Un choice genera un evento cuando se selecciona una opción. Al igual que en el caso de los
botones hay que registrar un oidor, solo que en el caso de los choices el registro se hace con
el método addItemListener, el oidor debe ser del tipo ItemListener y el método que se
sobrepone es itemStateChanged.

Choice ch = new Choice ();


ch.add ("opción 1");
....
ch.add ("opción n");
ch.addItemListener (
new java.awt.event.ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{
código que se desea que se ejecute cuando se seleccione una opción
}});

81
El listado 6-12 presenta un applet que tiene un choice y un campo de texto. Cada vez que se
selecciona una opción del choice el campo de texto despliega el nombre de la opción. La
figura 6-13 muestra la salida del applet una vez que se seleccionó la opción 4.

Listado 6-12
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class eventos_chs extends Applet {


GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
Choice c = new Choice ();
Label lbl_opcion = new Label ("La opcion fue: ");
TextField tf_opcion = new TextField (20);

public void init ()


{
setLayout (gbl);
c.add ("opcion 1");
c.add ("opcion 2");
c.add ("opcion 3");
c.add ("opcion 4");
c.add ("opcion 5");
// Choice
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (c, gbc);
add (c);
// Etiqueta de opcion
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (lbl_opcion, gbc);
add (lbl_opcion);
// Textfield de opcion
gbc.gridx = 1; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;

82
Listado 6-12 Continuación
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_opcion, gbc);
add (tf_opcion);
// Agrega los oidores
c.addItemListener (
new java.awt.event.ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{
tf_opcion.setText (c.getSelectedItem ());
}
});
}
}

Figura 6-13

6.4.4 Eventos de checkbox

Los checkboxes generan un evento al ser marcados o desmarcados. Al igual que en el caso
de los choices hay que registrar un oidor con el método addItemListener, el oidor debe ser
del tipo ItemListener y el método que se sobrepone es itemStateChanged.

Checkbox cb1 = new Choice ("opción 1");


cb1.addItemListener (
new java.awt.event.ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{

83
código que se desea que se ejecute al marcar o desmarcar el checkbox 1
}});

El listado 6-13 presenta un applet que tiene dos checkboxes y un campo de texto. Cada vez
que se marca o se desmarca un checkbox se escribe un mensaje alusivo a la acción en el
campo de texto. La figura 6-14 presenta la salida después de marcar el primer checkbox.

Listado 6-13
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class eventos_cb extends Applet {


GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
Checkbox cb1 = new Checkbox ("opcion 1");
Checkbox cb2 = new Checkbox ("opcion 2");
Label lbl_mssg = new Label ("Mensaje: ");
TextField tf_mssg = new TextField (20);

public void init ()


{
setLayout (gbl);
// Checkbox 1
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbl.setConstraints (cb1, gbc);
add (cb1);
// Checkbox 2
gbc.gridx = 1; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (cb2, gbc);
add (cb2);
// Etiqueta de mensaje
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 30;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;

84
Listado 6-13 Continuación
gbl.setConstraints (lbl_mssg, gbc);
add (lbl_mssg);
// Textfield de mensaje
gbc.gridx = 1; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 50; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_mssg, gbc);
add (tf_mssg);
// Agrega los oidores
cb1.addItemListener (
new java.awt.event.ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{
if (cb1.getState ())
tf_mssg.setText ("se activo checkbox 1");
else
tf_mssg.setText ("se desactivo checkbox 1");
}
});
cb2.addItemListener (
new java.awt.event.ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{
if (cb2.getState ())
tf_mssg.setText ("se activo checkbox 2");
else
tf_mssg.setText ("se desactivo checkbox 2");
}
});
}
}

85
Figura 6-14

6.5 Componentes avanzados


6.5.1 Paneles

Un panel es un componente contenedor y como tal puede contener otros componentes de


AWT. Se utilizan para eliminar la restricción que imponen los diseños de que solo cabe un
componente por celda o por zona y poder programas interfaces gráficas complejas. Un
panel tiene su propio diseño y sus propios componentes como botones, campos de texto,
etc. Los paneles a su vez pueden contener otros paneles que a su vez tengan otros
componentes. No hay restricción en el número de paneles ni en el nivel de anidación.

Parar usar un panel hay que crear un objeto de tipo Panel.

Panel p = new Panel ();

Como se dijo anteriormente los paneles pueden tener su propio diseño especificado con el
método setLayout.

p.setLayout (diseño);

Todo lo que se ha dicho sobre agregar componentes con el método add es válido para los
paneles.

p.add (botón);
p.add (checkbox);

86
El uso de paneles puede mejorar el diseño de una interface gráfica evitando que los
componentes se vean alineados como si estuvieran en una parrilla gigante. Una regla de
diseño especifica que los componentes que están relacionados entre sí se coloquen juntos
en un panel. El listado 6-14 presenta un applet que utiliza 3 paneles en un diseño de parrilla
vertical. El panel de en medio tiene a su vez dos paneles para que sus componentes no se
amontonen en un solo lado. La figura 6-15 muestra la salida del programa.

Listado 6-14
import java.awt.*;
import java.applet.*;

public class testPanel extends Applet {


Panel pnl_1 = new Panel ();
Panel pnl_2 = new Panel ();
Panel pnl_3 = new Panel ();
Panel pnl_4 = new Panel ();
Panel pnl_5 = new Panel ();
Checkbox cb_1 = new Checkbox ("Opcion uno");
Checkbox cb_2 = new Checkbox ("Opcion dos");
Checkbox cb_a = new Checkbox ("Opcion A");
Checkbox cb_b = new Checkbox ("Opcion B");
Checkbox cb_c = new Checkbox ("Opcion C");
Label lbl_1 = new Label ("Soy etiqueta");
Choice ch_1 = new Choice ();
Choice ch_2 = new Choice ();
Button btn_1 = new Button ("Soy un boton");
Button btn_2 = new Button ("Soy otro boton");

public void init ()


{
setBackground (Color.lightGray);
setLayout (new GridLayout (3, 1));
add (pnl_1);
add (pnl_2);
add (pnl_5);
// panel 1
pnl_1.setLayout (new FlowLayout (FlowLayout.LEFT));
pnl_1.add (cb_1);
pnl_1.add (cb_2);
// panel 2
pnl_2.setLayout (new BorderLayout ());
// panel 3
pnl_3.setLayout (new GridLayout (3, 1));
pnl_3.add (cb_a);
pnl_3.add (cb_b);

87
Listado 6-14 Continuación
pnl_3.add (cb_c);
pnl_2.add ("West", pnl_3);
// panel 4
pnl_4.setLayout (new GridLayout (1, 3, 30, 10));
pnl_4.add (lbl_1);
ch_1.add ("A"); ch_1.add ("B"); ch_1.add ("C");
ch_2.add ("1"); ch_2.add ("2");
pnl_4.add (ch_1);
pnl_4.add (ch_2);
pnl_2.add ("East", pnl_4);
// panel 5
pnl_5.setLayout (new FlowLayout (FlowLayout.CENTER, 10, 30));
pnl_5.add (btn_1);
pnl_5.add (btn_2);
}
}

Figura 6-15

6.5.2 Lienzos

Un lienzo es un componente cuya principal utilidad es desplegar gráficas o imágenes.


Debido a que no puede contener otros componentes por lo general no se usa en forma
directa sino a través de programar una clase que extienda a la clase Canvas.

public miCanvas extends Canvas {

88
La clase Canvas define un método paint vacío. La clase que extienda a Canvas sobrepone
el método paint para dibujar o desplegar una imagen de la misma forma en que lo hace el
método paint de un applet. Si se revisa el manual de Java encontraremos que tanto la clase
Canvas como la clase Applet son subclases de la clase Component y de ella heredan el
método paint. La clase Component representa objetos que pueden ser desplegados en la
pantalla y pueden interactuar con el usuario. Otras subclases de Component son
precisamente las clases que definen los componentes que ya vimos como Panel, Button,
TextField, etc.

El listado 6-15 presenta un applet que del lado izquierdo coloca un lienzo donde dibuja una
lámpara sobre una mesa y del lado derecho un panel donde hay dos choices que controlan
el color con que se dibujan la mesa y la lámpara. La clase miCanvas es la que realmente
hace el dibujo en su método paint y además tiene que definir dos métodos para que el
applet le avise si cambia el color. La figura 6-16 muestra la salida del applet.

Por otra parte el listado 6-16 presenta un applet que utiliza un lienzo para desplegar una
imagen GIF. El método paint de la clase idCanvas es el que despliega la imagen. La figura
6-17 muestra la salida de este applet.

Listado 6-15
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class testCanvas extends Applet {


miCanvas mc;
Panel pnl_1;
Label lbl_1 = new Label ("La lampara de Aladino");
Label lbl_2 = new Label ("Color de la mesa");
Choice color_mesa = new Choice ();
Label lbl_3 = new Label ("Color de la lampara");
Choice color_lampara = new Choice ();

public void init ()


{
setLayout (new BorderLayout ());
lbl_1.setFont (new Font ("TimesRoman", Font.BOLD, 18));
add ("North", lbl_1);
add ("Center", mc = new miCanvas ());
add ("East", pnl_1 = new Panel ());
pnl_1.setLayout (new GridLayout (4, 1));
pnl_1.add (lbl_2);
color_mesa.add ("Negro"); color_mesa.add ("Rojo");
color_mesa.add ("Azul"); color_mesa.add ("Verde");
pnl_1.add (color_mesa);
pnl_1.add (lbl_3);

89
Listado 6-15 Continuación
color_lampara.add ("Negro"); color_lampara.add ("Rojo");
color_lampara.add ("Azul"); color_lampara.add ("Verde");
pnl_1.add (color_lampara);
// oidores
color_mesa.addItemListener (
new java.awt.event.ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{
mc.cambiaColorMesa (color_mesa.getSelectedIndex ());
}
});
color_lampara.addItemListener (
new java.awt.event.ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{
mc.cambiaColorLampara (color_lampara.getSelectedIndex ());
}
});
}
}

class miCanvas extends Canvas {


int color_mesa = 0;
int color_lampara = 0;

public void paint (Graphics g)


{
switch (color_mesa) {
case 0 : g.setColor (Color.black); break;
case 1 : g.setColor (Color.red); break;
case 2 : g.setColor (Color.blue); break;
case 3 : g.setColor (Color.green); break;
}
g.fillRect (0, 250, 290, 290); // dibuja la mesa
switch (color_lampara) {
case 0 : g.setColor (Color.black); break;
case 1 : g.setColor (Color.red); break;
case 2 : g.setColor (Color.blue); break;
case 3 : g.setColor (Color.green); break;
}
g.drawLine (125, 250, 125, 160);
g.drawLine (175, 250, 175, 160); // dibuja la base
g.drawArc (85, 157, 130, 50, -65, 312); // dibuja la cubierta

90
Listado 6-15 Continuación
g.drawArc (85, 87, 130, 50, 62, 58);
g.drawLine (85, 177, 119, 89);
g.drawLine (215, 177, 181, 89);
g.fillArc (78, 120, 40, 40, 63, -174); // dibuja las motas
g.fillOval (120, 96, 40, 40);
g.fillArc (173, 100, 40, 40, 110, 180);
}

public void cambiaColorMesa (int c)


{
color_mesa = c; repaint ();
}

public void cambiaColorLampara (int c)


{
color_lampara = c; repaint ();
}
}

Figura 6-16

91
Listado 6-16
import java.awt.*;
import java.applet.*;

public class Licencia extends Applet {


Panel pnl_east = new Panel ();
Panel pnl_north = new Panel ();
Panel pnl_1 = new Panel ();
Panel pnl_2 = new Panel ();
Panel pnl_3 = new Panel ();
Label lbl_dmv = new Label ("Department of Motor Vehicles");
Label lbl_number = new Label ("B47U89RE243");
Label lbl_nombre = new Label ("BART JO-JO SIMPSON");
Label lbl_calle = new Label ("742 Evergreen Terrace");
Label lbl_ciudad = new Label ("Springfield, U.S.A.");
Label lbl_sex1 = new Label ("Sex");
Label lbl_height1 = new Label ("Height");
Label lbl_eyes1 = new Label ("Eyes");
Label lbl_weight1 = new Label ("Weight");
Label lbl_sex2 = new Label ("M");
Label lbl_height2 = new Label ("4'0");
Label lbl_eyes2 = new Label ("blue");
Label lbl_weight2 = new Label ("85");

public void init ()


{
setBackground (Color.black);
setForeground (Color.yellow);
setLayout (new BorderLayout ());
Image foto = getImage (getCodeBase (), "bart_id.gif"); // agrega la foto
idCanvas cvs_foto = new idCanvas (foto);
add ("Center", cvs_foto);
pnl_north.setLayout (new BorderLayout ());
lbl_dmv.setFont (new Font ("TimesRoman", Font.BOLD, 18));
pnl_north.add ("Center", lbl_dmv); // agrega el titulo
add ("North", pnl_north);
pnl_east.setLayout (new BorderLayout ());
pnl_1.setLayout (new BorderLayout ());
pnl_1.add ("Center", lbl_number); // agrega la descripción
pnl_east.add ("North", pnl_1);
pnl_2.setLayout (new GridLayout (3, 1));
pnl_2.add (lbl_nombre); pnl_2.add (lbl_calle); pnl_2.add (lbl_ciudad);
pnl_east.add ("Center", pnl_2);
pnl_3.setLayout (new GridLayout (2, 4));
pnl_3.add (lbl_sex1);

92
Listado 6-16 Continuación
pnl_3.add (lbl_height1);
pnl_3.add (lbl_eyes1); pnl_3.add (lbl_weight1); pnl_3.add (lbl_sex2);
pnl_3.add (lbl_height2); pnl_3.add (lbl_eyes2); pnl_3.add (lbl_weight2);
pnl_east.add ("South", pnl_3);
add ("East", pnl_east);
}
}

class idCanvas extends Canvas {


Image image;

idCanvas (Image image)


{
super ();
this.image = image;
}

public void paint (Graphics g)


{
g.drawImage(image, 0, 0, 100, 100, this);
g.drawRect(0, 0, 100, 100);
}
}

Figura 6-17

93
6.5.3 Áreas de texto

Un área de texto es un componente que se utiliza para leer datos de entrada de manera
semejante al campo de texto. La diferencia es que, mientras el campo de texto puede leer
solo una línea de texto, el área de texto puede leer varias. Si el texto es más grande que el
tamaño con el que se despliega el área de texto automáticamente se colocan barras de scroll
horizontales o verticales según sea el caso.

Para utiliza un área de texto hay que crear un objeto de tipo TextArea pasando como
argumentos al constructor en forma opcional un string inicial, un tamaño preferido de
desplegado en renglones y columnas y un indicador para que despliegue o no barras de
scroll de inicio.

TextArea ta1 = new TextArea (); // el tamaño se deja al diseño


TextArea ta2 = new TextArea (10, 60); // 10 renglones y 60 columnas
TextArea ta3 = new TextArea ("hola mundo", 10, 60); // texto inicial
TextArea ta4 = new TextArea ("hola", 10, 60, TextArea.SCROLLBARS_NONE);

A pesar de que el texto esté en varias líneas, el área de texto lo trata como un string, es
decir, como si fuera una sola cadena de caracteres. Cuando desde un programa se altera el
texto de un área de texto es responsabilidad del programador insertar saltos de línea ('\n')
dentro del string para que el texto se visualice por renglones.

El texto capturado se puede recuperar llamando al método getText.

String s = ta1.getText ();

Se puede asignar texto desde el programa con el método setText.

ta2.setText ("Esto es una línea\nEsto es otra línea");

El listado 6-17 presenta un applet con dos áreas de texto. Después de escribir un texto
cualquiera en la primera área de texto, al oprimir el botón de Cuenta, la segunda área de
texto despliega el número de líneas y de caracteres que se escribieron. La figura 6-18
muestra la salida del programa.

Listado 6-17
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class ta extends Applet {


GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
TextArea ta1 = new TextArea ("Escribe algo");
TextArea ta2 = new TextArea ("Soy una textarea", 5, 25);
Button btn = new Button ("Cuenta");

94
Listado 6-17 Continuación
public void init ()
{
setLayout (gbl);
// text area de arriba
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 100; gbc.weighty = 33;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (ta1, gbc);
add (ta1);
// boton
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 33;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (btn, gbc);
add (btn);
// text area de abajo
gbc.gridx = 0; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 33;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (ta2, gbc);
add (ta2);
btn.addActionListener (
new java.awt.event.ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
String s = ta1.getText ();
int k = 0;
for (int i = 0; i < s.length (); i++)
if (s.charAt (i) == '\n')
k++;
ta2.setText ("Escribiste\n" + k + " renglones\n" + s.length () + " caracteres");
}
});
}
}

95
Figura 6-18

6.5.4 Listas

Una lista es similar al choice que permite seleccionar de entre varias opciones de una lista
con dos diferencias: su presentación es en forma de lista y permite seleccionar más de una
opción.

Para usar una lista hay que crear un objeto de tipo List pasando como argumentos
opcionales el número de opciones que estarán visibles y un indicador si la liste permite o no
opciones múltiples.

List lst1 = new List (); // permite escoger una opción


List lst2 = new List (5); // cinco opciones visibles a la vez
List lst3 = new List (5, true); // permite opciones múltiples

Las opciones se agregan con el método add.

lst2.add ("opción uno");


lst2.add ("opción dos");

96
En las listas que permiten la selección de únicamente un elemento el método
getSelectedIndex devuelve la posición del elemento seleccionado.

int opción = lst1.getSelectedIndex ();

En las listas que permiten múltiples selecciones el método getSelectedIndexes devuelve un


arreglo de enteros con las posiciones de los elementos seleccionados.

int opciones[] = lst3.getSelectedIndexes ();

El listado 6-18 presenta un applet con un choice, una lista que permite seleccionar un solo
elemento y una lista que permite selección múltiple. Al oprimir el botón, en la parte inferior
se despliegan las opciones que fueron seleccionadas en el choice y en las listas. La figura 6-
19 muestra la salida del programa.

Listado 6-18
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class lst extends Applet {


GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
Choice chs_1 = new Choice ();
List lst_1 = new List (); // una sola seleccion
List lst_2 = new List (4, true); // seleccion multiple
Button btn_revisar = new Button ("Revisar");
Label lbl_1 = new Label ("En el choice la opcion es: ");
Label lbl_2 = new Label ("En la lista 1 la opcion es: ");
Label lbl_3 = new Label ("En la lista 2 las opciones son: ");

public void init ()


{
setLayout (gbl);
// choice
chs_1.add ("opcion 1"); chs_1.add ("opcion 2"); chs_1.add ("opcion 3");
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 33; gbc.weighty = 40;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (chs_1, gbc);
add (chs_1);
// lista 1
lst_1.add ("opcion 1"); lst_1.add ("opcion 2"); lst_1.add ("opcion 3");
lst_1.add ("opcion 4"); lst_1.add ("opcion 5");

97
Listado 6-18 Continuación
gbc.gridx = 1; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 33; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (lst_1, gbc);
add (lst_1);
// lista 2
gbc.gridx = 2; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 33; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (lst_2, gbc);
lst_2.add ("opcion 1"); lst_2.add ("opcion 2"); lst_2.add ("opcion 3");
lst_2.add ("opcion 4");
add (lst_2);
// boton de revisar
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 3; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 20;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (btn_revisar, gbc);
add (btn_revisar);
// etiqueta 1
gbc.gridx = 0; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 40;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (lbl_1, gbc);
add (lbl_1);
// etiqueta 2
gbc.gridx = 1; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (lbl_2, gbc);
add (lbl_2);
// etiqueta 3
gbc.gridx = 2; gbc.gridy = 2;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 0;

98
Listado 6-18 Continuación
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (lbl_3, gbc);
add (lbl_3);
// oidor
btn_revisar.addActionListener (
new java.awt.event.ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
lbl_1.setText ("En el choice la opcion es: " + chs_1.getSelectedIndex ());
lbl_2.setText ("En la lista 1 la opcion es: " + lst_1.getSelectedIndex ());
int idx[] = lst_2.getSelectedIndexes ();
StringBuffer mssg = new StringBuffer ("En la lista 2 las opciones son: ");
for (int i = 0; i < idx.length; i++)
mssg.append (idx[i] + " ");
lbl_3.setText (mssg.toString ());
}
});
}
}

Figura 6-19

99
6.5.5 Frames

Un frame es una ventana que, dependiendo del sistema operativo, cuenta con un título, un
menú de barra, indicadores para minimizar, maximizar o cerrar, y otras características
típicas de las ventanas.

Un frame es un componente contenedor y como tal puede tener su propio diseño y contener
otros componentes de AWT.

Para usar un frame hay que crear un objeto de tipo Frame pasando como parámetro el título
de la ventana.

Frame frm = new Frame ("titulo de la ventana");

El frame puede tener su propio diseño y componentes.

frm.setLayout (new GridLayout (4, 1));


frm.add (new Button ("soy un botón"));
frm.add (new Label ("soy una etiqueta"));

Se le puede asignar un tamaño en pixeles usando el método setSize o indicarle las


coordenadas de su esquina superior izquierda con setLocation.

frm.setSize (400, 300); // 400 pixeles de ancho y 300 pixeles de alto


frm.setLocation (200, 100); // 200 en x y 100 en y

Cuando se crea un nuevo frame, éste es invisible. El método setVisible hace que el frame
sea visible o de vuelta invisible mediante un argumento booleano que puede valer true (la
ventana se hace visible) o false (el frame se hace invisible).

frm.setVisible (true); // hace visible el frame

El método dispose elimina el frame y libera la memoria. Se invoca cuando el frame ya no


va a ser utilizado.

frm.dispose ();

Por default el indicador de cerrar ventana, que en Windows tiene una marca de cruz y está
en la parte superior derecha del frame, esta inactivo. Es necesario crear un oidor de la clase
WindowAdapter y registrarlo usando el método addWindowListener para indicarle al frame
que se destruya al oprimir ese indicador.

frm.addWindowListener (
new java.awt.event.WindowAdapter ()
{
public void windowClosing (WindowEvent e)
{

100
frm.dispose ();
}
});

El listado 6-19 presenta un applet que crea un frame, le agrega una etiqueta, un campo de
texto y un botón y luego lo hace visible. La figura 6-20 muestra la salida de este programa.

Listado 6-19
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class frm extends Applet {


Frame frm_1 = new Frame ("Mi ventanita");
Panel pnl_1 = new Panel ();
GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
Label lbl_nombre = new Label ("Nombre");
TextField tf_nombre = new TextField (20);
Button btn_ok = new Button ("OK");
Label lbl_applet = new Label ("aqui es el applet");

public void init ()


{
// layout del applet
setBackground (Color.lightGray);
setLayout (new FlowLayout (FlowLayout.LEFT));
add (lbl_applet);
// layout de la ventana
frm_1.setLayout (gbl);
// etiqueta de nombre
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 30; gbc.weighty = 80;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (lbl_nombre, gbc);
frm_1.add (lbl_nombre);
// textfield de nombre
gbc.gridx = 1; gbc.gridy = 0;
gbc.gridwidth = 1; gbc.gridheight = 1;
gbc.weightx = 70; gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints (tf_nombre, gbc);
frm_1.add (tf_nombre);

101
Listado 6-19 Continuación
// boton de ok
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 2; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 20;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints (btn_ok, gbc);
frm_1.add (btn_ok);
frm_1.setLocation (100, 150);
frm_1.setSize (400, 300);
frm_1.setVisible (true);
// oidor
frm_1.addWindowListener (
new java.awt.event.WindowAdapter ()
{
public void windowClosing (WindowEvent e)
{
frm_1.dispose ();
}
});
}
}

Figura 6-20

102
6.5.6 Diálogos

Un diálogo es semejante a un frame en el sentido que es una ventana que puede tener su
propio diseño y contener a otros componentes. Las diferencias son que los diálogos
necesitan tener un frame ligado a ellos, no tienen indicadores de maximizar ni de minimizar
y pueden operar en forma modal. Un diálogo modal impide que sean accesadas otras
ventanas hasta que el diálogo sea cerrado. Por estos motivos y por lo general, los diálogos
se utilizan en forma modal y para avisar de errores en el programa o pedir datos al usuario.

Para usar un diálogo hay que crear un objeto de tipo Dialog pasando como argumento
obligatorio el frame al cual están ligados y opcionalmente un argumento string con el título
del diálogo y una variable booleana para indicar si el diálogo es modal o no modal.

Dialog dlg1 = new Dialog (frame); // el diálogo es no modal por default


Dialog dlg2 = new Dialog (frame, true); // el diálogo es modal
Dialog dlg3 = new Dialog (frame, "titulo", true); // tiene título y es modal

Los métodos setLayout, add, setSize, setLocation, setVisible, dispose y addWindowListener


tienen la misma funcionalidad que en los frames.

Para que un applet pueda usar un diálogo necesita hacer referencia a la ventana del
navegador que lo contiene. Una forma de conseguirlo es obtener los componentes padres
del applet, usando el método getParent, hasta encontrar un objeto de tipo Frame y pasar
como argumento ese objeto al constructor del diálogo.

Object tmp = getParent ();


while (! (tmp instanceof Frame))
tmp = ((Component) tmp).getParent ();
dlg1 = new Dialog ((Frame) tmp, "título", true);

El listado 6-20 presenta un applet que abre un diálogo modal y por lo tanto se bloquea. Al
cerrar el diálogo el programa puede continuar y abre un frame. La figura 6-21 muestra la
salida del programa antes de cerrar el diálogo.

Listado 6-20
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class dlgm extends Applet {


GridBagLayout gbl = new GridBagLayout ();
GridBagConstraints gbc = new GridBagConstraints ();
Label lbl_error = new Label ("El frame no se abrira hasta cerrar este dialogo");
Label lbl_hola = new Label ("Hola");
Button btn_ok = new Button ("Cerrar");
Frame frm_1 = new Frame ("Ventanita");

103
Listado 6-20 Continuación
Dialog dlg_1;

public void init ()


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

104
Listado 6-20 Continuación
frm_1.setSize (400, 300);
frm_1.setVisible (true);
frm_1.addWindowListener (
new java.awt.event.WindowAdapter ()
{
public void windowClosing (WindowEvent e)
{
frm_1.dispose ();
}
});
}
}

Figura 6-21

6.6 Aplicaciones con AWT


En todos los ejemplos vistos en este capítulo se han usado applets. Hay dos razones para
haberlo hecho así:

• Al estar los applets incrustados en una página HTML y correr en un navegador se


prestan a que cualquier persona pueda correrlos sin necesidad de tener instalado el JDK
en su computadora.

• Como los applets están corriendo en el navegador ya tienen por default una ventana
contenedora donde pueden agregar componentes.

105
Sin embargo, como ya sabemos que por razones de seguridad, los applets tienen la
restricción de que no pueden accesar los recursos locales de la computadora donde están
corriendo y por eso muchas veces se tienen que programar aplicaciones.

La programación de aplicaciones usando AWT es muy parecida a la programación de


applets usando AWT. Hay tres diferencias:

• La aplicación debe crear un frame principal donde pueda colocar sus componentes
AWT. En la práctica lo que se hace es que la clase principal extienda a la clase Frame.
• Las definiciones de los componentes se hacen dentro del constructor de la clase en lugar
de hacerlo en el método init como los applets.
• Deben tener un método main que por lo general su única acción es instanciar la clase
principal (que es de tipo Frame) y hacerla visible.

El listado 6-21 presenta la versión aplicación del applet que se presentó en el listado 6-16 y
que despliega una imagen utilizando un lienzo. Se puede ver que el código es prácticamente
el mismo en las dos versiones. La figura 6-22 muestra la salida del programa.

Listado 6-21
import java.awt.*;
import java.awt.event.*;

public class DMV extends Frame {


Panel pnl_east = new Panel ();
Panel pnl_north = new Panel ();
Panel pnl_1 = new Panel ();
Panel pnl_2 = new Panel ();
Panel pnl_3 = new Panel ();
Label lbl_dmv = new Label ("Department of Motor Vehicles");
Label lbl_number = new Label ("B47U89RE243");
Label lbl_nombre = new Label ("BART JO-JO SIMPSON");
Label lbl_calle = new Label ("742 Evergreen Terrace");
Label lbl_ciudad = new Label ("Springfield, U.S.A.");
Label lbl_sex1 = new Label ("Sex");
Label lbl_height1 = new Label ("Height");
Label lbl_eyes1 = new Label ("Eyes");
Label lbl_weight1 = new Label ("Weight");
Label lbl_sex2 = new Label ("M");
Label lbl_height2 = new Label ("4'0");
Label lbl_eyes2 = new Label ("blue");
Label lbl_weight2 = new Label ("85");

DMV ()
{
setBackground (Color.lightGray);
setLayout (new BorderLayout ());

106
Listado 6-21 Continuación
// agrega la foto
Image foto = getToolkit ().getImage ("bart_id.gif");
fotoCanvas cvs_foto = new fotoCanvas (foto, this);
add ("Center", cvs_foto);
// agrega el titulo
pnl_north.setLayout (new BorderLayout ());
lbl_dmv.setFont (new Font ("TimesRoman", Font.BOLD, 18));
pnl_north.add ("Center", lbl_dmv);
add ("North", pnl_north);
// agrega la descripcion
pnl_east.setLayout (new BorderLayout ());
pnl_1.setLayout (new BorderLayout ());
pnl_1.add ("Center", lbl_number);
pnl_east.add ("North", pnl_1);
pnl_2.setLayout (new GridLayout (3, 1));
pnl_2.add (lbl_nombre); pnl_2.add (lbl_calle); pnl_2.add (lbl_ciudad);
pnl_east.add ("Center", pnl_2);
pnl_3.setLayout (new GridLayout (2, 4));
pnl_3.add (lbl_sex1); pnl_3.add (lbl_height1); pnl_3.add (lbl_eyes1);
pnl_3.add (lbl_weight1); pnl_3.add (lbl_sex2); pnl_3.add (lbl_height2);
pnl_3.add (lbl_eyes2); pnl_3.add (lbl_weight2);
pnl_east.add ("South", pnl_3);
add ("East", pnl_east);
setSize (350, 200);
setLocation (200, 100);
addWindowListener (
new java.awt.event.WindowAdapter ()
{
public void windowClosing (WindowEvent e)
{
dispose ();
}
});
}

public static void main (String args [])


{
DMV dmv = new DMV ();
dmv.setVisible (true);
}
}

107
Listado 6-21 Continuación
class fotoCanvas extends Canvas {
Image image;

public fotoCanvas (Image image, Container padre)


{
super ();
this.image = image;
}

public void paint (Graphics g)


{
g.drawImage(image, 0, 0, 100, 100, this);
g.drawRect(0, 0, 100, 100);
}
}

Figura 6-22

108
Capítulo 7 Archivos
7.1 Definición
En Java un flujo es una secuencia de bytes sin formato. Su origen puede ser un archivo en
disco, un socket de comunicación de entrada, un arreglo, el teclado, etc. Su destino puede
ser otro archivo de disco, un socket de comunicación de salida, otro arreglo, la pantalla, la
impresora, etc. En general los flujos no tienen porque saber cuál es el origen ni el destino
de la información que reciben, son las clases específicas las que determinan el origen y el
destino. La ventaja de hacerlo así es que se pueden agregar orígenes y destinos de
información sin que las clases generales tengan que cambiar.

En este capítulo en particular vamos a estudiar los flujos que tiene que tienen como origen
y como destino un archivo.

Un archivo es una secuencia de bytes que se guarda en disco y que puede actuar como
origen o como destino de un flujo. Los bytes no tienen ningún formato ni tamaño de
registro. El programador es el encargado de ordenar y darle sentido a esa secuencia.

La librería de entrada y salida de Java (java.io) divide las clases que manejan los flujos en
dos grandes categorías: las clases lectoras que se ocupan de los flujos de entrada y las
clases escritoras que se ocupan de los flujos de salida. Un vistazo rápido al manual nos
muestra que la librería define más de 40 clases entre lectoras y escritoras, sin embargo, para
efectos de una introducción al manejo de archivos en Java es suficiente con aprender a usar
cuatro clases especificas de archivos (dos lectoras y dos escritoras) y seis clases generales
de flujos (tres de cada clase). Al final del capítulo presentaremos otra clase general lectora
que funciona para resolver un problema en particular.

7.2 Principales clases lectoras


La clase FileInputStream es una clase específica que obtiene bytes de un archivo. En su
constructor recibe como argumento el nombre físico de un archivo y crear un flujo de bytes.
Define el método read que puede lee una secuencia de bytes y lo guarda en un arreglo de
bytes. Esta clase es útil si lo que se busca es tratar a un archivo como una secuencia de
bytes.

El siguiente segmento de código ilustra la forma en que se puede leer un archivo en bloques
de 1000 bytes.

byte buffer[] = new byte[1000];


FileInputStream fd_in = new FileInputStream ("archivo.dat");
while (fd_in.read (buffer) != -1) {
...
}
fd_in.close ();

109
La clase InputStreamReader es una clase general que convierte un flujo de bytes en un flujo
de caracteres. Define el método read que puede leer un carácter o un arreglo de caracteres.
Esta clase se utiliza si sabemos que el flujo de entrada es de caracteres de texto. Como la
clase es general y acepta cualquier flujo de bytes de entrada (no solo con origen en un
archivo de disco) hay que pasarle como argumento en el constructor la clase específica
ligada al origen de los datos y que en este caso es FileInputStream.

El siguiente segmento de código ilustra la forma en que se lee un archivo de caracteres:

InputStreamReader fd_in = new InputStreamReader (


new FileInputStream ("archivo.dat"));
int c;
while ((c = fd_in.read ()) != -1) {
procesa el carácter
}
fd_in.close ();

La clase BufferedReader recibe un flujo de caracteres e implementa un buffer para poder


leer líneas de texto. Define el método readLine para leer una línea de texto. Esta clase se
utiliza si sabemos que el archivo es de texto y está escrito en líneas separadas por retornos
de carro. Esta clase es general, no está ligada a ningún origen en particular y por lo tanto
hay que pasarle como argumento en su constructor un flujo de caracteres.

Para el manejo de archivos hay dos formas para construir un objeto de tipo BufferedReader.
Una es utilizar un objeto InputStreamReader creado sobre un objeto de tipo
FileInputStream:

BufferedReader fd_in = new BufferedReader (


new InputStreamReader (
new FileInputStream ("archivo.dat")));

La ventaja de hacerlo así es que se puede manejar el tamaño del buffer e incluso cambiar la
codificación de los caracteres. La codificación de caracteres es la forma que tiene Java de
convertir bytes de 8 bits en caracteres Unicode de 16 bits. Hay que recordar que en Java el
tipo char ocupa 16 bits y utiliza el código Unicode que a su vez es un superconjunto del
popular código ASCII de 7 bits.

La otra forma es aceptar el tamaño del buffer y la codificación predefinidos, lo cuál muchas
veces es lo más conveniente, y para estos casos se puede usar la clase FileReader que como
argumento en el constructor se le pasa el nombre del archivo.

BufferedReader fd_in = new BufferedReader (


new FileReader ("archivo.dat"));

El listado 7-1 presenta un programa que lee un archivo de texto y lo despliega en la pantalla
línea por línea.

110
Listado 7-1
import java.io.*;

public class fileCharReader {

public static void main (String args[])


{
BufferedReader fd = null;
String linea = "";
try {
fd = new BufferedReader (new FileReader ("texto.txt"));
}
catch (FileNotFoundException e) {
System.out.println ("No pude abrir el archivo texto.txt");
}
try {
while ((linea = fd.readLine ()) != null)
System.out.println (linea);
fd.close ();
}
catch (IOException e) {
System.out.println ("Error al leer");
}
}
}

En Java no hay una instrucción directa para leer datos del teclado. El teclado se considera
como origen de un flujo de datos, está asociado con la variable System.in de tipo
InputStreamReader y se trata de la misma manera que cualquier flujo. El listado 7-2
presenta un programa que lee del teclado una línea de texto y un entero.

Listado 7-2
import java.io.*;

public class teclado {

public static void main (String args[])


{
BufferedReader kbd = null;
String linea = "", s;
int n;
// abre un archivo asignado al teclado
kbd = new BufferedReader (new InputStreamReader (System.in));
try {
// los strings se leen facil
System.out.print ("escribe un string: ");

111
Listado 7-2 Continuación
linea = kbd.readLine ();
// los numeros hay que convertirlos de string a numero
System.out.print ("escribe un entero: ");
s = kbd.readLine ();
n = new Integer (s).intValue ();
System.out.println ("El string es: " + linea + " y el entero es: " + n);
}
catch (IOException e) {
System.out.println ("Error de I/O");
}
}
}

La clase DataInputStream permite leer tipos primitivos (enteros, reales, booleanos) de


forma portable. Define métodos como readInt para leer un entero, readFloat para leer un
real, readBoolean para leer un booleano, etc. Esta clase se utiliza si lo que se está leyendo
es un archivo de datos binarios. Esta clase es general, no está ligada a ningún origen de
datos en particular y por eso, para leer de un archivo, hay que mandarle como argumento en
su constructor un objeto de tipo FileInputStream.

El siguiente segmento de código muestra la forma en que se lee un archivo de datos que se
sabe trae 1000 enteros.

DataInputStream fd_in = new DataInputStream (


new FileInputStream ("archivo.dat"));
int dato;
for (int i = 0; i < 1000; i++) {
dato = fd_in.readInt ();
procesa el dato
}

El listado 7-3 presenta un programa que lee un archivo de datos en el cuál se sabe que
vienen 5 enteros, 3 reales y un booleano en ese orden.

Listado 7-3
import java.io.*;

public class fileBinReader {

public static void main (String args[])


{
int a = 0;
double b = 0;
boolean c = false;
DataInputStream dis = null;

112
Listado 7-3 Continuación
try {
dis = new DataInputStream (new FileInputStream ("datos.dat"));
}
catch (FileNotFoundException e) {
System.out.println ("No pude abrir el archivo datos.dat");
}
try {
for (int i = 0; i < 5; i++) {
a = dis.readInt ();
System.out.println ("a = " + a);
}
for (int i = 0; i < 3; i++) {
b = dis.readDouble ();
System.out.println ("b = " + b);
}
c = dis.readBoolean ();
System.out.println ("c = " + c);
dis.close ();
}
catch (IOException e) {
System.out.println ("Error al leer");
}
}
}

7.3 Principales clases escritoras


La clase FileOutputStream es una clase específica que escribe una secuencia de bytes a un
archivo en disco. Define el método write que recibe como argumento un arreglo de bytes.
Esta clase se utiliza si estamos tratando a un archivo como una secuencia de bytes.

Para abrir un archivo para escritura hay que crear un objeto de tipo FileOutputStream
pasando como argumento al constructor el nombre del archivo.

FileOutputStream fd_out = new FileOutputStream ("archivo.dat");

El listado 7-4 presenta un programa que recibe el nombre de un archivo, que puede ser de
texto o binario y genera una copia.

113
Listado 7-4
import java.io.*;

public class fileCopyBin {

public void copia (String infile, String outfile)


{
FileInputStream fd_in = null;
FileOutputStream fd_out = null;
try {
fd_in = new FileInputStream (infile);
}
catch (FileNotFoundException e) {
System.out.println ("El archivo " + infile + " no existe");
return;
}
try {
fd_out = new FileOutputStream (outfile);
}
catch (IOException e) {
System.out.println ("No pude crear el archivo " + outfile);
return;
}
try {
byte buffer[] = new byte[1000];
int num;
while ((num = fd_in.read (buffer)) != -1)
fd_out.write (buffer, 0, num);
fd_in.close (); fd_out.close ();
}
catch (IOException e) {
System.out.println ("Error de I/O " + e);
return;
}
System.out.println ("Copiado");
}

public static void main (String args[])


{
fileCopyBin fcb = new fileCopyBin ();
if (args.length > 1)
fcb.copia (args[0], args[1]);
else
System.out.println ("Uso: java fileCopyBin arch1 arch2");
}
}

114
La clase OutputStreamWriter es una clase general que convierte un flujo de caracteres en
un flujo de bytes. Define el método write para escribir a la salida un carácter o un arreglo
de caracteres. Esta clase se utiliza si sabemos que el flujo de salida es de caracteres de
texto. Como la clase es general y acepta cualquier flujo de bytes de salida (no solo con
destino en un archivo de disco) hay que pasarle como argumento en el constructor la clase
específica ligada al destino de los datos y que en este caso es FileOutputStream. Los
caracteres que recibe la clase se convierten a bytes de acuerdo a una codificación de
caracteres en específico y cada invocación al método write provoca que el convertidor de
codificación sea invocado. Por esta razón el manual de Java recomienda no usar esta clase
por separado, más bien como intermedio de la clase BufferedWriter que veremos a
continuación.

La clase BufferedWriter escribe texto a un flujo de salida que acepte caracteres


proporcionando un buffer para la escritura eficiente de caracteres, arreglos y strings. Define
el método write para escribir una línea de texto y el método newLine para escribir un salto
de línea de acuerdo al sistema operativo. Esta clase se utiliza si sabemos que el archivo es
de texto y está escrito en líneas separadas por retornos de carro. Esta clase es general, no
está ligada a ningún destino en particular y por lo tanto hay que pasarle como argumento en
su constructor un flujo de caracteres.

Para el manejo de archivos hay dos formas para construir un objeto de tipo BufferedWriter.
Una es utilizar un objeto OutputStreamReader creado sobre un objeto de tipo
FileOutputStream:

BufferedWriter fd_out = new BufferedWriter (


new OutputStreamWriter (
new FileOutputStream ("archivo.dat")));

La ventaja de hacerlo así es que se puede manejar el tamaño del buffer e incluso cambiar la
codificación de los caracteres.

La otra forma es aceptar el tamaño del buffer y la codificación predefinidos, lo cuál muchas
veces es lo más conveniente, y para estos casos se puede usar la clase FileWriter que como
argumento en el constructor se le pasa el nombre del archivo.

BufferedWriter fd_out = new BufferedWriter (


new FileWriter ("archivo.dat"));

El listado 7-5 presenta una aplicación que copia un archivo de texto por líneas.

115
Listado 7-5
import java.io.*;

public class fileCharCopy {

public void copia (String infile, String outfile)


{
BufferedReader fd_in = null;
BufferedWriter fd_out = null;
String linea = "";
try {
fd_in = new BufferedReader (new FileReader (infile));
}
catch (FileNotFoundException e) {
System.out.println ("No pude abrir el archivo" + infile);
}
try {
fd_out = new BufferedWriter (new FileWriter (outfile));
}
catch (IOException e) {
System.out.println ("No pude crear el archivo" + outfile);
}
try {
while ((linea = fd_in.readLine ()) != null) {
fd_out.write (línea, 0, linea.length ());
fd.newLine ();
}
fd_in.close (); fd_out.close ();
}
catch (IOException e) {
System.out.println ("Error al leer");
}
}
}

public static void main (String args[])


{
fileCharCopy fcc = new fileCharCopy ();
fcc.copia (args[0], args[1]);
}
}

La clase DataOutputStream permite a una aplicación escribir tipos primitivos (enteros,


reales, booleanos) de forma portable. Define métodos como writeInt para escribir un entero,
writeFloat escribir un real, writeBoolean para escribir un booleano, etc. Esta clase se utiliza
si se desea generar un archivo de datos binarios. Esta clase es general, no está ligada a

116
ningún destino de datos en particular y por eso, para escribir en un archivo, hay que
mandarle como argumento en su constructor un objeto de tipo FileOutputStream.

El siguiente segmento de código muestra la forma en que se genera un archivo de datos


enteros que están almacenados en el arreglo datos.

DataOutputStream fd_out = new DataOutputStream (


new FileOutputStream ("archivo.dat"));
int datos [] = { ... };
for (int i = 0; i < datos.length; i++)
fd_out.writeInt ();

El listado 7-6 presenta una aplicación que escribe un archivo de datos con 5 enteros, 3
reales y un booleano en ese orden. Este programa genera el archivo de datos que lee la
aplicación que se presentó en el listado 7-3

Listado 7-6
import java.io.*;

public class fileBinWriter {

public static void main (String args[])


{
int a[] = {10, 6, 5, 12, 5};
double b[] = {3.1415926, 2.8494, 10.834};
boolean c = true;
DataOutputStream dos = null;
try {
dos = new DataOutputStream (new FileOutputStream ("datos.dat"));
}
catch (FileNotFoundException e) {
System.out.println ("No pude crear el archivo");
}
try {
for (int i = 0; i < a.length; i++)
dos.writeInt (a[i]);
for (int i = 0; i < b.length; i++)
dos.writeDouble (b[i]);
dos.writeBoolean (c);
dos.close ();
}
catch (IOException e) {
System.out.println ("Error al escribir");
}
}
}

117
7.4 Archivos de datos
En muchas aplicaciones se requiere leer archivos de datos que son generados por otras
aplicaciones y que vienen en formatos especiales. Por ejemplo, un manejador de bases de
datos o una hoja de cálculo permiten exportar datos en formatos de columna fija o
separados por coma.

Los archivos de columna fija son aquellos donde los datos están alineados de tal forma que
cada renglón representa un conjunto de datos afines (lo que se conoce como un registro) y
cada dato representa un valor particular y siempre comienzan en la misma columna. La
figura 7-1 muestra el contenido de un archivo de datos de este tipo. Cada renglón representa
un alumno y las columnas representan su apellido paterno, apellido materno, primer
nombre y carrera respectivamente y están alineados para que siempre comiencen en la
misma columna.

Teorito Veloz Jaime IEC


Homero Here Elmer LCC
Kate Verde Elsa ISC
Clara Luna Soyla LCC

Figura 7-1

Para leer un archivo de columna fija se lee una línea de texto usando el método readLine de
la clase BufferedReader y luego se extraen los valores con el método de la clase String
substring. Opcionalmente al resultado se le puede aplicar el método trim que elimina los
caracteres blancos que haya al principio y al final de un string.

El listado 7-7 presenta una aplicación que lee un archivo con los datos que se muestran en
la figura 7-1 y los escribe en la pantalla.

Listado 7-7
import java.io.*;

public class archi1 {

public static void main (String args[])


{
String in_filename = "alumnos.dat";
BufferedReader fd_in = null;
try {
fd_in = new BufferedReader (new FileReader (in_filename));
}
catch (FileNotFoundException e) {
System.out.println ("El archivo " + in_filename + " no existe");
return;
}

118
Listado 7-7 Continuación
try {
String linea;
while ((linea = fd_in.readLine ()) != null) {
String ap_pat = linea.substring (0, 14).trim ();
String ap_mat = linea.substring (14, 24).trim ();
String npila = linea.substring (24, 36).trim ();
String carrera = linea.substring (36, linea.length ()).trim ();
System.out.println (ap_pat + " " + ap_mat + " " + npila + " " + carrera);
}
fd_in.close ();
}
catch (IOException e) {
System.out.println ("Error de I/O " + e);
return;
}
}
}

La salida del programa es:

Teorito Veloz Jaime IEC


Homero Here Elmer LCC
Kate Verde Elsa ISC
Clara Luna Soyla LCC

Los archivos de datos separados por comas son aquellos donde los datos, dentro de los
renglones, vienen separados por comas. La figura 7-2 muestra el contenido de un archivo
de datos separados por coma. Cada renglón representa los extremos de una línea recta. Las
dos primeras columnas son las coordenadas del punto (x1, y1) y las siguientes dos
columnas son las coordenadas del punto (x2, y2).

3.345, 73.983, 89, 90.2


43.9, 29.38, 38.0, 82.9
4.6, 6.932, 2.93, 2.98
6.93, 12.93, 0.84, 3.98

Figura 7-2

Para leer un archivo de datos separados por comas se podría proceder leyendo una línea del
archivo usando el método readLine y luego buscar las posiciones de las comas y copiar con
el método substring la parte que está entre las comas. Afortunadamente la librería de
entrada y salida de Java tiene una clase llamada StreamTokenizer que toma un flujo de
entrada y lo separa en "tokens" o palabras, permitiendo que los tokens se puedan leer uno a
la vez. La clase reconoce palabras, números, espacios en blanco, strings y comentarios.

119
Una aplicación típicamente crea una instancia de esta clase y luego invoca repetidamente al
método nextToken hasta que regrese el valor TT_EOF indicando que no hay mas tokens
disponibles.

El listado 7-8 presenta una aplicación que lee un archivo con los datos que se muestran en
la figura 7-2 y escribe la suma de las columnas.

Listado 7-8
import java.io.*;

public class archi2 {

public static void main (String args[])


{
String in_filename = "datos.dat";
double sx1 = 0, sx2 = 0, sy1 = 0, sy2 = 0;
BufferedReader fd_in = null;
try {
fd_in = new BufferedReader (new FileReader (in_filename));
}
catch (FileNotFoundException e) {
System.out.println ("El archivo " + in_filename + " no existe");
return;
}
try {
StreamTokenizer st = new StreamTokenizer (fd_in);
while ((st.nextToken ()) != st.TT_EOF) {
double x1 = st.nval; sx1 += x1;
st.nextToken (); st.nextToken ();
double y1 = st.nval; sy1 += y1;
st.nextToken (); st.nextToken ();
double x2 = st.nval; sx2 += x2;
st.nextToken (); st.nextToken ();
double y2 = st.nval; sy2 += y2;
}
fd_in.close ();
}
catch (IOException e) {
System.out.println ("Error de I/O " + e);
return;
}
System.out.println ("Suma de x1: " + sx1 + " Suma de y1: " + sy1);
System.out.println ("Suma de x2: " + sx2 + " Suma de y2: " + sy2);
}
}

120
La salida de este programa es:

Suma de x1: 58.775 Suma de y1: 123.225


Suma de x2: 130.77 Suma de y2: 180.06

7.5 Archivos remotos


En lugar de abrir un archivo local algunas veces es conveniente poder abrir un archivo que
esté almacenado en algún servidor de páginas web. Hacerlo en Java es muy sencillo, como
las clases generales de flujos no están ligadas a ningún origen en particular, lo único que se
requiere es obtener un origen ligado al archivo en red y utilizarlo como argumento en el
constructor de la clase que nos interese.

La librería de red de Java (java.net) tiene la clase URL para representar una dirección en
Internet y la clase URLConnection para representar una conexión a una dirección dada.

Para abrir un archivo remoto lo primero es crear un objeto de tipo URL pasando como
argumento al constructor la dirección completa del archivo.

URL mi_url = new URL (http://www.foo.com/archivo.txt);

Luego hay que crear un objeto del tipo URLConnection invocando al método
openConnection sobre el objeto de tipo URL y haciendo la conexión mediante el método
connect.

URLConnection conn = mi_url.openConnection ();


conn.connect ();

La clase URLConnection define el método getInputStream que asocia la conexión con un


flujo de entrada. Es este método el que se le pasa a una clase lectora general de flujo para
crear un flujo asociado al archivo remoto.

BufferedReader fd_in = new BufferedReader (


new InputStreamReader (
conn.getInputStream ()));

En este momento se puede proceder a leer el archivo por líneas de la misma manera que si
fuera local.

El listado 7-9 presenta un applet que lee un archivo remoto de un servidor de páginas web.
Hay que recordar que los applets tienen la restricción de que no pueden conectarse con
ninguna computadora que no sea donde estaban, en otras palabras el applet y el archivo
deben estar en la misma computadora. Las aplicaciones no tienen esa restricción. La figura
7-3 muestra la salida del applet.

121
Listado7-9
import java.awt.*;
import java.net.*;
import java.io.*;
import java.applet.*;

public class display extends Applet {


URL mi_url;
TextArea ta = new TextArea ("Leyendo ...");
GridLayout gl = new GridLayout (1, 1);
URLConnection conn = null;
BufferedReader data = null;
String linea;
StringBuffer buffer = new StringBuffer ();

public void init ()


{
try {
mi_url = new URL ("http://148.225.83.24/cursoJava/neruda.txt");
}
catch (MalformedURLException e) {}
setLayout (gl);
add (ta);
try {
conn = this.mi_url.openConnection ();
conn.connect ();
ta.setText ("Conexion abierta ...");
data = new BufferedReader (new InputStreamReader (conn.getInputStream ()));
ta.setText ("Leyendo datos ...");
while ((linea = data.readLine ()) != null)
buffer.append (linea + "\n");
ta.setText (buffer.toString ());
}
catch (IOException e) {
System.out.println ("Error de I/O: " + e.getMessage ());
}
}
}

122
Figura 7-3

123

View publication stats

También podría gustarte