Вы находитесь на странице: 1из 118

Java Avanzado Programa de Tecnología en Cómputo

UNIVERSIDAD NACIONAL AUTÓNOMA DE MÉXICO

FACULTAD DE INGENIERÍA

DIVISIÓN DE INGENIERÍA ELÉCTRICA

PROGRAMA DE TECNOLOGÍA EN CÓMPUTO

MANUAL DEL CURSO: JAVA AVANZADO

AUTORES:

CAMACHO PÉREZ HUGO


CASTAÑEDA MUÑOZ RICARDO
VARGAS MEJÍA ISAAC

1
Java Avanzado Programa de Tecnología en Cómputo

Java Avanzado
Excepciones:

Errores y excepciones

Excepciones básicas.

Capturar una Excepción: palabras reservadas try- catch

Lanzar y especificar excepciones: palabras reservadas throw y throws

Crear Excepciones Propias

Sistema de E/S de Java: java.io

Concepto de flujo o stream en Java

Tipos de flujo

Flujos de bytes (Byte Streams): Clases abstractas InputStream y OutputStream

Flujos de caracteres (Character Streams): Clases abstractas Reader y Writer

Leer la entrada estandar

Obtener un flujo de caracteres a partir de uno de bytes : Clase


InputStreamReader

Clase BufferedReader

Manejo de archivos y directorios: Clase File

Escribir y leer datos de archivos: Clases FileOutputStream y


FileInputStream

Otros ejemplos básicos de aplicación de flujos

Copia de archivos a nivel de bytes

Almacenar datos leídos desde el teclado en un archivo

Serialización de Objetos

¿Qué es serializar un objeto?

2
Java Avanzado Programa de Tecnología en Cómputo

Interfaz Serialiazable

Guardar y leer estados de objetos: Clases ObjetInputStream y


ObjetOutputStream

Clases de utilidad: java.u til

Colecciones de objetos: Clase Vector

Manejo de fechas y hora

Clase Date

Clase Calendar

Clase GregorianCalendar

Separar una cadena de caracteres: Clase StringTokenizer

Comprimir y empaquetar archivos: Paquete java.util.zip

Clases ZipFile y ZipEntry

Clases ZipOutputStream y ZipInputStream

Suma de comprobación: Interface Checksum y clases Alder32 y CRC32

Clases CheckedOutputStream y CheckedInputStream

Program ación multihilo: Threads.

¿Qué es la programación multihilo?

Threads en java

Diagrama de estados de los threads

Heredar de la clase Thread y manipulación de hilos

Métodos run y start()

Prioridades

Método sleep()

Método yield()

3
Java Avanzado Programa de Tecnología en Cómputo

El productor- consumidor: Sincronización de métodos

Interfaz Runnable

Interf aces gráficas de usuario (GUI's): java.aw t y javax.swing

Justificacion de las GUI's

Contenedores (Container, JFrame)

Layout Managers, JPanel

Componentes Swing

El modelo de eventos Swing: Interfaz ActionListener

Otros componentes Swing

Menús y mensajes de alerta

Mostrar Imágenes y dibujar figuras en un contenedor: Clase Graphics

Aplicaciones embebidas en una página web: Clases Applet y JApplet

Cargar imágenes y archivos en applets

Utilizar URL en Applets.

Comput ación Distribuida: java.ne t

Conceptos básicos de la comunicación con Sockets TCP/IP

Direcciones IP y puertos

¿Qué es un socket?

Clase Socket y SocketServer

Leer y escribir en los sockets: sockets y streams

Servir a múltiples clientes: sockets y threads

4
Java Avanzado Programa de Tecnología en Cómputo

Excepciones:
Errores y excepciones

Frecuentemente al ejecutar un programa ocurren situaciones extraordinarias


que pueden dar resultados no deseados, o incluso hasta terminar
inesperadamente la ejecución del mismo. Dentro de la terminología de Java,
estas situaciones dependiendo de su seriedad son distinguidas entre errores
y excepciones.

Los errores pueden producirse tanto en tiempo de compilación como de


ejecución. Los errores en tiempo de compilación normalmente son errores de
código, que no son graves y pueden corregirse modificando el código.
Errores de este tipo pueden ser errores de sintaxis, por ejemplo.

Así mismo hay errores que surgen en tiempo de ejecución, al igual que las
excepciones que solo ocurren en esta etapa de ejecución y la diferencia
entre ambos es que los errores en tiempo de ejecución son situaciones serias
que tienen que ver con situaciones extraordinarias al programa, como puede
ser un problema con la máquina virtual, la falta de memoria, la muerte
inesperada de un Thread, etc, y por lo mismo ya no es posible recuperar la
ejecución de programa.

Una excepción es producida también al tiempo de ejecución del programa


cuando en alguna parte del programa se produce alguna condición no
contemplada, pero a diferencia de un error en tiempo de ejecución, una
excepción puede ser manejada de tal forma que el programa pueda
comportarse adecuadamente para continuar su ejecución.

Las excepciones puede originarse por ejemplo tratar de ingresar a un


elemento que está fuera de límites de un arreglo, hacer una llamada a una
clase que no encontrada, querer acceder a un archivo ya cerrado, querer
conectarse un servidor que no admita conexiones o bien que ofrezca los
permisos necesarios, etc.

Excepciones básicas.

Para Java, tanto los errores como las excepciones también son objetos que
descienden de la clase java.lang.Throwable, y dependiendo del tipo de
situación que ocurra, va a ser el tipo de error o excepción lanzada.

En Java ya existen diversas excepciones ya definidas, dentro de las que se


pueden mencionar las siguientes:

5
Java Avanzado Programa de Tecnología en Cómputo

ArithmeticException . Es lanzada cuando ocurre una condición aritmética


excepcional. Por ejemplo, intentar dividir un número entre cero.

NullPointerException . Es lanzada cuando una aplicación pretende usar un


objeto nulo, en un caso donde un objeto es requerido. Esto incluye:

• Hacer una llamada a un método de instancia de un objeto null.

• Acceder o modificar algún atributo de un objeto null.

• Tomar la longitud de un objeto null como si fuera un arreglo.

• Acceder o modificar los elementos de un objeto null como si fuera un


arreglo.

• Lanzar un objeto null como si fuera un Throwable.

ClassCastException . Lanzada para indicar que el código ha pretendido


convertir un objeto a una subclase de la cual no es una instancia. Por
ejemplo:

Object x = new Integer(0);

System.out.println((String)x)

NegativeArraySizeException. Es lanzada cuando un programa intenta crear un arreglo


con tamaño negativo

ClassNotFoundException . Ocurre cuando se intenta cargar una clase que no ha sido


encontrada.

ArrayIndexOutOfBoundsException .Se lanza para indicar que un arreglo ha sido


ingresado en un índice ilegal. El índice es negativo o bien de mayor o igual tamaño
que el arreglo.

NoSuchFieldException . Señala que la clase no tiene el atributo especificado.

ArrayStoreException. Se lanza para indicar que un intento ha sido hecho de


almacenar tipos incorrectos de objetos en un arreglo de objetos. Por ejemplo:

Object x[] = new String[3];

x[0] = new Integer(0);

Capturar una Excepción: Sentencia try-catch

6
Java Avanzado Programa de Tecnología en Cómputo

Como ya se mencionó cuando una excepción es lanzada, es posible


capturarla para posteriormente indicarle al programa que tenga un
comportamiento determinado al ocurrir citada excepción. Para poder hacer
esto se utiliza la estructura try- catch, que tiene la siguiente sintaxis:
try{
...
//código que lanza una excepción
...
}catch( excepcionLanzada1 idExcepcion1){
...
//código que se ejecutará al capturar la excepción1
...
}catch( excepcionLanzada2 idExcepcion2){
...
//código que se ejecutará al capturar la excepción2
...
}

Hay que señalar que puede haber varios bloques catch, tantos como
excepciones se requiera capturar y darles un tratamiento especial.

Por ejemplo
package excepciones;
/**
* Programa que divide dos números, sin emplear el manejo
* de excepciones
* @author PROTECO
*
*/
public class Calculadora1 {

public static void main(String args[]){


double resultado;
/*
* Si no se proporcionan argumentos por la línea de comandos
* lanzará una excepción ArrayIndexOutOfBoundsException
*/
/*
* En caso de que se proporcionaran letras por ejemplo en vez
* de números se lanzaría una NumberFormatException
*/
double numerador=Double.parseDouble(args[0]);
double denominador=Double.parseDouble(args[1]);
/*
* Si el denominador es cero lanzará una excepción de tipo
* ArithmeticException, que por defecto ya es atrapada y muestra
* como resultado de una división entre cero un valor Infinity
*/
resultado=(double)(numerador/ deno mi nador);
System.out.println("El resultado de la operación es:"+resultado);
}
}

7
Java Avanzado Programa de Tecnología en Cómputo

En el programa anterior lanza dos excepciones que serán manejadas a


continuación
package excepciones;
/**
* Programa que divide dos números, manejando
* excepciones
* @author PROTECO
*
*/
public class Calculadora2 {

public static void main(String args[]){


double resultado=0;
try{
double numerador=Double.parseDouble(args[0]);
double denominador=Double.parseDouble(args[1]);
resultado=(double)(numerador/ deno mi nador);
System.out.println("El resultado de la operación es:"+resultado);
}catch(ArrayIndexOutOfBoundsException aioobe){
/*
* Al capturar esta excepción se despliega en pantall
* un mensaje de la forma de uso del programa
*/
System.out.println("USE: java Calculadora2 numerador denominador");
}catch(NumberFormatException nfe){
/*
* Al capturar la excepción se manda a llamar
* al método getMessage() de la clase Throwable
*/
System.out.println("\nInvalid argument "+nfe.getMessage());
}
}
}

Nótese que al capturar la segunda excepción se hace una llamada al método


getMessage(). Este médoto está definido en la clase Throwable, de la cual
podemos mencionar algunos otros métodos.

String getMessage() . Regresa el mensaje detallado de la excepción lanzada.

void printStackTrace() .
Imprime la excepción lanzada y su origen detallado en la
salida estándar de error.

void printStackTrace(PrintStream s) . Realiza lo mismo que el anterior, pero


imprime la salida en el PrintStream especificado.

void printStackTrace(PrintWriter s) .
Realiza lo mismo que el anterior, pero imprime
la salida en el PrintWriter especificado.

Lanzar y especificar excepciones: palabras reservadas throw y throws

8
Java Avanzado Programa de Tecnología en Cómputo

Uno puede lanzar excepciones explícitamente mediante la palabra reservada


throw, y cuya sintaxis es

throw Exception

Por ejemplo:
package excepciones;
/**
* Programa que divide dos números, manejando
* excepciones y validando que el denominador
* no sea cero
* @author PROTECO
*
*/
public class Calculadora3 {

public static void main(String args[]){


double resultado=0;
try{
double numerador=Double.parseDouble(args[0]);
double denominador=Double.parseDouble(args[1]);
/*
*Si el denominador es cero se lanza una
*ArithmeticException mediante la palabra
* reservada throw
*/
if(denominador==0)
throw new ArithmeticException("La división entre cero no está
definida");
resultado=(double)(numerador/ deno mi nador);
System.out.println("El resultado de la operación es:"+resultado);
}catch(ArrayIndexOutOfBoundsException aioobe){
System.out.println("USE: java Calculadora3 numerador denominador");
}catch(NumberFormatException nfe){
System.out.println("\nInvalid argument"+nfe.getMessage());
}
}
}

La palabra reservada throws indica que un método puede lanzar una


excepción determinada para posteriormente ser capturada en un bloque try-
catch cuando se haga una llamada a ese método, y por lo tanto se coloca en
la firma del método seguido del nombre de la excepción, por ejemplo:
static String readln() throws IOException{
...
//código del método que puede lanzar una excepción
...
}

Crear Excepciones Propias

9
Java Avanzado Programa de Tecnología en Cómputo

Para crear una excepción propia, lo recomendable es heredar directamente


de la clase java.lang.Exception e implementar los métodos necesarios. Una
vez hecho esto podemos trabajar con nuestra excepción como se trabaja con
las predefinidas de Java, como se vio anteriormente. Por ejemplo tenemos
estas cuatro clases, en la primera se define una excepción genérica
heredando de la clase Exception.
package excepciones.maquina;
/**
* Esta clase define una excepción heredando
* la clase java.lang.Exception
* @author PROTECO
*
*/
public class MaquinaException extends Exception{
MaquinaException(String mns){
/*
* mns es el mensaje que se va a desplegar cuando
* la excepción sea lanzada
*/
super(mns);
}
}

En las dos clases siguientes se definen excepciones a partir de la excepción


genérica, así como un método que posteriormente puede ser llamado.
package excepciones.maquina;
/**
* Esta clase define una excepción a partir del a
* excepción MáquinaException
* @author PROTECO
*
*/
class FaltaProducto extends MaquinaException{
public FaltaProducto(String mensaje){
super(mensaje);
}

void causa(){
System.out.println("Carencia de producto");
}
}
package excepciones.maquina;
/**
* Esta clase define una excepción a partir del a
* excepción MáquinaException
* @author PROTECO
*
*/
class Incompleto extends MaquinaException{
public Incompleto(String mensaje){
super(mensaje);
}
/*

10
Java Avanzado Programa de Tecnología en Cómputo

* Método que puede ser llamado al atraparse la excepción


*/
void causa(){
System.out.println("La cantidad es incompleta para ese producto");
}
}

Por último en la siguiente clase se emplean las excepciones anteriormente


definidas. Nótese como se hacen llamadas a los métodos definidos en las
clases anteriores desde los bloques catch al atrapar la excepción.
package excepciones.maquina;
/**
* Crea una máquina de golosinas que realiza
* las verificaciones utilizando las excepciones
* previamente definidas
* @author PROTECO
*
*/
public class MaquinaGolosinas {
private int codProducto;
private int[] Precios = { 5, 15, 5, 20, 8, 10 };
private int dineroDep;
private int[] productos = { 101, 102, 103, 205, 206, 207 };
private int[] existencia = { 5, 7, 10, 1, 2, 0 };

public int seleccionaProducto(int codigo, int diner) {


codProducto = codigo;
dineroDep = diner;
for (int i = 0; i < 6; i++)
if (productos[i] == codigo)
return i;
return 6;
}

public void verificarPrecio(int n) throws Incompleto {


if (dineroDep != Precios[n])
throw (new Incompleto("Al Verificar Deposito"));
else
System.out.println("Tome Su producto");
}

public void verificarExistencia(int n) throws FaltaProducto {


if (existencia[n] == 0)
throw (new FaltaProducto("Al Verificar Existencia"));
else
try {
verificarPrecio(n);
} catch (Incompleto In) {
In.causa();
}
}
public static void main(String args[]) {
MaquinaGolosinas mac = new MaquinaGolosinas();
int tempCod = Integer.parseInt(args[0]);
int tempDiner = Integer.parseInt(args[1]);

11
Java Avanzado Programa de Tecnología en Cómputo

int l;
l = mac.seleccionaProducto(tempCod, tempDiner);
try {
mac.verificarExistencia(l);
} catch (FaltaProducto fP) {
System.out.println(fP.toString() + " Dinero devuelto ");
fP.causa();
}
}
}

12
Java Avanzado Programa de Tecnología en Cómputo

Sistema de entrada/salida de Java: java.io


Concepto de flujo o stream en Java

Para java, cualquier operación de entrada/salida se maneja a través de una


abstracción del tránsito de información, la cual es conocida como un flujo o
stream. Es decir cuando nosotros queremos leer un archivo, un puerto, un
dispositivo, etc., se abre un flujo de entrada o salida, y esos datos son
introducidos en ese flujo, para posteriormente ser procesados por nuestro
programa en java.

Podemos ejemplificar lo anterior por medio del siguiente diagrama.

Tipos de flujos en java

Podemos distinguir básicamente dos tipos de flujos: Los flujos de bytes, y los
flujos de caracteres.

Flujos de bytes . Nos proporciona un medio adecuado para el manejo de


entradas y salidas de bytes y su uso lógicamente está orientado a la lectura
y escritura de datos binarios.

El tratamiento del flujo de bytes viene gobernado por dos clases abstractas
que son InputStream y OutputStream. Cada una de estas clases abstractas
tienen varias subclases concretas que controlan las diferencias ente los
distintos dispositivos de I/O que se pueden utilizar. Así mismo, estas dos
clases son las que definen los métodos que sus subclases tendrán
implementados y, de entre todas, destacan las clases read() y write() que
leen y escriben bytes de datos respectivamente.

Flujos de caracteres. Proporciona un medio conveniente para el manejo de


entradas y salidas de caracteres. Dichos flujos usan codificación Unicode y,
por tanto, se pueden internacionalizar.

Este es un modo que Java nos proporciona para manejar caracteres pero al
nivel más bajo todas las operaciones de I/O son orientadas a byte.

Al igual que la anterior el flujo de caracteres también viene gobernado por


dos clases abstractas: Reader y Writer. Dichas clases manejan flujos de
caracteres Unicode. Y también de ellas derivan subclases concretas que
implementan los métodos definidos en ellas siendo los más destacados los
métodos read() y write() que, en este caso, leen y escriben caracteres de
datos respectivamente.

13
Java Avanzado Programa de Tecnología en Cómputo

Leer la entrada estandar

Para poder leer datos desde la entrada estándar es necesario primero revisar
un poco la clase System, la cual es conocida por el System.out.println, que
nos permite escribir una cadena de caracteres en la pantalla.

Al revisar la documentación de la clase System, podemos ver lo siguiente:

Se puede observar que el atributo out de la clase System, es un objeto de la


clase PrintStream, que es una subclase de OutputStream, es decir un flujo de
salida de bytes, que mediante el método println() de la clase PrintStream,
nos permite escribir una cadena de caracteres en el flujo.

Tambien se puede observar que existen otros dos atributos además del
atributo out, dentro de la clase System, esto es el atributo err, que
representa la salida estandar de error, que es tambien un PrintStream y que
por lo tanto tiene el mismo comportamiento que el atributo out. Y además
está también el atributo in que nos representa la estrada estándar, esto es el
teclado.

Por lo consultado en la documentación, ahora sabemos que la entrada


estandar está representada por el atributo in de la clase System, y que
además es un objeto de la clase InputStream, que a su vez representa un
flujo de entrada de bytes. Esto es un poco inconveniente si lo que
deseablemente se esperaría es leer carateres. Para solucionar podemos
utilizar un objeto de la clase InputStreamReader, subclase de la clase
Reader, que proporciona un flujo de caracteres, a partir de un flujo de bytes.

Con el empleo de la clase InputStreamReader ahora tenemos nuestro flujo de


caracteres, pero con esto surge otro detalle, pues al leer datos desde el
teclado usualmente lo que uno proporciona son cadenas de caracteres que
terminan con un fin de línea, que puede ser un retorno de carro, una
interlínea, etc. Entonces el asunto ahora es almacenar de alguna forma esos
caracteres hasta que llegue el final de línea. Para esto podemos utilizar la
clase BufferedReader, que proporciona una manera de almacenar caracteres,
además de métodos para leer tanto caracteres individuales como líneas de
texto, mediante los métodos read() y readLine().

14
Java Avanzado Programa de Tecnología en Cómputo

Con esto se podría escribir un código que resuelva el problema, y puede


quedar como el siguiente:
package io;
import java.io.*;
/**
* Lee desde la entrada estándar y escribe el mensaje en la salida estándar,
* mientras el mensaje sea diferente de "adios"
* @author Ricardo Castañeda M.
*
*/
public class LeeTeclado{
static String readln() throws IOException{
InputStream is=System.in;
InputStreamReader isr= new InputStreamReader(is);
BufferedReader br=new BufferedReader(isr);
/* Lo anterior puede quedar en una sola línea
* BufferedReader br=new BufferedReader(new
* InputStreamReader(System.in));
*/
return br.readLine();
}

public static void main(String args[]){


String mensaje="";
PrintStream out=System.out;
do{
try{
mensaje=LeeTeclado.readln();
}catch(IOException ioe){
out.println(ioe.getMessage());
}
out.println("Lo que escribiste: "+mensaje);
}while(!mensaje.equals("adios"));
}
}

Cabe señalar que el método readLine() de la clase BufferedReader lanza una


excepción de tipo IOException que tendrá que ser tratada en algún momento
del código.

Manejo de archivos y directorios: La clase File

15
Java Avanzado Programa de Tecnología en Cómputo

La clase File es la representación de java para todos los elementos de


nuestro sistema de archivos, llámense directorios, archivos, programas, etc.,
y nos permite hacer referencia a ellos, obtener información, incluso modificar
su estado. Por ejemplo dentro de la clase File tenemos varios métodos
interesantes que por el mismo nombre se intuye su funcionamiento, y se
pueden observar mediante el siguiente ejemplo
package io;
import java.io.*;
import java.util.Date;

/**
* Obtiene información de un archivo, y en caso de ser un directorio,
* lista el contenido del mismo y obtiene información de los archivos
* contenidos en el mismo
*
* @author Ricardo Castañeda / PROTECO
*/
public class EjemploFile {
File f;
public EjemploFile(String path) {
f=new File(path);
if(f.exists()){
System.out.println("\nNom bre del archivo: " + f.getName());
System.out.println("\tTamaño: " + f.length());
System.out.println("\tRuta absoluta: " + f.getAbsolutePath());
System.out.println("\tPuede leerse: " + f.canRead());
System.out.println("\tPuede editarse: " + f.canWrite());
System.out.println("\tEs archivo regular: " + f.isFile());
System.out.println("\tEs oculto: " + f.isHidden());
System.out.println("\tModificado por última vez: " + new Date(
f.lastModified()));
System.out.println("\tEs directorio: " + f.isDirectory());
if(f.isDirectory()){
System.out.println("\n- --------Contenido del directorio:
"+path+"- -------");
/*
*El método list() devuelve un arreglo con el contenido de un
*/directorio
String [] lista=f.list();
for(int i=0;i<lista.length;i++){
new EjemploFile(path+"\ \"+lista[i]);
}
}
}else{
System.out.println("El "+path+" archivo no existe");
}
}
public static void main(String[] args) {
EjemploFile ejemplofile = new EjemploFile(args[0]);
}
}

Escribir y leer datos de archivos: Clases FileOutputStream y FileInputStream

16
Java Avanzado Programa de Tecnología en Cómputo

Retomando lo anterior, mediante la clase File tenemos una representación


eficiente de los archivos para manejarlos con java, sin embargo, hasta ahora
no podemos ni escribir ni leer datos de estos archivos, debido a que el
tránsito de datos es un flujo, y para java se tiene que tratar como tal. Para
este caso, existen dos clases que permiten manejar flujos específicamente
de entrada y salida de archivos, las cuáles son:

FileOutputStream, que hereda directamente de la clase OutputStream, por lo


cual nos provee de un flujo de bytes de salida hacia el archivo, esto es que
mediante esta clase podemos escribir bytes directamente en el archivo
mediante el método write().

FileInputStream, subclase de la clase InputStream, que proporciona un flujo


de bytes desde el archivo, por lo cual nos va a permitir leer datos desde el
archivo a nivel de bytes, a través del método read().

Para construir un objeto de cualquiera de estas dos clases, se tienen varios


métodos constructores que se especifican a continuación.

De los cuales los que abordaremos son aquellos donde se especifica un


objeto de la clase File, que nos representa nuestro archivo en el sistema de
archivos.

En cuanto a la clase FileDescriptor, solo se dirá que los objetos de esta clase
son una estructuras que nos permite hacer referencia a un archivo abierto,
un socket, un pipe, o alguna otra fuente de bytes.

17
Java Avanzado Programa de Tecnología en Cómputo

Cabe destacar que los constructores de la clase FileOutputStream que


requieren un dato bolean generan un flujo de entrada al archivo, el cual va a
escribir los datos al final del archivo, a diferencia que si se utilizara el
constructor de un solo parámetro, el cual sobrescribiría el archivo en su
totalidad.

Para ejemplificar el uso de flujos de entrada y salida en archivos, se propone


realizar un programa en java que lea el contenido de un archivo de texto y lo
muestre en pantalla. Para esto primero se crea un objeto de la clase File, con
la ruta especificada, para posteriormente construir un FileInputStream, que
nos permitirá leer el contenido del archivo.

Dado que un FileInputStream es un flujo de bytes, será necesario obtener a


partir de él un flujo de caracteres mediante la clase InputStreamReader y
posteriormente almacenar estos caracteres en un BufferedReader, para así
poder leer lineas, como lo hicimos para la lectura del teclado.

Por último solo hay que imprimir las cadenas obtenidas en la salida estándar.
package io;
import java.io.*;

/**
* Lee el archivo especificado por la línea de comandos y abre un
* flujo a nivel de bytes, que posteriormente es convertido en un flujo
* de caracteres para poder ser impreso en pantalla
*/
public class LeeArchivo {
public static void main(String[] args) {
try{
File f=new File(args[0]);
FileInputStream fis=new FileInputStream(f);
InputStreamReader isr=new InputStreamReader(fis);
BufferedReader br=new BufferedReader(isr);
String linea;
while((linea=br.readLine())!=null){
System.out.println(linea);
}
br.close();
isr.close();
fis.close();
}catch(FileNotFoundException fnfe){
System.out.println("No fue posible encontrar el archivo:
"+args[0]);
}catch(IOException ioe){
System.out.println(ioe.getMessage());
}catch(IndexOutOfBoundsException ioobe){
System.out.println("USO: java LeeArchivo nombreArchivo");
}
}
}

18
Java Avanzado Programa de Tecnología en Cómputo

Otros ejemplos básicos de aplicación de flujos

Copia de archivos a nivel de bytes

La ventaja principal de realizar una copia de archivos a nivel de bytes es que


es posible copiar cualquier tipo de archivo, ya sea una imagen, un programa,
etc., ya que si se lleva a cabo a nivel de caracteres, solo aplicaría para
archivos de texto.

El algoritmo del programa es el siguiente:

• Indicar el archivo de origen y abrir su flujo de entrada desde el archivo.

• El archivo de destino y abrir su flujo de salida hacia el archivo.

• Entrar en un ciclo donde se este leyendo el flujo de entrada desde el


archivo de origen, hasta que se llegue al final del archivo, y mientras
tanto estar escribiendo en el flujo hacia el archivo de destino.

• Cerrar los flujos y capturar las posibles excepciones.


package io;
import java.io.*;
/**
* Copia el contenido del archivo de origen en el archivo de destino
* @author PROTECO
*
*/
public class CopiaArchivo {
public void copiar(String Origen,String Destino) throws IOException {
File destino= new File(Destino);
FileOutputStream fos=new FileOutputStream(destino);
File origen= new File(Origen);
FileInputStream fis=new FileInputStream(origen);
int c;
while((c=fis.read())!=-1)
fos.write(c);
fis.close();
fos.close();
}
public static void main(String[] args) {
try {
CopiaArchivo cp=new CopiaArchivo();
cp.copiar(args[0],args[1]);
System.out.println("Archivo copiado con exito");
} catch(IOException e) {
System.out.println(e);
System.out.println(e.getMessage());
} catch(ArrayIndexOutOfBoundsException aioobe) {
System.out.println("Forma de uso: java CopiaArchivo origen destino");
}
}

19
Java Avanzado Programa de Tecnología en Cómputo

}
Almacenar datos leídos desde el teclado en un archivo

Este ejemplo es una combinación del código que hace la lectura del teclado,
y del anterior, con la diferencia de que ahora el flujo de salida hacia el
archivo se trabajará a nivel de caracteres. Esto se logra mediante el uso de
la clase PrintStream, que por medio de su método println(), nos permite
escribir líneas de caracteres, que obtendremos mediante la lectura del
teclado.
package io;
import java.io.*;
/**
* Este programa leerá líneas desde el teclado
* y las guardará en el archivo especificado
* hasta que la línea solo tenga la palabra
* "adios"
* @author Ricardo J. Castañeda M. / PROTECO
*
*/
public class TecladoArchivo {
public static String leeTeclado() throws IOException{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
return br.readLine();
}
public static void main(String[] args){
String cadena;
File archivo;
FileOutputStream fos;
PrintStream ps;
try{
archivo=new File(args[0]);
/*
* Este flujo escribirá caracteres al final del
* archivo en vez de sobrescribir su contenido
*/
fos=new FileOutputStream(archivo, true);
/*
*Con este PrintStream se escribirán líneas de
*caracteres en el flujo de salida hacia el
*archivo
*/
ps=new PrintStream(fos);
do{
cadena=TecladoArchivo.leeTeclado();
ps.println(cadena);
}while(!cadena.equals("adios"));
fos.close();
ps.close();
}catch(IOException ioe){
System.out.println(ioe.getMessage());
}catch(Exception e){
System.out.println(e.getMessage());
}

20
Java Avanzado Programa de Tecnología en Cómputo

}
}

Serialización de objetos

¿Qué es serializar un objeto?

La serialización de un objeto consiste en obtener una secuencia de bytes que


represente el estado de dicho objeto. Esta secuencia puede utilizarse de
varias maneras (puede enviarse a través de la un socket, guardarse en un
archivo para su uso posterior, utilizarse para recomponer el objeto original,
etc.).

El estado de un objeto viene dado, básicamente, por el estado de sus


campos. Así, serializar un objeto consiste, básicamente, en guardar el estado
de sus campos. Si el objeto a serializar tiene campos que a su vez son
objetos, habrá que serializarlos primero. En ocasiones puede interesar que
un atributo concreto de un objeto no sea serializado. Esto se puede conseguir
utilizando el modificador transient, que informa a la JVM de que no nos
interesa mantener el valor de ese atributo para serializarlo o hacerlo
persistente, por ejemplo:
public class MiFecha{
public int n;
public Date fecha;
public transient long s;
...
}

Para este caso, los atributos n y fecha serán incluidos en la secuencia de


bytes resultante de serializar un objeto de clase MiFecha. El atributo s no
será incluido, por tener el modificador transient.

Interfaz Serializable

Un objeto serializable es un objeto que se puede convertir en una secuencia


de bytes. Para que un objeto sea serializable, debe implementar la interfaz
java.io.Serializable. Esta interfaz no define ningún método. Simplemente se
utiliza para indicar aquellas clases cuyas instancias pueden ser convertidas a
secuencias de bytes y posteriormente reconstruidas. Ejemplo:
package io.serializacion;
import java.util.Date;
import java.io.Serializable;
/**
* Esta clase puede ser serializada
* @author PROTECO
*
*/
public class Alumno implements Serializable{
String nombre;

21
Java Avanzado Programa de Tecnología en Cómputo

String carrera;
int semestre;
Date registro;
/*
* El estado de este atributo no se guardará debido al
* modificacor transient
*/
transient long milis;

public Alumno(String nombre, String carrera, int semestre){


this.nombre=nombre;
this.carrera=carrera;
this.semestre=semestre;
this.registro=new Date();
this.milis=this.registro.getTime();
}

public void decirNombre(){


System.out.println("Mi nombre es:"+nombre);
/*
* Comprobando que milis tiene un valor antes de la
* serialización
*/
System.out.println("La hora en milisegundos es:"+milis);
}
}
Guardar y leer estados de objetos: Clases ObjectInputStream y ObjectOutputStream

Para poder serializar un objeto, se debe construir primero un


ObjectOutputStream sobre otro flujo de tipo OutputStream, por ejemplo un
FileOutputStream. Este caso en específico guardará el estado del objeto en el
archivo especificado al crear el FileOutputStream.

Una vez construido el flujo, solo restará utilizar el método writeObject() de la


clase ObjectOutputStream, para llevar a cabo la serialización del objeto.
package io.serializacion;
import java.io.*;
/**
* Este programa lleva a cabo la serialización de un objeto de la
* clase Alumno y guarda su estado en el archivo alumno.out
* @author PROTECO
*
*/
public class Escuela{
public static void main(String[] args) throws IOException{
Alumno a1= new Alumno(args[0], args[1], Integer.parseInt(args[2]));
a1.decirNombre();
ObjectOutputStream salida= new ObjectOutputStream(new FileOutputStream
("alumno.out"));
/*
*Escribiendo un objeto de la clase String
*/
salida.writeObject("Almacenamiento Alumno");
/*

22
Java Avanzado Programa de Tecnología en Cómputo

*Escribiendo un objeto de la clase Alumno


*/
salida.writeObject(a1);
salida.close();
}
}

Ahora, para poder recuperar el objeto serializado y a partir de ello


reconstruirlo con el estado en que se guardó, se lleva a cabo un proceso dual
al anteriormente mencionado, pues primero se debe de abrir un flujo de
entrada desde el archivo, o bien un FileInputStream, mismo que servirá para
construir el ObjectInputStream que nos permitirá recuperar nuestro objeto de
la clase Alumno, por medio del método readObject de la clase
ObjectInputStream.
package io.serializacion;
import java.io.*;
/**
* Este programa recupera el objeto de la clase alumno
* guardado en el archivo alumno.out
* @author PROTECO
*
*/
public class Escuela2{
public static void main(String[] args) throws ClassNotFoundException, IOException{
ObjectInputStream entrada= new ObjectInputStream(
new FileInputStream("alumno.out"));
/*
* Recuperando el objeto de tipo String
* Por cuestiones de polimorfismo se realiza
* un cast al objeto Object regresado por el
* método readObject()
*/
String s=(String)entrada.readObject();
/*
* Recuperando el objeto de tipo Alumno
*/
Alumno a2=(Alumno)entrada.readObject();
System.out.println(s);
a2.decirNombre();
System.out.println("Carrera: "+a2.carrera);
System.out.println("Semestre: "+a2.semestre);
System.out.println("Registro: "+a2.registro);
/*
* Este valor debe de ser cero o nulo debido al
* modificador transient
*/
System.out.println("Milis: "+a2.milis);
entrada.close();
}
}

23
Java Avanzado Programa de Tecnología en Cómputo

El stream ObjectInputStream implementa la interfaz DataInput que define


métodos para leer tipos de datos primitivos. Los métodos de DataInput son
paralelos a los definidos en DataOutput para escribir tipos de datos
primitivos. Entre ellos se incluyen readInt, readFloat, y readUTF. Se usan
estos métodos para manejar tipos de datos primitivos con un
ObjectInputStream o bien un ObjectOutputStream que implementa la interfaz
DataOutput, que define métodos como writeByte, writeFloat, writeUTF, etc.

24
Java Avanzado Programa de Tecnología en Cómputo

Clases de utilidad: java.util


Agrupación de objetos: Clase Vector

La clase Vector implementa un arreglo de objetos de tamaño variable. Como


un arreglo, contiene componentes que pueden ser accedidos por medio de
un índice entero. Así mismo el tamaño de un Vector puede crecer o disminuir
como se necesite, agregando o removiendo objetos después que el vector ha
sido creado.

Para construir un objeto de la clase Vector se pueden considerar los


siguientes constructores especificados en la documentación

Donde el primer contructor define un Vector con un tamaño de 10


elementos, y una capacidad de incremento de 0.

El segundo constructor define un Vector con una capacidad inicial y una


capacidad de incremento definidas por los parámetros.

La capacidad de incremento en un Vector es el número por el cual se


incrementa la capacidad del Vector al desbordarse.

La clase Vector ofrece diversos métodos para poder manejar sus elementos.
Entre ellos se pueden listar los siguientes:

void add(E o) . Agrega al final del Vector el elemento especificado

void add(int index , E o). Agrega el elemento especificado en la posición indicada


como parámetro.

Agrega el elemento especificado al final del Vector,


void addElement(E o).
incrementando en uno su tamaño

Object get(int index) . Regresa el elemento especificado por la posición en el


Vector

Busca la primera ocurrencia del objeto especificado,


int indexOf(Object o).
regresando la posición de este dentro del Vector.

25
Java Avanzado Programa de Tecnología en Cómputo

boolean isEmpty() . Prueba si el Vector no tiene componentes.

boolean remove(int index) . Remueve el elemento especificado.

boolean remove(Object o). Remueve la primera ocurrencia del objeto


especificado.

E set(int index, E element) .


Reemplaza el elemento especificado con el índice, por
el elemento especificado.

int size(). Regresa el número de elementos del Vector.

int capacity() . Regresa la capacidad del Vector.

void trimToSize() . Modifica la capacidad del Vector al tamaño actual del Vector.

En el siguiente programa se ejemplifican algunos de estos métodos.


package util;
import java.util.Vector;

/**
* Esta clase ejemplifica los métodos de la clase
* java.util.Vector
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class MetodosVector {
/**
* Recorre el vector y manda a llamar a un
* metodo de la clase Alumno
* @param v Se le proporciona un vector de alumnos
*/
public static void recorreVector(Vector v){
Alumno tmp;
for(int i=0;i<v.size();i++){
System.out.println("\nElemento "+i);
/*
* Dado que el metodo get de la clase
* Vector regresa un elemento, se debe realizar
* un cast para especificar que tipo de objeto
* es el que regresa.
*/
tmp=(Alumno)v.get(i);
tmp.decirDatos();
}
}
/**
* Remueve los elementos cuyo indice sea un numero
* par
* @param v Se le proporciona un vector de alumnos
*/

26
Java Avanzado Programa de Tecnología en Cómputo

public static void remuevePares(Vector v){


for(int i=0;i<v.size();i++){
if((i%2)==0){
/*
* El metodo remove, eliminará objetos del
* vector
*/
v.remove(i);
}
}
}
public static void main(String args[]){
/*
* Se declara un vector de objetos de la clase Alumno
*/
Vector vectorAlumnos=new Vector(0,1);
/*
* Se agregan 10 alumnos al Vector
*/
vectorAlumnos.addElement(new Alumno("Ricardo Castañeda Muñoz",
"300216785","Ing en Computación"));
vectorAlumnos.addElement(new Alumno("Silvia Navarro Sánchez",
"300216786","Ing en Minas"));
vectorAlumnos.addElement(new Alumno("Sandra Fragoso Jimenez",
"300216787","Ing Eléctrica Electrónica"));
vectorAlumnos.addElement(new Alumno("Nidia Palacios Nuñez",
"300216788","Ing Petrolera"));
vectorAlumnos.addElement(new Alumno("Aura López Suarez",
"300216789","Ing Civil"));
vectorAlumnos.addElement(new Alumno("Alma Marcela Silva Alegría",
"300216780","Ing Geofísica"));
vectorAlumnos.addElement(new Alumno("Ana Dávalos Bustos",
"300216781","Ing Mecánica"));
vectorAlumnos.addElement(new Alumno("Sonia Tovar Lopez ",
"300216782","Psicología"));
vectorAlumnos.addElement(new Alumno("Luz Salazar Castro",
"300216783","Comunicaciones"));
vectorAlumnos.addElement(new Alumno("Ulises Cárdenas",
"300216784","Química"));
/*
* Se verifica que el vector no es vacío mediante
* el metodo isEmpty()
*/
System.out.println("Es vacio: "+vectorAlumnos.isEmpty());
/*
* Se manda a llamar al metodo que recorre los elementos del
* vector e imprime datos de los mismo elementos.
* Nótese como mediante la referencia al vector es
* posible manipular toda la colección de alumnos
*/
MetodosVector.recorreVector(vectorAlumnos);
/*
* Se verifica tanto la capacidad y el numero
* de elementos en el Vector
*/
System.out.println("\nCapacidad del vector: "+

27
Java Avanzado Programa de Tecnología en Cómputo

vectorAlumnos.capacity());
System.out.println("Elementos del vector: "+vectorAlumnos.size());
MetodosVector.remuevePares(vectorAlumnos);
MetodosVector.recorreVector(vectorAlumnos);
System.out.println("\nCapacidad del vector: "+
vectorAlumnos.capacity());
System.out.println("Elementos del vector: "+vectorAlumnos.size());
/*
* Se aplica el metodo trimToSize para reducir la
* capacidad del vector a que sea igual al numero de
* elementos que contiene
*/
vectorAlumnos.trimToSize();
System.out.println("\nDespués de aplicar el método trimTosize()");
System.out.println("Capacidad del vector: "+
vectorAlumnos.capacity());
System.out.println("Elementos del vector: "+vectorAlumnos.size());

}
}
/**
* Servirá como clase auxiliar para el ejemplo del vector
* @author Ricardo Castañeda M. / PROTECO
*
*/
class Alumno{
private String nombre;
private String numCuenta;
private String carrera;
public Alumno(String nombre, String numCuenta,String carrera){
this.nombre=nombre;
this.numCuenta=numCuenta;
this.carrera=carrera;
}
public void decirDatos(){
System.out.println("\tMi nombre es: "+nombre);
System.out.println("\tMi numero de cuenta es: "+numCuenta);
System.out.println("\tMi carrera es: "+carrera);
}
}

Una de las principales utilidades de la clase Vector es que nos da la


posibilidad de agrupar objetos sin necesidad de que sean de la misma clase,
dado que maneja todo como instancias de la clase Object, que como se
recordará es la clase padre de todas las clases.

Manejo de fechas: Clases Date, Calendar y GregorianCalendar

Clase Date

La clase Date nos da la representación de una fecha y una hora, así mismo
proporciona métodos para el manejo de fechas y horas. Sin embargo debido
a que se basa en el formato de fecha americano, no permitía el uso de

28
Java Avanzado Programa de Tecnología en Cómputo

fechas de otros países. Por esta razón a partir del JDK1.1 se emplea la clase
abstracta Calendar, para proporcionar la internacionalización de fechas, por
lo cual la mayoría de métodos de la clase Date son obsoletos, con excepción
de los siguientes:

Para construir un objeto de la clase Date podemos utilizar alguno de los


siguientes constructores

El primer constructor, crea un objeto de tipo Date que representa la fecha y


hora del instante en que fue creado.

El segundo nos construye un objeto Date y lo inicializa para representar el


número especificado de milisegundos desde el 1 de enero de 1970, a las
00:00:00 GMT.

Algunos métodos importantes de la clase Date son los siguientes

int compareTo(Date anotherDate) . Compara si dos fechas por ordenamiento.

boolean after(Date when). Prueba si esta fecha es anterior a la fecha


especificada.

boolean before(Date when). Prueba si esta fecha es posterior a la fecha


especificada.

Regresa el número de milisegundos a partir del 1 de enero de


long getTime().
1970 a las 00:00:00 GMT, que el objeto representa.

Clase Calendar

Como ya se mencionó debido a los problemas de internacionalización de la


clase Date, para el manejo de fechas se desarrolló la clase abstracta
Calendar, que nos provee de métodos para hacer conversiones entre un
instante de tiempo y los calendar fields, o bien los atributos de la clase
Calendar que son atributos de clase y constantes finales enteras, que son los
que nos van a permitir el control de fechas y horas.

Entre los calendar fields podemos mencionar los siguientes:

29
Java Avanzado Programa de Tecnología en Cómputo

• El atributo int AM_PM puede tomar dos valores: las constantes enteras
AM y PM

• El atributo int DAY_OF_WEEK que puede tomar los valores SUNDAY,


MONDAY, TUESDAY,WENDSDAY,THUSRDAY,FRIDAY y SATURDAY.

• El atributo MONTH, que puede tomar los valores JANUARY, FEBRUARY,


MARCH, APRIL, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER,
NOVEMBER, y DECEMBER, o bien se pueden dar los valores
correspondientes que son números enteros del 0 al 11, donde JANUARY
es el 0 y DECEMBER corresponde al 11.

• El atributo HOUR indica la hora de la mañana o la tarde en relojes de


12 horas, mientras que el atributo HOUR_OF_DAY indica la hora en
relojes de 24 horas. Cabe señalar que para java, las horas se
representan por enteros del 0 al 23 para la hora, y del 0 al 59 para los
minutos y los segundos en un formato hh:mm:ss.

• Complementando los atributos anteriores tenemos ERA, YEAR, MINUTE,


SECOND, MILLISECOND.

• Otros atributos son DAY_OF_WEEK, DAY_OF_WEEK_IN_MONTH,


DAY_OF_MONTH (o bien DATE), DAY_OF_YEAR, WEEK_OF_MONTH,
WEEK_OF_YEAR.

• Los atributos ZONE_OFFSET y DST_OFFSET indican la zona horaria, y el


desfase en milisegundos respecto al GMT.

La clase Calendar provee de un método de clase, getInstance() , para obtener


un objeto de uso general. El método getInstance() regresa un objeto Calendar
cuyos atributos han sido inicializados con los valores actuales de fecha y
hora.

Un instante de tiempo está representado por un valor en milisegundos


medidos a partir del 1 de enero de 1970, a las 00:00:00 GMT.

Los atributos de la clase Calendar pueden obtenerse por medio del método
int get(int field).

Ejemplo:
package util;
import java.util.*;
/**
* Esta clase ejemplifica el funcionamiento

30
Java Avanzado Programa de Tecnología en Cómputo

* básico de la clase Calendar


* @author Ricardo Castañeda M. / PROTECO
*
*/
public class CalendarFields {
public static void main(String[] args) {
/*
* Se obtiene un objeto Calendar con
* los valores de fecha y hora actuales
*/
Calendar cal=Calendar.getInstance();
/*
* Se utiliza el método get para obtener los valores
* de los atributos de la clase Calendar.
*
*/
System.out.println("Era: "+cal.get(Calendar.ERA));
System.out.println("Mes: "+cal.get(Calendar.MONTH));
System.out.println("Año: "+cal.get(Calendar.YEAR));
System.out.println("Hora: "+cal.get(Calendar.HOUR));
System.out.println("AmPm: "+cal.get(Calendar.AM_PM));
System.out.println("Hora del dia "+cal.get(Calendar.HOUR_OF_DAY));
System.out.println("Minuto: "+cal.get(Calendar.MINUTE));
System.out.println("Segundo: "+cal.get(Calendar.SECOND));
System.out.println("Milisegundo: "+cal.get(Calendar.MILLISECOND));
System.out.println("Dia de la semana: "+cal.get(Calendar.DAY_OF_WEEK));
System.out.println("Dia del mes: "+cal.get(Calendar.DAY_OF_MONTH));
System.out.println("Dia del año: "+cal.get(Calendar.DAY_OF_YEAR));
System.out.println("Dia dede la semana en el mes: "+cal.get
(Calendar.DAY_OF_WEEK_IN_MONTH));
}
}

Los atributos previamente mencionados pueden cambiarse por medio de los


métodos set y add.

El método set() es un método sobrecargado que nos pérmite modificar el


valor de uno o varios atributos, por los valores especificados, dependiendo
del método set() empleado.

El método add(int field, int amount) , agrega o sustrae la cantidad de tiempo,


basado en la reglas del calendario. Este método es útil para realizar
operaciones con fechas y horas.

31
Java Avanzado Programa de Tecnología en Cómputo

Además es conveniente mencionar que pueden obtenerse objetos de la clase


Date a partir de un Calendar, y utilizar un objeto Date para asignar valores a
los Calendar Fields, mediante los métodos Date getTime() y void setTime(Date
date) . Dado que todas las fechas están dadas por un valor en milisegundos,
se tienen los métodos long getTimeInMillis() y void setTimeInMillis(long millis) , que
nos permiten trabajar directamente con los milisegundos que representan la
fecha y hora de interés.
package util;
import java.util.*;
/**
* Esta clase valida que una fecha esté en un intervalo
* de 5 dias mas o menos que la fecha actual
* @author Ricardo Castañeda M / PROTECO
*
*/
public class ValidaFecha {
/**
* Construye un objeto de tipo Date a partir de un
* objeto Calendar y los parámetros proporcionados
* @param dia
* @param mes
* @param anno
* @return El tipo Date que representa la fecha de interés
*/
public Date construyeFecha(int dia,int mes, int anno){
Calendar c=Calendar.getInstance();
c.set(anno, mes, dia);
/*
* El método getTime() devuelve la representación
* Date de un objeto Calendar
*/
return c.getTime();
}
public Date limSuperior(Calendar current){
Calendar lim=Calendar.getInstance();
/*
* Se inicializan los valores del Calendar que
* servirá como límite superior, a partir del Calendar
* proporcionado y los métodos que trabajan con milisegundos
*/
lim.setTimeInMillis(current.getTimeInMillis());
/*
* Con el método add se agregan 5 dias a la fecha
* actual obteniendo así el límite superior
*/
lim.add(Calendar.DATE, 5);
System.out.println("limiteSuperior :"+lim.getTime());
return lim.getTime();
}
public Date limInferior(Calendar current){
Calendar lim=Calendar.getInstance();
/*
* Se inicializan los valores del Calendar que
* servirá como límite superior, a partir del Calendar

32
Java Avanzado Programa de Tecnología en Cómputo

* proporcionado y los métodos que trabajan con objetos


* Date
*/
lim.setTime(current.getTime());
/*
* Con el método add se sustraen 5 dias a la fecha
* actual obteniendo así el límite inferior
*/
lim.add(Calendar.DATE, -5);
System.out.println("limiteInferior: "+lim.getTime());
return lim.getTime();
}
public boolean validaFecha(Date date){
/*
* Se obtine el objeto Calendar con la fecha y hora
* actuales
*/
Calendar c=Calendar.getInstance();
System.out.println("Fecha actual: "+c.getTime());
/*
* Se obtienen los límites a partir del objeto Calendar
* con la fecha y hora actuales
*/
Date limSup=limSuperior(c);
Date limInf=limInferior(c);
boolean band;
System.out.println("despues "+date.after(limInf));
System.out.println("antes "+date.before(limSup));
/*
* Se verifica que la fecha actual sea posterior
* al límite inferior e anterior al límite superior
* para comprobar su validez, mediante los métodos
* after y before de la clase Date
*/
band=date.after(limInf)&&date.before(limSup);
return band;
}
public static void main(String[] args) {
try{
int dia,mes,anno;
/*
* Se llama al método parseInt de la clase
* Integer para hacer la conversión de String a
* primitivos int. Nótese como al mes se le resta
* una unidad para el mes corresponda a la notación
* de java para los meses.
*/
dia=Integer.parseInt(args[0]);
mes=Integer.parseInt(args[1])-1;
anno=Integer.parseInt(args[2]);
ValidaFecha vf=new ValidaFecha();
Date fecha=vf.construyeFecha(dia, mes, anno);
System.out.println("Fecha a validar: "+fecha);
System.out.println("\n¿La fecha es válida?:
"+vf.validaFecha(fecha));
}catch(ArrayIndexOutOfBoundsException aioobe){

33
Java Avanzado Programa de Tecnología en Cómputo

System.out.println("USO: java validaFecha dia mes año");


}

}
}
Clase GregorianCalendar

La clase GregorianCalendar es la subclase concreta de la clase Calendar que


provee un sistema de calendario estándar para la mayor parte del mundo.
Dado que es una subclase hereda todos lo métodos y atributos
anteriormente comentados, de la clase Calendar, y su funcionamiento es
muy semejante.

Una de las principales ventajas de la clase GregorianCalendar es la manera


de construir fechas, por medio de los constructores que se definen, a
diferencia de la clase Calendar, que utilizábamos el método set para definir
estos valores.

Por medio del primer constructor podemos obtener un GregorianCalendar


con los valores de fecha y hora actuales, de manera semejante a como con
el método getInstance() de la clase Calendar. Además con los siguientes tres
constructores podemos directamente definir valores para los Calendar Fields,
dependiendo del constructor empleado.

Separar una cadena de caracteres: Clase StringTokenizer

Usualmente dentro de la programación, es necesario separar una cadena de


caracteres en cadenas más pequeñas o tokens delimitadas por un carácter
específico, comocido como delimitador de campo por ejemplo si se tiene una
cadena que diga "Esta es una prueba y se quiere separar en tokens, cuyo
delimitador sea el espacio, el resultado serían cuatro tokens
correspondientes a cada una de las palabras.

Para realizar esta tarea, el paquete java.util provee de la clase


StringTokenizer que realiza de manera muy sencilla el proceso anteriormente
descrito, por ejemplo:

34
Java Avanzado Programa de Tecnología en Cómputo

package util;
import java.util.StringTokenizer;
/**
* Comportamiento básico de la clase
* StringTokenizer
*
*/
public class TokenizerBasico {
public static void main(String[] args) {
/*
* Realiza el proceso de separación de tokens con la
* cadena proporcionada y el delimitador de campo
* predefinido que es el espacio en blanco
*/
StringTokenizer stnz=new StringTokenizer("Esta es una prueba");
/*
*El método hasMoreTokens() devuelve true si hay
*más tokens, y falso en caso contrario
*/
while(stnz.hasMoreTokens()){
/*
* El método nextToken() regresa una cadena
* correspondiente al siguiente token
*/
System.out.println(stnz.nextToken());
}

}
}

Como se puede ver, el procedimiento es simplemente crear un objeto de la


clase StringTokenizer e indicarle la cadena a segmentar. Una vez hecho esto
simplemente hay que ir recorriendo y obteniendo los tokens a traves de los
métodos hasMoreTokens() y nextToken().

Ahora, el ejemplo anterior funciona para cuando el delimitador de campo es


el espacio en blanco, pero cuando se requiere de algún delimitador de
campo específico se puede utilizar alguno de los constructores de la clase

El primer constructor es el que utiliza el delimitador predefinido del cual ya


se ha hecho mención, mientras que a los siguientes es posible especificar un
delimitador por medio del segundo parámetro. La diferencia entre el segundo
y el tercer constructor, es que el tercero incluirá al delimitador como un
token adicional, mientras que el segundo no lo hará.

35
Java Avanzado Programa de Tecnología en Cómputo

package util;
import java.util.StringTokenizer;
/**
* Esta clase ejemplifica el uso de la clase
* StringTokenizer cuando se quiere separar una
* cadena con un delimitador específico
*/
public class TokenizerDelim {
public static void main(String[] args) {
String cadena="soy:un:hombre:muy:honrado";
/*
* Se crea un objeto StringTokenizer, cuyo delimitador
* de campo es ":"
*/
StringTokenizer stnz=new StringTokenizer(cadena,":");
/*
* Se crea un objeto StringTokenizer, cuyo delimitador
* de campo es ":", que además tomará al delimitador
* como otro token adicional
*/
StringTokenizer stnz2=new StringTokenizer(cadena,":",true);
while(stnz.hasMoreTokens())
System.out.print(stnz.nextToken()+" ");
System.out.println("\n");
while(stnz2.hasMoreTokens())
System.out.print(stnz2.nextToken()+" ");
}
}

Paquete java.util.zip

Este paquete provee de clases para la lectura y escritura de los archivos de


formatos ZIP y GZIP estándar. Es decir, mediante las clases de este paquete
podemos, mediante un programa en java, seleccionar distintos archivos,
comprimirlos y empaquetarlos en un archivo ZIP o GZIP utilizando el
algoritmo de compresión DEFLATE.

Para poder realizar la compresión hay que entender primero algunas clases
que nos permiten realizar esta operación. Para este caso se mencionarán las
clases que nos permiten crear un archivo de formato ZIP.

Clase ZipFile

Esta clase es usada para leer entradas desde un archivo ZIP que se
especifica a través de alguno de los métodos constructores de la clase, ya
sea como un objeto de la clase File, o bien a través de un String que indique
la ruta y nombre del archivo de interés.

36
Java Avanzado Programa de Tecnología en Cómputo

Dentro de los métodos que podemos destacar de esta clase se encuentran

void close() . Cierra el archivo ZIP.

Enumeration entries() . Regresa una enumeración con las entradas del archivo
ZIP

ZipEntry getEntry(String name) .


Regresa la entrada del archivo especificada por
name, o null si no se encuentra.

InputStream getInputStream(ZipEntry entry) .


Regresa un flujo de entrada para leer
el contenido de la entrada especificada.

String getName() . Regresa la ruta y el nombre del archivo ZIP.

int size() .Regresa el número de entradas en el archivo ZIP.

Clase ZipEntry

Esta clase es usada para representar una entrada en un archivo ZIP. Para
instanciar esta clase podemos utilizar el siguiente constructor, que creará
una entrada en el archivo ZIP con el nombre especificado.

Esta clase tiene métodos que nos permiten obtener y fijar información del
archivo comprimido que representa la entrada, por ejemplo:

String getComment(). Obtiene el comentario para la entrada, o null si no hay


ninguno.

long getCompressedSize() . Regresa el tamaño comprimido, o -1 si es desconocido

String getName() . Obtiene el nombre de la entrada.

37
Java Avanzado Programa de Tecnología en Cómputo

long getSize(). Devuelve el tamaño de la entrada, o -1 si es deconocido.

long getTime() .
Devuelve la fecha de la última modificación del archivo,
expresada en milisegundos.

boolean isDirectory( ). Devuelve verdadero si la entrada representa un directorio.

Clase ZipOutputStream

Esta clase implenta un flujo de salida para escribir archivos en un archivo de


formato ZIP. Incluye soporte para ambas entradas comprimidas y
descomprimidas. Esto es que mediante un objeto de este tipo podemos
escribir archivos con formato ZIP, a partir de un grupo de archivos que
pueden o no estar comprimidos.

La forma de crear un objeto ZipOutputStream es sobre otro flujo


OutputStream, que bien puede ser un FileOutputStream, por ejemplo.

Para controlar este tipo de flujo, esta clase tiene los siguientes métodos,
propios, además de los heredados de la clase OutputStream.

void close() . Cierra el ZipOutputStream así como el flujo sobre el que se


construyó.

void putNextEntry(ZipEntry e) .
Comienza escribir una nueva entrada del archivo
ZIP y posiciona el flujo para iniciar la entrada de datos.

void closeEntry() .
Cierra la entrada actual y posiciona el flujo para escribir la
siguiente entrada.

void setComment(String comment) . Coloca un comentario al archivo ZIP.

void finish() .
Termina de escribir el contenido en el flujo de salida del archivo
ZIP sin cerrar el flujo sobre el que se contruyó

Clase ZipInputStream

Esta clase implementa un flujo de entrada para leer archivos en formato ZIP.
Incluye soporte para entradas comprimidas y descomprimidas. De manera

38
Java Avanzado Programa de Tecnología en Cómputo

similar a la clase ZipOutputStream, esta clase nos permite leer archivos


tanto comprimidos como sin comprimir, construyendo un objeto de esta
clase sobre un flujo InputStream.

Entre los métodos que podemos mencionar de esta clase se encuentran los
siguientes:

ZipEntry getNextEntry() .
Lee la siguiente entrada del archivo ZIP y posiciona el
flujo al comienzo de la entrada de datos.

void closeEntry() .
Cierra la entrada actual y posiciona el flujo para que lea la
siguiente entrada.

int available() .
Regresa 0 después de que el final del archivo (EOF) ha sido
alcanzado por la entrada de datos actual, si no siempre regresa 1.

Suma de comprobación: Interface Checksum y clases Alder32 y CRC32

Unas clases importantes del paquete son Adler32 y CRC32, estas clases
implementan la interface java.util.zip.Checksum y calculan la suma de
comprobación requerida para la compresión de datos. El algoritmo Adler32
se conoce por ser mas rápido que el CRC32, y éste ultimo es conocido por
ser más confiable.

Las sumas de comprobación se usan para detectar archivos o mensajes


corruptos. Por ejemplo, imagina que quieres crear un archivo ZIP y después
transferirlo a una PC remota. Una vez que está en la máquina remota,
usando la suma de comprobación, puedes verificar si el archivo se corrompió
durante la transmisión.

Clase CheckedInputStream

Es un flujo de entrada que también mantiene la suma de comprobación de


los datos que están siendo leídos. Esta clase también es una subclase de
InputStream, por lo que en algún momento puede ser utilizada para crear un
ZipInputStream, y al igual que esta clase, los objetos de la clase
CheckedInputStream son creados sobre un InputStream, pero con la
diferencia de que tambien es necesario un objeto que implemente la
interface Checksum, como lo son los objetos de las clases Alder32 y CRC32,

39
Java Avanzado Programa de Tecnología en Cómputo

que a su vez especifican, como se ha comentado, el algoritmo utilizado para


la suma de comprobación.

Clase CheckedOutputStream

Es un flujo de entrada que también mantiene la suma de comprobación de


los datos que están siendo escritos. Semejante a la clase ZipOutStream, los
objetos de esta clase son creados a partir de un OutputStream, y al igual que
la clase CheckedInputStream, hay que especificar el algoritmo para la suma
de comprobación a partir de un objeto que implemente la interface
Checksum.

Un método destacado de las dos clases anteriores es:

Que regresa el objeto Checksum asociado al flujo, por


Checksum getChecksum().
medio del cual podemos realizar la suma de comprobación.

El ejemplo de aplicación de esto es un programa que comprima un conjunto


de archivos y los agregué a un archivo ZIP, como pueden ser el siguiente.
package util.zip;

import java.io.*;
import java.util.zip.*;
/**
* Un ejemplo de almacenamiento con ZIP
* Utiliza la compreción de tipo ZIP para
* comprimir y empaquetar cualquier número
* de archivos indicados por la línea de
* comandos
*/
public class ComprimeZip{
/*
* Las excepciones serán lanzadas a consola
*/
public static void main(String[] args) throws IOException{
/*
* El archivo se llamará prueba.zip, y a partir de
* ello se construye un FileOutputStream
*/
FileOutputStream f= new FileOutputStream(new File("prueba.zip"));

40
Java Avanzado Programa de Tecnología en Cómputo

/*
* Se construye un CheckedOutputStream a partir del
* FileOutputStream y se elije el algoritmo CRC32
* para tener una suma de comprobación
*/
CheckedOutputStream csum= new CheckedOutputStream(f,new CRC32());
/*
* A partir del CheckedOutputStream se crea un
* ZipOutputStream que será el flujo encargado de
* la compresión de los archivos y agregarlos como
* entradas al archivo ZIP
*/
ZipOutputStream salida= new ZipOutputStream(csum);
/*
* Se agrega un comentario al archivo
*/
salida.setComment("Una prueba de compresión Zip con java");
for(int i=0;i<args.length;i++){
System.out.println("Escribiendo el archivo "+args[i]);
/*
* Se leen cada uno de los archivos como un
* flujo de en modo de bytes para asi poder
* comprimir cualquier tipo de archivo
*/
FileInputStream entrada=new FileInputStream(args[i]);
/*
* Se crea una nueva entrada por cada archivo.
*/
salida.putNextEntry(new ZipEntry(args[i]));
int c;
while((c=entrada.read())!=-1)
salida.write(c);
salida.closeEntry();
entrada.close();
}
salida.close();
/*
* suma de comprobacón válida unicamente cerrado el archivo
*/
System.out.println("Suma de comprobacion..."
+csum.getChecksum().getValue());
}
}

41
Java Avanzado Programa de Tecnología en Cómputo

Program ación multihilo: Threads


¿Qué es la programación multhilo?

Podemos definir la programación multihebra o multihilo como un estilo de


ejecución en el que se conmuta entre distintas partes del código de un
mismo programa durante la ejecución. A cada una de estas partes
individuales y atómicas(no divisibles) se les da el nombre de Threads. Los
threads traducidos como hilos, hebras o contextos de ejecución son espacios
de código que la Java Virtual Machine ejecutará de forma simultanea en una
maquina multiprocesador y de forma conmutada en maquina de un solo
procesador, este es, la ejecución de los threads se realizará asignando un
determinado quantum o espacio de tiempo a cada uno de los threads.

Threads en java y estados de los threads

En Java un hilo es una clase que desciende de la clase java.lang.Thread o


bien que extiende a la interfaz Runnable(util en los casos de que la clase ya
forme parte de una jerarquia de clases esto es que ya derive de otra clase).
La forma de construccion del hilo(derivación o implementación) es
independiente de su utilización,una vez creados se usan de la misma forma
sea cual sea el metodo de construcción utilizado.En Java un hilo puede
encontrarse en cualquiera de los siguiente estados:

Nace. El hilo se declarado pero todavia no se ha dado la orden de puesta en


ejecución.(metodo start() ).

Listo. El hilo esta preparado para entrar en ejecución pero el planificador aun
no ha decidido su puesta en marcha

Ejecutandose. El hilo esta ejecutandose en la CPU.

Dormido. El hilo se ha detenido durante un instante de tiempo definido


mediante la utilización del método sleep() .

Bloqueado El hilo está pendiente de una operación de I/O y no volverá al


estado listo hasta que esta termine.

Suspendido. El hilo ha detenido temporalmente su ejecución mediante la


utilización del metodo suspend() y no la reanudará hasta que se llame a resume
() (Obsoleto desde JDK 1.2.)

42
Java Avanzado Programa de Tecnología en Cómputo

Esperando. El hilo ha detenido su ejecución debido a una condición externa o


interna mediante la llamada a wait() y no reanudará esta hasta la llamada al
metodo notify() o notifyAll() .

Muerto El hilo ha terminado su ejecución bien porque termino de realizar su


trabajo o bien porque se llamó al metodo stop() .(Obsoleto desde JDK 1.2.)

Diagrama de estados de los threads

Gráficamente podemos representar lo anterior mediante el diagrama de


estados de los Threads, que se muestra a continuación:

De todos los métodos que se pueden observar los que no deben utilizarse
por estar considerados como obsoletos son stop() , suspend() y resume() .Su
funcionalidad se ha sustituido por otros métodos.

Heredando de la clase Thread y manipulación de hilos.

Como habiamos dicho un hilo puede crearse extendiendo a la clase Thread o


bien implementando a la interfaz Runnable. Analizemos primero la
programación multihilos tomando como punto de partida el crear un subclase
a partir de la clase Thread.

43
Java Avanzado Programa de Tecnología en Cómputo

Métodos run() y start().

Heredar de la clase Thread es la forma más sencilla de implementar la


programación multihilos, pues solo bastará con implementar los métodos y
atributos propios de nuestra clase, y sobre escribir el método run() , de la
clase Thread. En el método run() se definen las acciones que el Thread llevará
a cabo al comenzar su ejecución por medio del método start() . Veamos un
ejemplo sencillo de lo anterior, con una clase que se define a partir de un
programa que utiliza tres hilos.

package threads;
/**
* Ejemplo básico de creación de multihilos
* @author PROTECO
*
*/
public class HeredandoThread extends Thread {
/*
* Se sobrescribe este método para definir el código
* a realizarse cuando se ejecute en Thread
*/
public void run(){
System.out.println("Esta es una instancia de nuestra clase: "
+getName());
}
public static void main(String[] args) {
/*
* Se crean dos objetos de la clase HeredandoThread
*/
HeredandoThread ht1=new HeredandoThread();
HeredandoThread ht2=new HeredandoThread();
/*
* Se inicia la ejecución de los hilos con el
* método start()
*/
ht1.start();
ht2.start();
System.out.println("Hilo principal: "
+Thread.currentThread().getName());
}
}

Si se observa la salida del programa se puede observar que el método


getName() nos devuelve un String que mandamos a imprimir en pantalla, y
que representa un nombre mediante el cual podemos identificar a cada uno
de los hilos. Además aunque creamos dos hilos a partir de la definición de
nuestra clase, al llamar al método currentThread() en el método main, también
nos devuelve un resultado, por lo cual podemos concluir que el método main,
también se ejecuta en un hilo.

44
Java Avanzado Programa de Tecnología en Cómputo

Cuando no asignamos un nombre a cada uno de los hilos, estos tienen un


nombre predefinido que consiste en la palabra Thread, seguida de un
número entero consecutivo, empezando en 0. Hay dos formas de poder
definir un nombre a los hilos. Una es utilizando uno de los constructores
definidos en la clase Thread, y la otra es utilizando el método setName() , de la
misma clase.

Modifiquemos el código anterior para ejemplificar ambos procedimientos.


package threads;
/**
* Esta clase ejemplifica como manipular los nombres
* de los hilos
* @author PROTECO
*
*/
public class HeredandoThreadN extends Thread {
/*
* Se define un constructor que recibe
* el nombre del Thread y llama al constructor
* de la superclase y se imprime en pantalla el
* nombre del hilo.
*/
public HeredandoThreadN(String nombre){
super(nombre);
System.out.println("Nombre desde el constructor: "+getName());
}
public void run(){
System.out.println("Esta es una instancia de nuestra clase: "
+getName());
}
/*
* Se define un método que imprimira el nombre
* del hilo en pantalla y su prioridad
*/
public void printName(){
System.out.println(getName()+" prioridad: "+getPriority());
}

public static void main(String[] args) {


/*
* Se crean dos objetos de la clase HeredandoThreadN
* con nombres definidos desde el constructor
*/
HeredandoThreadN ht1=new HeredandoThreadN("Hilo 0");
HeredandoThreadN ht2=new HeredandoThreadN("Hilo 1");
ht1.start();
ht2.start();
/*
* Se cambian los nombres con el método setName()
*/

45
Java Avanzado Programa de Tecnología en Cómputo

ht1.setName("Hilo A");
ht2.setName("Hilo B");

System.out.println("Hilo principal: "+


Thread.currentThread().getName());
/*
* Se mandan a imprimir las prioridades de los hilos
*/
ht1.printName();
ht2.printName();
/*
* Se cambia el nombre del hilo principal y se
* imprime en pantalla asi como su prioridad
*/
Thread.currentThread().setName("Hilo principal");
System.out.println("Hilo principal: "+
Thread.currentThread().getName()+" prioridad: "+
Thread.currentThread().getPriority());
}
}
Prioridades

Ahora, el lector podrá notar que en la salida del programa se imprimió


primero el nombre del hilo principal, a pesar de que creamos e iniciamos
nuestros hilos antes.

Esto se debe a que los hilos tienen prioridades que van en número del 1 al
10 donde 0 es lo más bajo y 10 es lo más alto, o bien definidos por las
constantes enteras MAX_PRIORITY, MIN_PRIORITY y NORM_PRIORITY, de la clase
Thread. Como se pude observar en el código anterior, la prioridad de los tres
hilos es 5 que corresponde a NORM_PRIORITY, que es la prioridad predefinida.
Cuando dos o más hilos tienen la misma prioridad, influye el órden en que
cada uno empezó a ejecutarse. Si lo vemos de esta manera el hilo principal
fue el primero en ejecutarse y por lo tanto tiene prioridad sobre los otros dos,
por esa razón imprime sus datos antes que los hilos que se ejecutaron
después. Sabiendo esto modifiquemos el código para modificar la prioridad
del hilo principal.
package threads;
/**
* Esta clase ejemplifica como manipular la
* ejecución de los hilos modificando prioridades
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class HeredandoThreadP extends Thread {
public HeredandoThreadP(String nombre){
super(nombre);
}
public void run(){
System.out.println("Esta es una instancia de nuestra clase: "
+getName()+"prioridad: "+getPriority());

46
Java Avanzado Programa de Tecnología en Cómputo

public static void main(String[] args) throws Exception{


HeredandoThreadY ht1=new HeredandoThreadY("Hilo 0");
HeredandoThreadY ht2=new HeredandoThreadY("Hilo 1");
ht1.start();
ht2.start();
/*
* Se le asigna la menor prioridad al hilo princpal
* lo que permitirá la ejecución de los demás hilos
*/
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
Thread.currentThread().setName("Hilo principal");
System.out.println("Hilo principal: "
+Thread.currentThread().getName()
+" prioridad: "+Thread.currentThread().getPriority());
}
}

Nótese como al disminuir la prioridad del hilo principal, ahora los otros dos
hilos pueden ejecutarse primero y por lo tanto imprimir sus datos antes que
el hilo principal.

Método sleep()

Otra manera de hacer que el hilo principal permita que los otros dos hilos
ejecuten su código es durmiendo el hilo principal un tiempo con el método
sleep() .
package threads;
/**
* Esta clase ejemplifica como manipular la
* ejecución de los hilos con el método sleep()
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class HeredandoThreadS extends Thread {
public HeredandoThreadS(String nombre){
super(nombre);
}
public void run(){
System.out.println("Esta es una instancia de nuestra clase: "
+getName()+" prioridad: "+getPriority());
}

public static void main(String[] args) throws Exception{


HeredandoThreadY ht1=new HeredandoThreadY("Hilo 0");
HeredandoThreadY ht2=new HeredandoThreadY("Hilo 1");
ht1.start();
ht2.start();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
/*
* Con el método sleep() se duerme el hilo principal
* durante 3 segundos lo que permite la ejecución de
* los demás hilos sin importar que el hilo principal

47
Java Avanzado Programa de Tecnología en Cómputo

* tenga mayor prioridad


*/
sleep(3000);

Thread.currentThread().setName("Hilo principal");
System.out.println("Hilo principal: "
+Thread.currentThread().getName()
+" prioridad: "+Thread.currentThread().getPriority());
}
}

En este caso se le dio la máxima prioridad al hilo principal con el objeto de


asegurar que su código se ejecutara primero. Aun así al dormir el hilo
durante 3 segundos da la posibilidad de que los demás hilos continúen su
ejecución obteniendo así el resultado esperado.

Método yield().

Veamos otra forma utilizando el método yield() .


package threads;
/**
* Esta clase ejemplifica como manipular la
* ejecución de los hilos con el método yield()
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class HeredandoThreadY extends Thread {
public HeredandoThreadY(String nombre){
super(nombre);
}
public void run(){
System.out.println("Esta es una instancia de nuestra clase: "
+getName()+" prioridad: "+getPriority());
}

public static void main(String[] args) throws Exception{


HeredandoThreadY ht1=new HeredandoThreadY("Hilo 0");
HeredandoThreadY ht2=new HeredandoThreadY("Hilo 1");
ht1.start();
ht2.start();
/*
* Haciendo una llamada al método yield() se cede la
* ejecución a otros hilos
*/
yield();

Thread.currentThread().setName("Hilo principal");
System.out.println("Hilo principal: "
+Thread.currentThread().getName()
+" prioridad: "+Thread.currentThread().getPriority());
}
}

48
Java Avanzado Programa de Tecnología en Cómputo

El método yield() causa que el hilo actual haga una pausa momentáneamente
para permitir que otros hilos continúen con su ejecución.

El productor- consumidor: Sincronización de métodos

El problema del productor- consumidor es un problema clásico de aplicación


de los Threads, el cual consiste en un hilo que produce un dato, por ejemplo,
mientras otro lo consume, pero para esto el consumidor no puede proseguir
si antes el productor no ha terminado su labor, para esto debe de haber un
mediador entre ambos que sea el encargado de la sincronización entre
productor y consumidor.

Para realizar esto se tienen los métodos wait() , notify() y notifyAll() .

void wait() .
Hace que un hilo haga una pausa en su ejecución hasta que algún
otro hilo le notifique que puede continuar

void notify() . Notifica a otro hilo en espera que puede continuar con su
ejecución.

void notifyAll() . Notifica a todos los hilos en espera que pueden continuar con
su ejecución.

Además los métodos que implementen los métodos anteriores deben de


llevar en su firma el modificador synchronized, para indicar la sincronización
entre ellos.

Veamos el siguiente ejemplo. donde implementa un reloj, donde el minutero


es una clase que se ejecuta sobre un hilo, mientras que el segundero es otra
clase que se ejecuta en otro diferente.
package threads;
/**
* Esta clase será la responsable de la
* sincronización entre el productor y
* el consumidor
* @author PROTECO
*
*/
class TicToc {
private boolean ticOcurre = false;
/*
* El modificador synchronized indica que
* dos o más métodos son sincornizados
*/
public synchronized void esperarTic() {
while (!ticOcurre)
try {
/*

49
Java Avanzado Programa de Tecnología en Cómputo

* El método wait() indica al


* hilo en ejecución que espere
* a que otro hilo invoque un notify()
*/
wait();
} catch (InterruptedException e) {
System.err.println("Excepcion");
}
ticOcurre = false;
}

public synchronized void tic() {


ticOcurre = true;
/*
* El método notify() indica a un hilo en
* espera que puede continuar con su ejecución
*/
notify();
}

}
/**
* Esta clase se ejecutará en hilo propio
*
* @author PROTECO
*
*/
class Segundo extends Thread {
private int segundos = 0;

private TicToc minutoTic;

public Segundo(TicToc minutoTic) {


this.minutoTic = minutoTic;
}

public void run() {


while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.err.println("Excepcion");
}

if (segundos == 59) {
/*
* Notifica a minuto que puede
* continuar ejecutándose
*/
minutoTic.tic();
segundos = 0;
} else
segundos++;
System.out.println(segundos);
}
}

50
Java Avanzado Programa de Tecnología en Cómputo

class Minuto extends Thread {

private int minutos = 0;

private TicToc minutoTic;

public Minuto(TicToc minutoTic) {


this.minutoTic = minutoTic;
}

public void run() {


while (true) {
/*
* Espera a que se le notifique
* que Segundo ha llegado a 59
*/
minutoTic.esperarTic();
if (minutos == 59) {
minutos = 0;
} else
minutos++;
System.out.println(minutos);
}
}
}

public class Reloj {

public static void main(String a[]) {


/*
* Se crea un objeto de la clase TicToc que será
* el encargado de sincronizar los minutos y los
* segundos
*/
TicToc minutoTic = new TicToc();
/*
* Se crea un objeto Minuto con la referencia
* al TicToc creado
*/
Minuto minuto = new Minuto(minutoTic);
minuto.start();
/*
* Se crea un objeto Segundo con la misma
* referecia a TicToc que se le proporcionó
* a Minuto
*/
Segundo segundo = new Segundo(minutoTic);
segundo.start();
}
}

Para el caso anterior nuestro productor es el objeto de la clase Segundo, que


produce números del 0 al 59; el consumidor es el objeto de la clase Minuto

51
Java Avanzado Programa de Tecnología en Cómputo

que está en espera a que Segundo llegue al 59 para poder continuar con su
ejecución, y además el objeto de la clase TicToc es el que hace posible la
sincronización entre ambos mediante los métodos sincronizados esperarTic() y
tic() .

La interfaz Runnable

Como se mencionó anteriormente, otra manera de realizar la programación


multihilos es implementando la interfaz Runnable. Esto es de especial
utilidad debido a la inexistencia en java de la herencia múltiple, por que en
muchos casos dentro de la programación se hereda de una clase, y al querer
tambien emplear la programación multihilos, el implementar la interfaz
Runnable es una buena solución para estos casos.

Analizando un poco la interfaz Runnable, podemos ver que es una interfaz


con un solo método que es precisamente el método run(), que ya
conocemos, por que tiene la misma función que el de la clase Thread, y esto
es verificable a primera instancia por que la clase Thread también
implementa la interfaz Runnable.

Debido a las reglas de las interfaces, al implementar la interfaz Runnable


estamos obligados a definir el método run() , que similar a lo que ya hemos
trabajado previamente, pues en el método run() se definen el código a
realizarse cuando se inicie la ejecución del hilo.

Hasta ahora hemos descrito que para crear hilos hay que implementar la
interfaz Runnable, pero aun no hemos creado hilos. Volvamos a la clase
Thread y veamos el siguiente constructor.

Para crear un Thread requiere de un Runnable, que por polimorfismo las


clases que implementen la interfaz, cumplen con la condición y pueden se
utilizadas para crear Threads, que se manejan de la misma forma que
describimos anteriormente.

Para ejemplificar esto veamos un código semejante al primero que


utilizamos, pero ahora implementando la interfaz Runnable.

package threads;

/**
* Ejemplo de creación de hilos a partir de la interfaz
* Runnable
* @author Ricardo Castañeda M. / PROTECO

52
Java Avanzado Programa de Tecnología en Cómputo

*
*/
public class ImplementandoRunnable implements Runnable{
static int hilosCreados=0;

public void run() {


hilosCreados++;
System.out.println("Se han creado "+hilosCreados+
" hilos a partir de ImplementadoRunnable");
}
public static void main(String[] args) {
/*
* Se crean dos objetos de la clase Thread a partir de
* dos objetos de la clase ImplementandoRunnable
*/
Thread ir1=new Thread(new ImplementandoRunnable());
Thread ir2=new Thread(new ImplementandoRunnable());
/*
* Se inicia la ejecución de los hilos con el
* método start()
*/
ir1.start();
ir2.start();
/*
* Se imprimen en pantalla los nombres de los hilos
*/
System.out.println("Hilo principal: "
+Thread.currentThread().getName());
System.out.println("Hilo 1: "+ir1.getName());
System.out.println("Hilo 2: "+ir2.getName());
}
}

53
Java Avanzado Programa de Tecnología en Cómputo

Interf aces gráficas de usuario (GUI's): paquet es


java.aw t y javax.swing
Justificación de las GUI's.

Las interfaces gráficas de usuario surgen por la necesidad de tener


programas más amigables para usuarios finales ya que uno como
programador, la mayor parte de las veces uno no hace programas para
programadores, la mayor parte de las personas rehuyen a programas
escritos en modo consola o modo comando, ya sea por que parecen ser
complicados, por que parecen ser aburridos, etc.

Además ofrecen programas más vistosos y llamativos, programas más


intuitivos y sencillos de utilizar, incluso hasta más poderosos, pues abren la
posibilidad de realizar tareas que en modo comando serían muy
complicadas, como lo es la edición de imágenes y video, y proporcionan a los
usuarios una manera agradable de trabajar.

Originalmente con la versión 1.0 del JDK se cubría el desarrollo de interfáces


gráficas mediante el paquete AWT (Abstract Windows Toolkit), que fue
mejorado para la versión 1.1, pero que presentaba problemas al ejecutarse
en distintas plataformas, lo que representó un problema de portabilidad. Esto
se corrigió para la versión 1.2 del JDK con la aparición de las Java Foudation
Classes (JFC) que resuelven los problemas presentados en AWT, y que
además incorporan muchas otras características. El paquete principal de las
JFC y al que nos enfocaremos principalmente es el paquete javax.swing, que
incorpora muchas clases heredadas de AWT, así como su modelo de eventos,
por lo que se trabajará con ambos paquetes simultáneamente, para el
desarrollo de GUI's.

Básicamente una aplicación realizada mediante una interfaz gráfica se


compone de tres elementos básicos:

Componentes. Son elementos que nos van a permitir la interacción entre el


usuario y el programa. Los componentes en java, provienen de la clase
Component de awt, y específicamente los componentes de swing con los que
vamos a trabajar preferentemente provienen de la clase JComponent
definida en swing y que por cuestiones de herencia es la clase que va a
definir el comportamiento básico de todas los componentes swing

Contenedores. Son elementos que nos van a permitir distribuir y organizar


los componentes de la interfaz .En java todos los contenedores son subclases
de la clase Container del paquete awt, que a su vez hereda de la clase
Component, lo que nos permite decir que los contenedores tambien pueden

54
Java Avanzado Programa de Tecnología en Cómputo

considerarse como componentes, y por lo tanto es posible anidar


contenedores dentro de otros contenedores, tal como si fueran
componentes, por cuestiones de polimorfismo.

Eventos. Finalmente los eventos son acciones específicas de cada


componente. Mediante los eventos es como vamos a darle funcionalidad al
programa. En java los eventos son representados por distintas interfaces
definidas en el paquete java.awt.event, dependiendo del tipo de evento, y el
implementarlas es la manera de agregarle funcionalidad al programa, por
medio de eventos.

Clase JFrame

JFrame es el primer contenedor y es el que nos ayudará a comenzar con el


desarrollo de la mayoría de nuestras GUI’s. Esta clase hereda de la clase
Container, y nos proporciona una ventana funcional con una apariencia y
comportamiento igual a del sistema de ventanas del Sistema Operativo.
Veamos un ejemplo básico.

package gui;
import javax.swing.JFrame;
/**
* Este ejemplo muestra como crear una
* GUI básica a partir de la clase
* JFrame de swing
*
* @author PROTECO
*
*/
/*
* Se hereda de la clase JFrame para
* aprovechar sus características
*/
public class FrameBasico extends JFrame {
public FrameBasico(String title){
/*
* Se llama al constructor de la
* clase padre que recibe un String
* que será el título del JFrame, pero
* además el JFrame obtenido no será
* visible
*/
super(title);
/*
* setVisible() es un método heredado de
* la clase Component, por lo que se puede
* utilizar de la misma manera para cualquier
* clase heredada de Component y que nos va
* a permitir ocultar o mostrar el componente
*/
setVisible(true);

55
Java Avanzado Programa de Tecnología en Cómputo

/*
* Ocurre lo mismo para setSize() definido en
* la clase Component y cuya utilidad es definir
* el tamaño inicial con que se mostratá el
* componente
*/
setSize(300,200);
}
public static void main(String args[]){
new FrameBasico("Mi primera GUI");
}
}

En este ejemplo anterior se pueden hacer algunas observaciones que en


algún momento dado podemos modificar a nuestra conveniencia.

En primer lugar el JFrame tiene un icono predeterminado que se trata del


logotipo de la tasa de café de Java, que siempre estará presente en todos
nuestros JFrames a menos que especifiquemos otro.

La ventana puede cambiar de tamaño, puede maximizarse y restaurarse,

La ventana por defecto aparece siempre alineada en la esquina superior


izquierda de la pantalla, que si lo tomamos como un sistema de coordenadas
cuyas unidades son los pixeles, podríamos decir que se encuentra en la
coordenada (0,0).

El color por defecto del fondo es gris

Al cerrar la ventana con el boton cerrar del JFrame, el programa sigue en


ejecución.

Para cada una de las observaciones anteriores podemos utilizar un método


que nos permite modificar de acuerdo a lo que especifiquemos, como en el
ejemplo siguiente.

package gui;
import java.awt.Image;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import java.awt.Color;
/**
* Esta clase ejemplifica algunos métodos que
* nos permiten tener un JFrame un poco más
* elaborado
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class FrameBasico2 extends JFrame{

56
Java Avanzado Programa de Tecnología en Cómputo

public FrameBasico2(String title){


super(title);
setVisible(true);
/*
* Este metodo se define con el objetivo de
* abrir un archivo como imágen y utilizarlo
* para cambiar el icono del JFrame
*/
cambiarIcono();
setSize(300, 200);
/*
* Este método definido en la clase Component
* se utiliza para mover el componente a una
* nueva localización, que en este caso es
* dentro de la pantalla
*/
setLocation(300,300);
/*
* El método setBackground tambien se define
* en la clase Component y permite definir un
* nuevo color de fondo, utilizando para eso
* un objeto de la clase Color, definida en
* java.awt
*/
setBackground(Color.YELLOW);
/*
* El método setResizable() indica si el JFrame
* va a poder cambiar de tamaño las acciones de
* maximizar y restaurar el Frame van a estar
* deshabilitadas. Este método se define en la
* clase Frame de java.awt
*/
setResizable(false);
/*
* Este método se utiliza para indicarle al programa
* como proseguir cuando se cierra el JFrame, que en
* este caso se indica que va a terminar cuando el
* JFrame sea cerrado. Este método está definido en
* otras clases como JDialog y JInternalFrame
*/
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void cambiarIcono(){
/*
* El método empleado para cambiar el icono es el
* método setIconImage(Image icon), que requiere
* un objeto de la clase Image
*/
Image icono;
try{
/*
* Se utiliza el método estático de la clase
* ImageIO para obtener un objeto de la clase
* Image a partir de un objeto de tipo File
*/
icono = ImageIO.read(new File("proteco.jpg"));

57
Java Avanzado Programa de Tecnología en Cómputo

this.setIconImage(icono);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
public static void main(String[] args) {
new FrameBasico2("JFrame corregido y aumentado");
}
}

En el ejemplo anterior se puede observar que utilizar métodos definidos en


muchas clases, pero que por cuestiones de herencia también son propios de
la clase JFrame, así mismo tambien son válidos para otras clases, ya sean
componente o contenedores dependiendo el caso. También este ejemplo nos
sirve para introducir algunas clases nuevas que son comunes en la
programación de interfaces gráficas

La clase Color, definida en java.awt, es una clase utilizada para encapsular


colores de tipo RGB, y nos va a permitir utilizar colores ya definidos (como en
el caso del ejemplo), así como crear nuestros propios colores utilizando
alguno de los constructores de esta clase, siguiendo la terminología de los
colores RGB que representan la formación de colores a partir de los colores
primarios de la luz, que son el rojo, verde y azul, que pueden tener un canal
alpha que representa el grado de transparencia de ese color.

Otras clases utilizadas en este ejemplo tienen que ver con el manejo de
imágenes. Veamos primero la clase Image, definida también en java.awt.

La clase Image es una clase abstracta que es la superclase de todas las


clases que representan imágenes gráficas y tiene como subclases directas a
la clase BufferedImage y VolatileImage. En este caso lo que nos interesa es
construir una imagen a partir de un archivo para poder definirla como el

58
Java Avanzado Programa de Tecnología en Cómputo

nuevo icono del JFrame, por lo cual utilizamos una clase llamada ImageIO,
que es parte del paquete javax.imageio que nos permite controlar
operaciones de entrada y salida de imágenes, y que en este caso mediante
su método estático read(File f) , nos permite tener un objeto Image a partir de
un archivo.

Layout Managers y JPanel

Antes de continuar con otros contenedores, debemos de aprender como


distribuir los elementos en los mismos. Para esto nos ayudamos de los
LayoutManagers, o bien manejadores de disposición. Los Layout managers
son clases que implementan la interface LayoutManager, y entre los que
podemos mencionar algunos

• FlowLayout

• BorderLayout

• GridLayout

• GridBagLayout

• CardLayout

• BoxLayout

FlowLayout y JPanel

Es el LayoutManager de los contenedores por defecto. Lo que hace es


acomodar los componentes en un flujo direccional como las líneas de texto
en un párrafo. El flujo direccional es determinado por la propiedad
componentOrientation del componente y puede tener alguno de dos valores

ComponentOrientation.LEFT_TO_RIGTH

ComponentOrientation.RIGTH_TO_LEFT
package gui;
import javax.swing.*;
/**
* Muestra el comportamiento del
* FlowLayout, que no se especifica en
* este ejemplo por que es el LayoutManager
* predefinido
* @author PROTECO
*
*/
public class Flow extends JFrame {

59
Java Avanzado Programa de Tecnología en Cómputo

/*
* La clase JPanel representa al
* contenedor genérico, y nos permite
* agrupar varios elementos. Además
* el JPanel puede ser agregado a otro
* contenedor
*/
JPanel jpnl;
public Flow(String arg0){
super(arg0);
jpnl=new JPanel();
for(int i=1;i<=5;i++)
/*
* Se agregan elementos al
* contenedor por medio del
* método add()
*/
/*
* Se obtiene el panel contenedor del
* JFrame para poder agregarle nuestro
* JPanel
*/
jpnl.add(new JButton("Boton"+i));
this.getContentPane().add(jpnl);
setSize(300,200);
setVisible(true);
}
public static void main(String[] args) {
new Flow("FlowLayout");
}
}

En este ejemplo se puede ver la distribución de los elementos agregados al


contenedor con un FlowLayout, pero además utilizamos una clase importante
llamada JPanel

La clase JPanel es un contenedor genérico ligero, subclase directa de


JComponent utilizada para agrupar y distribuir elementos. Además los
objetos de esta clase pueden estar contenidos en otro contenedor como si
fueran otro elemento. Esta característica facilita la distribución y
organización de elementos. Dado que los JPanel son contenedores, también
tienen su propio LayoutManager, que por defecto es el FlowLayout, pero que
puede especificarse ontro mediante uno de los métodos constructores de
esta clase, o bien con el método setLayout() de la clase Container.

BorderLayout

60
Java Avanzado Programa de Tecnología en Cómputo

El BorderLayout es un LayoutManager que divide al contenedor en cinco


regiones, NORT, SOUTH, EAST, WEST y CENTER, para acomodar los
elementos indicados. Cada región no puede contener más de un componente

La forma de agregar elementos a un contenedor que tiene definido este tipo


de manejador de dispocisión es con el método add(Component comp, int index),
de la clase Container, con el cual indicamos el componente de interés, así
como el índice que define la ubicación en el contenedor. Para este caso
específico podemos utilizar las constantes definidas en la clase BorderLayout
que son intuitivas y son las siguientes.

BorderLayout.NORTH

BorderLayout.SOUTH

BorderLayout.EAST

BorderLayout..WEST

BorderLayout.CENTER

Veamos un ejemplo de aplicación


package gui;
import java.awt.BorderLayout;
import javax.swing.*;
/**
* Muestra el comportamiento del
* BorderLayout
* @author PROTECO
*
*/ public class Border extends JFrame {
/*
* La clase JPanel representa al
* contenedor genérico, y nos permite
* agrupar varios elementos. Además
* el JPanel puede ser agregado a otro
* contenedor
*/
JPanel jpnl;
public Border(String arg0){
super(arg0);
/*
* Se define que el LayoutManager
* va a ser BorderLayout() a través
* del método constructor
*/
jpnl=new JPanel(new BorderLayout());
/*
* Se agregan elementos al
* contenedor por medio del
* método add(Component comp,int index)

61
Java Avanzado Programa de Tecnología en Cómputo

* donde el segundo parámetro va a estar


* dado por las constantes definidas por
* la clase BorderLayout
*/
jpnl.add(new JButton("NORTH"),BorderLayout.NORTH);
jpnl.add(new JButton("SOUTH"),BorderLayout.SOUTH);
jpnl.add(new JButton("EAST"),BorderLayout.EAST);
jpnl.add(new JButton("WEST"),BorderLayout.WEST);
jpnl.add(new JButton("CENTER"),BorderLayout.CENTER);

this.getContentPane().add(jpnl);
setSize(300,200);
setVisible(true);
}
public static void main(String[] args) {
new Border("BorderLayout");
}
}

GridLayout

Este layout manager ocupa como modelo una matriz de nxm, en cuyas
celdas se acomodan los elementos de manera secuencial. El contenedor es
dividido en celdas del mismo tamaño, y un componente es agregado en cada
celda.
package gui;
import java.awt.GridLayout;
import javax.swing.*;
/**
* Muestra el comportamiento del
* BorderLayout
* @author PROTECO
*
*/
public class Grid extends JFrame {
JPanel jpnl;
GridLayout gl;
public Grid(String arg0){
super(arg0);
jpnl=new JPanel();
/*
* Se define un GridLayout de
* 3 renglones y 5 columnas
*/
gl=new GridLayout(3,5);
/*
* Ahora se define el LayoutManager
* con el método setLayout(),
*/
jpnl.setLayout(gl);
/*
* Se obtiene el número de columnas
* y de renglones para determinar el
* número de elementos dinámicamente,
* utilizando los métodos getColumns()

62
Java Avanzado Programa de Tecnología en Cómputo

* y getRows() de la clase GridLayout


*/
for(int i=1;i<=(gl.getColumns()*gl.getRows());i++)
jpnl.add(new JButton("B"+i));
this.getContentPane().add(jpnl);
setSize(300,200);
setVisible(true);
}
public static void main(String[] args) {
new Grid("GridLayout");
}
}

Componentes Swing

JButton

En los ejemplos anteriores tuvimos la oportunidad de manejar un poco la


clase JButton. Esta clase hereda de la clase AbstractButton, que define el
comportamiento común para los botones y elementos de menú, y de la cual
podemos utilizar sus métodos para personalizar el comportamiento de
nuestros JButton.

Veamos algunos constructores de la clase JButton

El primer constructor crea un JButton con el icono especificado, para esto el


constructor requiere un objeto de tipo Icon.

Icon es una interface definida en el mismo paquete javax.swing, por lo que


directamente no podemos crear objetos directamente, sin embrago para este
caso podemos utilizar la clase ImageIcon que implementa la interfaz Icon, y
además provee diversos métodos para construir objetos de esta clase.

63
Java Avanzado Programa de Tecnología en Cómputo

El segundo constructor ya lo hemos utilizado, pues es el que crea un JButton


con la etiqueta especificada. Y el tercer constructor crea un JButton con la
etiqueta y el icono especificado.
package gui;

import java.awt.*;
import javax.swing.*;
/**
* Esta clase muestra la creación y
* personalización de JButtons
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class Button extends JFrame {
JPanel jpnl;
JButton btn1;
JButton btn2;
JButton btn3;

public Button(String title){


super(title);
jpnl=new JPanel();
/*
* Se agregan los botones
* uno a uno al panel
*/
jpnl.add(new JButton("btn0"));
jpnl.add(getBtn1());
jpnl.add(getBtn2());
jpnl.add(getBtn3());

/*
* Se agrega el panel al
* JFrame
*/
getContentPane().add(jpnl);
setVisible(true);
setSize(300,200);

64
Java Avanzado Programa de Tecnología en Cómputo

setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* @return Un boton de colores personalizados
*/
public JButton getBtn1(){
if(btn1==null){
btn1=new JButton("btn1");
/*
* Se utilizan los métodos setBackground
* y setForeground definidos en la clase
* JContainer
*/
btn1.setBackground(Color.BLUE);
btn1.setForeground(Color.GREEN);
}
return btn1;
}
/**
* @return Un boton deshabilitado
*/
public JButton getBtn2(){
if(btn2==null){
btn2=new JButton("btn2");
/*
* Para deshabilitar el boton se utiliza
* el método setEnable(boolean b), de la
* clase AbstractButton
*/
btn2.setEnabled(false);
}
return btn2;
}
/**
*
* @return Un boton con un icono
*/
public JButton getBtn3(){
if(btn3==null){
/*
* Utilizamos en constructor que nos
* proporciona un boton sin etiqueta
*/
btn3=new JButton();
try{
/*
* Creamos un objeto ImageIcon, a partir
* del nombre del archivo.
* La clase ImageIcon implementa la interface
* Icon, y que por polimorfismo es válido
* utilizarlo como Icon
*/
ImageIcon icon=new ImageIcon("proteco.jpg");
/*
* Este método se utiliza para colocar un
* icono en el boton

65
Java Avanzado Programa de Tecnología en Cómputo

*/
btn3.setIcon(icon);
}catch(Exception e){
System.out.println(e.getMessage());
}
btn3.setBackground(Color.WHITE);
}
return btn3;
}
public static void main(String[] args) {
new Button("JButton");

}
}

JLabel

Representa un área para mostrar una cadena de texto pequeñ, una imágen o
ambos. Las etiquetas no reaccionan a los eventos de entrada, como
resultado no pueden tener el foco del teclado.Veamos los constructores de
esta clase y un ejemplo.

package gui;
import javax.swing.*;
import java.awt.GridLayout;
/**
* Ejemplo JLabel
* @author Ricardo Castañeda M / PROTECO
*
*/
public class Label extends JFrame {
JPanel jpnl;
JLabel jlbl1;
JLabel jlbl2;
ImageIcon icon;
public Label(String title){
super(title);
icon=new ImageIcon("proteco.jpg");
jpnl=new JPanel(new GridLayout(3,1));
/*

66
Java Avanzado Programa de Tecnología en Cómputo

* Se crean tres JLabel utilizando los


* distintos constructores
*/
jpnl.add(new JLabel(icon));
jpnl.add(new JLabel("Este es un JLabel"));
jpnl.add(new JLabel("Esto es otro JLabel",
icon,SwingConstants.CENTER));
getContentPane().add(jpnl);
setSize(300,200);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}

public static void main(String[] args) {


new Label("JLabel");

}
}
JTextField

La clase JTextField es un componente ligero que permite editar una línea de


texto. Es una subclase de la clase JTextComponent, que es conveniente
consultar para capacidades adicionales, como utilizar el portapapeles del
sistema para las operaciones de copy() , cut() , paste() , por ejemplo.

Los constructores de esta clase son los siguientes, además del predefinido
que nos construye un JTextField sin ningún texto predeterminado.

Los constructores anteriores nos permiten construir objetos de esta clase,


definiendo el texto que se desplegará al iniciar el componente, el número de
columnas que abarcará, o ambos.

Los principales métodos de esta clase son los que nos permiten trabajar con
el texto que contiene, o bien que podemos agregar a un componente de este
tipo, que también están definidos en la clase JTextComponent y son:

String getText() . Nos devuelve el texto contenido en el JTextComponent

void setText(String text) . Coloca el texto especificado en el JTextComponent

void setEditable(boolean b). Define


si el usuario va a poder o no modificar el texto
contenido en el JTextComponent.

67
Java Avanzado Programa de Tecnología en Cómputo

void copy() .
Transfiere el rango seleccionado actual en el modelo de texto
asociado, al portapapeles del sistema, dejando el contenido del modelo de
texto.

void cut() .
Transfiere el rango seleccionado actual en el modelo de texto
asociado, al portapapeles del sistema, removiendo el contenido del modelo
de texto.

void paste() .
Transfiere el contenido del portapapeles del sistema al modelo de
texto asociado.

El modelo de eventos de AWT: Interfaces ActionListener

Antes de continuar viendo los componentes hablaremos un poco del modelo


de eventos de AWT. Hasta ahora hemos visto la parte del diseño de una GUI,
pero sin funcionalidad alguna, misma que se va a dar mediante eventos.

Un evento es una acción que algún componente o dispositivo realiza, por lo


que necesitamos algo que nos permita "escuchar" esos eventos, para cuando
ocurran, llevar a cabo algun procedimiento. Para poder llevar a cabo esta
tarea, awt contiene un paquete llamado java.awt.event, que contiene de
interfaces y clases para atender los diferentes tipos de eventos ocurridos por
los componentes de AWT, y por lo tanto tambien los de Swing.

Interface ActionListener

Esta interface se encarga de escuchar eventos de acción, como puede ser


dar click a un boton, por ejemplo.

La interface ActionListener, solo tiene un método que se debe definir al


implementarla, y se trada del método actionPerformed(ActionEvent e) , que se
invoca cada vez que ocurre una acción.

Veamos un ejemplo utilizando los componentes vistos y la interface


Actio nListener.
package gui;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/**
* Esta clase es un panel con elementos
* de un formulario
* @author Ricardo Castañeda M / PROTECO
*
*/
public class Forma extends JPanel implements ActionListener{

68
Java Avanzado Programa de Tecnología en Cómputo

/*
* Se declaran los componentes del panel
* como private
*/
private JLabel jLblTelefono = null;
private JTextField jTFNombre = null;
private JLabel jLblNombre = null;
private JTextField jTFDireccion = null;
private JLabel jLblDireccion = null;
private JTextField jTFTelefono = null;
private JButton jBtnEnviar = null;
private JButton jBtnLimpiar = null;
/*
* El constructor llama al constructor
* de la clase padre y llama al método
* initializa(), que define las condiciones
* iniciales de los componentes
*/
public Forma() {
super();
initialize();
}

/**
* This method initializes this
*
* @return void
*/
private void initialize() {
jLblDireccion = new JLabel();
jLblDireccion.setText("Direccion");
jLblNombre = new JLabel();
jLblNombre.setText("Nombre");
jLblTelefono = new JLabel();
jLblTelefono.setText("Telefono");
this.setSize(300, 200);
this.setLayout(new GridLayout(4,2));
this.setVisible(true);
this.add(jLblNombre);
this.add(getJTFNombre());
this.add(jLblDireccion);
this.add(getJTFDireccion());
this.add(jLblTelefono);
this.add(getJTFTelefono());
this.add(getJBtnEnviar(), getJBtnEnviar().getName());
this.add(getJBtnLimpiar(), null);
}

public JTextField getJTFNombre() {


if (jTFNombre == null) {
jTFNombre = new JTextField();
}
return jTFNombre;
}

69
Java Avanzado Programa de Tecnología en Cómputo

/*
* Se definen métodos get() que no serviran
* para obtener los componentes encapsulados
* e incializarlos en caso de que no haya sido.
*/
public JTextField getJTFDireccion() {
if (jTFDireccion == null) {
jTFDireccion = new JTextField();
}
return jTFDireccion;
}

public JTextField getJTFTelefono() {


if (jTFTelefono == null) {
jTFTelefono = new JTextField();
}
return jTFTelefono;
}

public JButton getJBtnEnviar() {


if (jBtnEnviar == null) {
jBtnEnviar = new JButton();
/*
* Se invoca a este método para agregar un
* ActionListener al boton
*/
jBtnEnviar.addActionListener(this);
jBtnEnviar.setText("Enviar");
}
return jBtnEnviar;
}
private JButton getJBtnLimpiar() {
if (jBtnLimpiar == null) {
jBtnLimpiar = new JButton();
/*
* Se invoca a este método para agregar un
* ActionListener al boton
*/
jBtnLimpiar.addActionListener(this);
jBtnLimpiar.setText("Limpiar");
}
return jBtnLimpiar;
}

/**
* Este metodo limpia los JTextField
*
*/
public void reset(){
/*
* Con el método setText podemos definir
* el texto que llevarán los Componentes
* como los JLabels y los JTextField
*/
this.jTFDireccion.setText("");
this.jTFNombre.setText("");

70
Java Avanzado Programa de Tecnología en Cómputo

this.jTFTelefono.setText("");
}

/*
* Este se declara en la interfaz ActionListener
* pero se define en esta clase para que escuche
* eventos.
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource()==this.jBtnLimpiar)
this.reset();

if(e.getSource()==this.jBtnEnviar){
System.out.println("Nombre: "+jTFNombre.getText());
System.out.println("Nombre: "+jTFDireccion.getText());
System.out.println("Nombre: "+jTFTelefono.getText());
}

}
public static void main(String[] args) {
/*
* Se crea un objeto JFrame que servirá
* de contenedor de nuestro JPanel
*/
JFrame test=new JFrame("JPanel Forma");
Forma forma=new Forma();
test.getContentPane().add(forma);
test.setVisible(true);
test.setSize(300,200);
}
}

Otros componentes y contendores swing

JTextArea y JScrollPane

Estas dos clases usualmente se utilizan juntas, por lo que las estudiaremos
de igual forma. La clase JTextArea representa un area multilíneas que
despliega texto plano y de manera análoga a la clase JTextField, desciende
de la clase JTextComponent, y por lo tanto los métodos vistos en la clase
JTextField son válidos tambien para esta clase.

Los cosntructores de esta clase con también similares a los de la clase


JTextField, con la diferencia de que para esta clase podemos definir tambien
el número de renglones que abarcará nuestra área de texto.

71
Java Avanzado Programa de Tecnología en Cómputo

Y el método más sobresaliente de esta clase es:

void append(String str) .


Que va a agregar texto al final del área de texto sin
sobreescribir el existente.

La clase JScrollPane provee de una vista con barras de scroll, de un


componente ligero, que en este caso servirá para contener una JTextArea.
Los contenedores de este tipo no soportan componentes compuestos o
complejos.

Un JScrollPane básicamente consiste en JScrollBar's, que son las barras de


desplazamiento usuales, y un JViewPort, que provee de una ventana que
mostrará solo una parte del componente contenido en el JScrollPane.

Algunos métodos de esta clase es el siguiente.

void setViewportView(Component view) .


Que creará la ventana si es necesario y
entonces pondrá la vista del componente.

void setLayout(LayoutManager layout) . Cambia el layout manager del JScrollPane


por el especificado.

Agregemos al formulario anterior algunas líneas para agregar otra entrada al


formulario utilizando un JScrollPane una JTextArea, podemos agregar los
siguientes dos métodos y agregar el JScrollPane con un JLabel, al metodo
initialize() de nuestra clase Forma, agregando tambien otra columna al
GridLayout. Por legibilidad solo mostraremos las líneas que se aumentaron al
código anterior, para resaltar los cambios.
package gui;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

/**
* Esta clase es un panel con elementos
* de un formulario
* @author Ricardo Castañeda M / PROTECO
*
*/
public class Forma2 extends JPanel implements ActionListener{
...

72
Java Avanzado Programa de Tecnología en Cómputo

private void initialize() {


jLblObservaciones = new JLabel();
jLblObservaciones.setText("Observaciones");
jLblDireccion = new JLabel();
/*
* Se define un GridLayout con 5 columnas
*/
this.setLayout(new GridLayout(5,2));
/*
* Se agregan la etiqueta y el área de
* texto, con su panel, al JPanel principal
*/
this.add(jLblObservaciones);
this.add(getJScrllPnlObservaciones());
...
}
...
public void reset(){
...
/*
* Se agrega esta línea para limpiar el
* area de texto, por medio del método
* setText() de la clase JTextComponent
*/
this.jTxtAObservaciones.setText("");
}
...
/*
* En este metodo se crea el JScrollPane y se proporciona
* la JTextArea que contendra
*/
private JScrollPane getJScrllPnlObservaciones() {
if (jScrllPnlObservaciones == null) {
jScrllPnlObservaciones = new JScrollPane();
/*
* Con este metodo se le indica al JScrollPane
* que contendra la JTextArea, a la cual le
* creara un viewport y mostrata solo una parte
* del componente
*/
jScrllPnlObservaciones.setViewportView(
getJTxtAObservaciones());
}
return jScrllPnlObservaciones;
}

/*
* En este metodo regresa nuestra JTextArea y la inicializa
* en caso de necesitarse
*/
public JTextArea getJTxtAObservaciones() {
if (jTxtAObservaciones == null) {
jTxtAObservaciones = new JTextArea();
}
return jTxtAObservaciones;
}

73
Java Avanzado Programa de Tecnología en Cómputo

...
}

JComboBox

Es un componente que combina un boton o un campo editable, con una lista


desplegable. El usuario puede seleccionar un valor de la lista. Es algo
parecido a lo que genera la etiqueta <select > del HTML.

De esta clase podemos mencionar varios métodos que nos permitirán


trabajar con los valores desplegados en la lista

void addItem(Object) . Agrega un objeto a la lista

Object getItemAt(int index) . Regresa de la lista el elemento cuyo índice se


especifica.

int getItemCount() . Regresa el número de elementos de la lista.

int getSelectedIndex() . Regresa el índice del elemento seleccionado.

Object getSelectedItem(). Regresa el elemento seleccionado.

void removeAllItems() . Quita todos los elementos de la lista.

void removeItem() . Remueve un elemento de la lista.

Para darle funcionalidad a un objeto de este tipo, podemos utilizar la


interface ActionListener, como lo hicimos para los casos anteriores.
package gui;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

/**
* Esta clase es un panel con elementos
* de un formulario.
* Se intorducen elementos de la clase
* JComboBox
* @author Ricardo Castañeda M / PROTECO
*
*/
public class Forma3 extends JPanel implements ActionListener{

...

/*

74
Java Avanzado Programa de Tecnología en Cómputo

* Se declaran los JComboBox


*/
private JComboBox jCmbBxPais = null;
private JComboBox jCmbBxCiudad = null;
...
private void initialize() {
...
jLblCiudad = new JLabel();
jLblCiudad.setText("Ciudad");
jLblPais = new JLabel();
jLblPais.setText("Pais");
/*
* Se agregan los JComboBox y sus etiquetas
*/
this.add(jLblPais);
this.add(getJCmbBxPais());
this.add(jLblCiudad);
this.add(getJCmbBxCiudad());
...
}
...
public void actionPerformed(ActionEvent e) {
...
/*
* Se agrega una verificación para el JComboBox de
* paises para obtener su valor actual y que se llame
* al método imprimeCiudad(pais), con el pais
* seleccionado, cada vez que cambie de valor
*/
if(e.getSource()==this.jCmbBxPais){
String pais=(String)jCmbBxPais.getSelectedItem();
imprimeCiudad(pais);
}
}
...
/*
* Este método es el encargado de imprimir los valores
* del JComboBox que corresponde a la ciudad, a partir
* del pais seleccionado
*/
private void imprimeCiudad(String pais) {
/*
* Se remueven todos los elementos del
* JComboBox para poder empezar a agregar
* elementos
*/
jCmbBxCiudad.removeAllItems();
/*
* Si el pais es Mexico se agregan 3
* ciudades al JComboBox previamente
* vaciado
*/
if(pais.equals("Mexico")){
jCmbBxCiudad.addItem("D. F.");
jCmbBxCiudad.addItem("Guadalajara");
jCmbBxCiudad.addItem("Monterrey");

75
Java Avanzado Programa de Tecnología en Cómputo

/*
* Ocurre lo mismo si el pais es
* Estados Unidos o Canada
*/
}else if(pais.equals("Estados Unidos")){
jCmbBxCiudad.addItem("Atlanta");
jCmbBxCiudad.addItem("Miami");
jCmbBxCiudad.addItem("Las Vegas");
}else if(pais.equals("Canada")){
jCmbBxCiudad.addItem("Montreal");
jCmbBxCiudad.addItem("Ottawa");
jCmbBxCiudad.addItem("Toronto");
}else{
jCmbBxCiudad.addItem("Seleccione el pais");
}
}
...
/*
* Se definen los métodos get para los JComboBox para
* obtenerlos, o inicializarlos y obtenerelos si fuera
* el caso
*/
private JComboBox getJCmbBxPais() {
if (jCmbBxPais == null) {
jCmbBxPais = new JComboBox();
/*
* Se agregan 3 elementos al
* JComboBox
*/
jCmbBxPais.addItem("Mexico");
jCmbBxPais.addItem("Estados Unidos");
jCmbBxPais.addItem("Canada");
jCmbBxPais.addActionListener(this);
}
return jCmbBxPais;
}

private JComboBox getJCmbBxCiudad() {


if (jCmbBxCiudad == null) {
jCmbBxCiudad = new JComboBox();
imprimeCiudad((String)jCmbBxPais.getSelectedItem());
}
return jCmbBxCiudad;
}
...
}

Una vez terminado el formulario falta darle verdadera funcionalidad. Se


propone utilizar los datos del formulario y a partir de ellos crear objetos de
una clase envolvente para guardar estos datos en un archivo.

Lo primero será crear la clase envolvente y que cada entrada del formulario
sea un atributo de la clase. Posteriormente definir métodos que nos permitan
tanto ingresar registros al archivo, así como obtener esos registros.

76
Java Avanzado Programa de Tecnología en Cómputo

package gui.formulario;
import java.io.*;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* Clase envolvente para poder almacenar los
* datos de un contacto en un archivo
* @author Ricardo Castañeda M / PROTECO
*
*/
public class Contacto {
/*
* Se declaran los atributos private para encapsularlos
*/
private String nombre="";
private String direccion="";
private String telefono="";
private String observaciones="";
private String pais="";
private String ciudad="";
/*
* Se definen los metodos set, para poder dar valores
* a los atributos
*/
public void setNombre(String nombre){
this.nombre=nombre;
}
public void setDireccion(String direccion){
this.direccion=direccion;
}
public void setTelefono(String telefono){
this.telefono=telefono;
}
public void setObservaciones(String obs){
this.observaciones=obs;
}
public void setPais(String pais){
this.pais=pais;
}
public void setCiudad(String ciudad){
this.ciudad=ciudad;
}

/*
* Se definen los métodos get para poder obtener el
* valor de los atributos
*/
public String getNombre(){
return this.nombre;
}
public String getDireccion(){
return this.direccion;
}
public String getTelefono(){
return this.telefono;
}

77
Java Avanzado Programa de Tecnología en Cómputo

public String getObservaciones(){


return this.observaciones;
}
public String getPais(){
return this.pais;
}
public String getCiudad(){
return this.ciudad;
}

/*
* En este m?todo se concatenan los valores y
* se guardan en el archivo especificado en una
* sola linea por registro
*/
public void almacenaContacto(File archivo) throws IOException{
File file=archivo;
FileOutputStream fos=new FileOutputStream(file,true);
PrintWriter pw=new PrintWriter(fos);
String entry="";
entry=nombre+":"
+direccion+":"
+telefono+":"
+observaciones+":"
+pais+":"
+ciudad;
pw.println(entry);
pw.close();
fos.close();
}
/*
* Este método lee el archivo especificado y construye
* objetos de la clase Contacto a partir de los datos
* obtenidos del archivo, para después guardar los
* objetos en un Vector que será el que resgresará al
* terminar.
*/
public static Vector regresaContactos(File archivo)
throws IOException{
Contacto c=null;
Vector v=new Vector(0,1);
File file=archivo;
FileInputStream fis=new FileInputStream(file);
BufferedReader br=new BufferedReader(
new InputStreamReader(fis));
String linea;
while((linea=br.readLine())!=null){
c=new Contacto();
StringTokenizer st=new StringTokenizer(linea,":");
String nombre=st.nextToken();
c.setNombre(nombre);
c.setDireccion(st.nextToken());
c.setTelefono(st.nextToken());
c.setObservaciones(st.nextToken());
c.setPais(st.nextToken());
c.setCiudad(st.nextToken());

78
Java Avanzado Programa de Tecnología en Cómputo

v.add(c);
}
br.close();
fis.close();
return v;
}

public static void main(String[] args) {


Contacto c=new Contacto();
c.setNombre("Ricardo");
c.setDireccion("Nicolas leon 155 F 7");
c.setTelefono("55523922");
c.setObservaciones("Ninguna");
c.setPais("Mexico");
c.setCiudad("D. F.");

Contacto sc=new Contacto();


sc.setNombre("Silvia");
sc.setDireccion("Xochi");
sc.setTelefono("55523922");
sc.setObservaciones("Si rifa");
sc.setPais("Mexico");
sc.setCiudad("D. F.");

Contacto test;
try{
//c.almacenaContacto("contactos.out");
//sc.almacenaContacto("contactos.out");
Vector v=Contacto.regresaContactos(
new File("contactos.out"));
for(int i=0;i<v.size();i++){
test=(Contacto)v.get(i);
System.out.println(i+" "+test.getNombre());
System.out.println(test.getCiudad());
}

}catch(Exception ioe){
System.out.println(ioe.getMessage());
ioe.printStackTrace();
}
}
}

Una vez hecho lo anterior solo restará dar funcionalidad al boton enviar,
mediante el método actionPerformed en la clase que define nuestro
formulario.
package gui.formulario;
public class Formulario extends JPanel implements ActionListener{
public void actionPerformed(ActionEvent e) {
...
/*
* Se modifica este bloque para que se ejecuten los
* metodos send() y reset al apretar el boton enviar
*/
if(e.getSource()==this.jBtnEnviar){

79
Java Avanzado Programa de Tecnología en Cómputo

this.send();
this.reset();
}
...
}
...
}

Además, para cuestiones de interactividad se agrega el archivo como otro


atributo private de tipo File, así como sus respetivos métodos get y set para
trabajar con él.

Menús y mensajes de alerta.

Menús

Una característica común en cualquier interfaz gráfica son precisamente los


menus, como los básicos Archivo, Editar o Ayuda, por lo que es conveniente
colocar una barra de menus en nuestra aplicación. Para poder trabajar con
menus en Java, debemos de estudiar tres clases que implementan la interfaz
MenuElement:

JMenuBar

Que es la implementación de una barra de menús, a la que se le podrán


agregar objetos de la clase JMenu, para construir el menu. Cuando se
selecciona uno de los objetos JMenu, se despliega un menu emergente,
permitiendo que el usuario seleccione uno de los JMenuItems en él.

Esta clase solo tiene un constructor que nos crea un JMenuBar vacío.

Para trabajar con los elementos del menu, podemos mencionar los siguientes
métodos.

void add(JMenu c) . Agrega el JMenu especificado al final de la barra de menús.

JMenu get(int index) . Regresa el menú de la posición especificada en la barra de


menús.

JMenu

Es una subclase de JMenuItem, y es la implementación de un menu


emergente, contenido en una barra de menús, y que su vez contiene objetos

80
Java Avanzado Programa de Tecnología en Cómputo

de tipo JMenuItem que son desplegados cuando el usuario selecciona un


elemento en la barra de menús.

Entre los métodos más importantes para nuestros fines podemos mencionar
los siguientes:

JMenuItem add(JMenuItem menuItem) . Agrega el elementos de menu especificado


al final del menú.

JMenuItem add(String s) . Crea un nuevo elemento de menú con el texto


especificado y lo agrega al final del menú.

void addSeparator() . Agrega un nuevo separador al final del menú.

JMenuItem getItem(int index) . Regresa el elemento de menú que esté en la


posición especidficada.

JMenuItem

Esta clase es la implementación de un elemento en un menú. Es subclase de


AbstractButton. Una vez creado el menú, habrá que crear instancias de la
clase JMenuItem y agregarlas, para posteriormente darle funcionalidad
mediante eventos mediante la interfaz ActionListener, como lo hemos hecho
anteriormente.

Esta clase proporciona los siguiente métodos constructores, útiles para


diversos fines.

Como se puede ver, proporciona constructores semejantes a los que


proporcionan otras clases, para crear desde objetos de tipo JMenuItem
vacíos, hasta con texto e iconos.

81
Java Avanzado Programa de Tecnología en Cómputo

Las clases anteriores son suficientes para definir la estructura del menu, pero
una vez definido habrá que colocarlo en nuestra aplicación. En el caso de la
clase JFrame, podemos utilizar el método:

void setMenuBar(JMenuBar m) . Que coloca el objeto JMenuBar especificado en el


Frame.

Una vez hecho esto faltará darle funcionalidad a los menus, tal como lo
hicimos con los demás componentes, mediante la interfaz actionListener.
Podemos seguir aplicando la misma metodología vista, de implementar la
interface y sobreescribir el método actionPerformed, y haciendo las
comparaciones correspondientes. O bien, otra forma es creando clases
anónimas para cada componente que se requiera. Veamos un ejemplo de lo
anterior.
package gui.formulario;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.*;
import javax.swing.*;
import javax.swing.JMenuItem;

/**
* Esta clase ejemplifica la estructura b?sica de una
* barra de menus
* @author Ricardo Casta?eda Mu?oz / PROTECO
*
*/
public class FrameMenu extends JFrame {
private JPanel jContentPane = null;
private JMenuBar jJMenuBar = null;
private JMenu fileMenu = null;
private JMenu contactsMenu = null;
private JMenu helpMenu = null;
private JMenuItem exitMenuItem = null;
private JMenuItem aboutMenuItem = null;
private JMenuItem almacenarMenuItem = null;
private JMenuItem listaMenuItem = null;
private JMenuItem fileMenuItem = null;

public FrameMenu() {
super();
initialize();
}

private void initialize() {


this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
/*
* Con este m?todo se coloca nuestra barra de menu
* en el JFrame
*/
this.setJMenuBar(getJJMenuBar());

82
Java Avanzado Programa de Tecnología en Cómputo

this.setSize(300, 200);
this.setContentPane(getJContentPane());
this.setTitle("JMenuBar");
}

private JPanel getJContentPane() {


if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
}
return jContentPane;
}

/*
* Este m?todo regresa la JMenuBar y la define
* en caso de ser necesario
*/
private JMenuBar getJJMenuBar() {
if (jJMenuBar == null) {
jJMenuBar = new JMenuBar();
/*
* Se agregan menus a la JMenuBar
*/
jJMenuBar.add(getFileMenu());
jJMenuBar.add(getContactsMenu());
jJMenuBar.add(getHelpMenu());
}
return jJMenuBar;
}

/*
* Los siguientes metodos definen y regresan
* cada uno de los JMenu contenidos en la barra
* de menus
*/
private JMenu getFileMenu() {
if (fileMenu == null) {
fileMenu = new JMenu();
/*
* Se define el texto que llevar? el menu
*/
fileMenu.setText("Archivo");
/*
* Con este metodo se agregan elementos
* del menu, al menu
*/
fileMenu.add(getFileMenuItem());
fileMenu.add(getExitMenuItem());

}
return fileMenu;
}

private JMenu getContactsMenu() {


if (contactsMenu == null) {
contactsMenu = new JMenu();

83
Java Avanzado Programa de Tecnología en Cómputo

contactsMenu.setText("Contactos");
contactsMenu.add(getAlmacenarMenuItem());
contactsMenu.add(getListaMenuItem());
}
return contactsMenu;
}

private JMenu getHelpMenu() {


if (helpMenu == null) {
helpMenu = new JMenu();
helpMenu.setText("Help");
helpMenu.add(getAboutMenuItem());
}
return helpMenu;
}
/*
* Los siguientes metodos definen y regresan los
* elementos de menu, de la clase JMenuItem
*/
private JMenuItem getExitMenuItem() {
if (exitMenuItem == null) {
exitMenuItem = new JMenuItem();
/*
* Se define el texto del elemento de
* menu
*/
exitMenuItem.setText("Salir");
/*
* Se le da funcionalidad al menu mediante la creaci?n
* de una clase an?nima del tipo ActionListener.
* La ventaja de esta implementaci?n es que se evitan
* las comparaciones para que el programa pueda
* proceder
*/
exitMenuItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
/*
* Este m?todo de la clase System se encarga
* de terminar con la ejecuci?n de un
* programa al ser llamado
*/
System.exit(0);
}
});
}
return exitMenuItem;
}

private JMenuItem getAboutMenuItem() {


if (aboutMenuItem == null) {
aboutMenuItem = new JMenuItem();
aboutMenuItem.setText("About");
}
return aboutMenuItem;
}

84
Java Avanzado Programa de Tecnología en Cómputo

private JMenuItem getAlmacenarMenuItem() {


if (almacenarMenuItem == null) {
almacenarMenuItem = new JMenuItem();
almacenarMenuItem.setText("Almacenar contacto");
}
return almacenarMenuItem;
}

private JMenuItem getListaMenuItem() {


if (listaMenuItem == null) {
listaMenuItem = new JMenuItem();
listaMenuItem.setText("Lista de contactos");
}
return listaMenuItem;
}

private JMenuItem getFileMenuItem() {


if (fileMenuItem == null) {
fileMenuItem = new JMenuItem();
fileMenuItem.setText("Seleccionar archivo");
}
return fileMenuItem;
}

public static void main(String[] args) {


FrameMenu application = new FrameMenu();
application.setVisible(true);
}
}

JDialog

Es la clase principal para crear ventanas de diálogo. Se puede usar esta clase
para crear un diálogo personalizado, o invocar los métodos de la clase
JOptionPane para crear variedad de diálogos estándar. Esta clase tiene gran
variedad de métodos constructores como se puede ver acontinuacion.

Como se puede observar, en la mayoría de los constructores, hay que indicar


un contenedor que sea el propietario de nuestra ventana de diálogo, y puede
ser un Frame o bien otro Dialog. En varios de estos métodos es posible
indicar un título, de la misma manera que lo hicimos para la clase JFrame.
Además tiene en varios casos un parémetro booleano que va a indicar si el
JDialog va a ser modeal o no, esto significa que si es modal, el JDialog tomará
control del programa y no lo regresará al propietario hasta que el diálogo sea
cerrado.

Crear ventanas de diálogo es muy semenjante a la creación de JFrames, pues


se siguen los mismos pasos. Se puede heredar de la clase JDialog, para
después determinar el LayoutManager, para posteriormente agregar
componentes y por último darles funcionalidad. Veamos un ejemplo.

85
Java Avanzado Programa de Tecnología en Cómputo

package gui;
import java.awt.*;
import javax.swing.*;

/**
* Un sencillo ejemplo de como crear una ventana
* de dialogo a partir de la clase JDialog
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class Dialogo extends JDialog {

private JPanel jContentPane = null;

public Dialogo(Frame owner, String title, boolean modal){


super(owner, title, modal);
initialize();
}

private void initialize() {


this.setSize(300, 200);
this.setContentPane(getJContentPane());
}

/*
* Se define el contenedor como se hizo para la clase
* JFrame
*/
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
}
return jContentPane;
}

/*
* En el método main se crea un objeto JFrame que
* servirá de contenedor para al JDialog
*/
public static void main(String[] args) {
JFrame jf=new JFrame("JFrame propietario");
/*
* El cuadro de dialogo creado tendrá como
* propietario al JFrame anterior, un título
* especificado, y además será modal, es
* decir que cuando sea invocado el cuadro de
* diálogo, no devolverá el control a su
* propietario hasta que sea cerrado
*/
Dialogo d=new Dialogo(jf,"JDialog",true);
jf.setVisible(true);
jf.setSize(400,300);
d.setVisible(true);
d.setSize(300,200);
}

86
Java Avanzado Programa de Tecnología en Cómputo

Modifiquemos la clase anterior para que muestre la lista de contactos


almacenados en el archivo especificado, agregando un objeto JTextArea con
un JScrollPane, para colocar los datos obtenidos.
package gui.formulario;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.util.*;
/**
* Una clase creada para que se comporte como
* un cuadro de diálogo en el que se mostrarán
* los datos de los contactos obtenidos a partir
* de un archivo
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class MostrarContactos extends JDialog {

File archivo=null;
private JPanel jContentPane = null;
private JScrollPane jScrollPane = null;
private JTextArea jTextArea = null;

public MostrarContactos(
Frame owner, String title,
boolean modal,File archivo){
super(owner, title, modal);
this.archivo=archivo;
initialize();
}

private void initialize() {


this.setSize(300, 200);
this.setContentPane(getJContentPane());
try{
listarContactos();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
/*
* Este metodo será el encargado de obtener los contactos
* del archivo, e imprimir los datos en el area de texto
*/
private void listarContactos() throws IOException{
Vector v=Contacto.regresaContactos(this.archivo);
for(int i=0;i<v.size();i++){
Contacto c=(Contacto)v.get(i);
jTextArea.append(c.getNombre()+"\n");
jTextArea.append(c.getTelefono()+"\n");
jTextArea.append(c.getDireccion()+"\n");
jTextArea.append(c.getCiudad()+"\n");

87
Java Avanzado Programa de Tecnología en Cómputo

jTextArea.append(c.getPais()+"\n");
jTextArea.append(c.getObservaciones()+"\n"+"\ n");
}
}

/*
* Se define el contenedor como se hizo para la clase
* JFrame
*/
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
jContentPane.add(getJScrollPane(),
java.awt.BorderLayout.CENTER);
}
return jContentPane;
}

private JScrollPane getJScrollPane() {


if (jScrollPane == null) {
jScrollPane = new JScrollPane();
jScrollPane.setViewportView(getJTextArea());
}
return jScrollPane;
}

private JTextArea getJTextArea() {


if (jTextArea == null) {
jTextArea = new JTextArea();
/*
* Se deshabilita que el usuario pueda
* editar el texto mostrado en el area
* de texto
*/
jTextArea.setEditable(false);
}
return jTextArea;
}

/*
* En el método main se crea un objeto JFrame que
* servirá de contenedor para al JDialog
*/
public static void main(String[] args) {
JFrame jf=new JFrame("JFrame propietario");
/*
* El cuadro de dialogo creado tendrá como
* propietario al JFrame anterior, un título
* especificado, y además será modal, es
* decir que cuando sea invocado el cuadro de
* diálogo, no devolverá el control a su
* propietario hasta que sea cerrado
*/
MostrarContactos d=new MostrarContactos(
jf,"Contactos disponibles",

88
Java Avanzado Programa de Tecnología en Cómputo

true,"contactos.out");
jf.setVisible(true);
jf.setSize(400,300);
d.setVisible(true);
d.setSize(300,200);
}
}

JOptionPane

Esta clase hace un sencillo cuadro de diálogo emergente que avisa a los
usuarios por un valor o informe.

Esta clase puede ser difícil por la grán cantidad de métodos que contiene, sin
embrago provee de diversos métodos estáticos para crear ventanas de
diálogo predefinidas, que se pueden agrupar en los siguientes:

Nombre del método Descripción

showConfirmDialog Hace una pregunta de confirmación

showInputDialog Aviso de alguna entrada

showMessageDialog Informa al usuario acerca de algo


que ha ocurrido

showConfirmDialog Es la unificación de los tres


anteriores

Todos los diálogos anteriores son modales. Es decir que bloquean el hilo
actual hasta que la interacción con el usuario sea finalizada.

Cada uno de estos métodos son sobrecargados, por lo que pueden requerir
uno o varios de los siguientes parámetros.

ParentComponent.Define el componente padre o propietario del cuadro de


diálogo. Hay dos formas de utilizarlo. La primera es especificar
explícitamente el Frame propietario del cuadro de diálogo. Asi mismo este
parámetro puede ser null, en este caso el Frame predeterminado es usado
como propietario, y el diálogo es centrado en la pantalla.

Message. Un mensaje descriptivo es colocado en el cuadro de diálogo.


Usualmente el mensaje es un String. Sin embargo el tipo de parámetro es un
Object. Su interpretación depende del tipo.

89
Java Avanzado Programa de Tecnología en Cómputo

Define el estilo del mensaje, lo que hará que la apariencia del


MessageType.
cuadro dependa del valor especidficado:

ERROR_MESSAGE

INFORMATION_MESSAGE

WARNING_MESSAGE

QUESTION_MESSAGE

PLAIN_MESSAGE

optionType.Define los botones de opción que aparecerán en el cuadro de


diálogo. Puede tener los siguientes valores

DEFAULT_OPTION

YES_NO_OPTION

YES_NO_CANCEL_OPTION

OK_CANCEL_OPTION

options. Es una opción más detallada para definir botones de opción que
aparecerán en el cuadro de diálogo. El valor usual para el parámetro options
es un arreglo de String's, pero el parámetro es un arreglo de object's. Un
botón es creado por cada objeto dependiendo del tipo:

Component. El componente es agregado a la fila de botones


directamente.

Icon. El JButton es creado con este, como si fuera una etiqueta.

Se le aplica el método toString() al objeto y el resultado es


Otro.
utilizado como etiqueta del botón.

Icon. Un icono decorativo es colocado en el cuadro de diálogo.

Title. Título del cuadro de diálogo.

InitialValue. La selección predeterminada.

Veamos un ejemplo.
package gui;
import java.awt.*;

90
Java Avanzado Programa de Tecnología en Cómputo

import javax.swing.*;
import java.awt.event.*;
import javax.swing.JButton;

/**
* Muestra como generar cuadros de diálogo predefinidos
* mediante la clase JOptionPane
* @author Ricardo Castañeda M / PROTECO
*
*/
public class Options extends JFrame {

private JPanel jContentPane = null;


private JButton jButton = null;
private JButton jButton1 = null;
private JButton jButton2 = null;
private JButton jButton3 = null;

public Options() {
super();
initialize();
}

private void initialize() {


this.setSize(300, 200);
this.setVisible(true);
this.setContentPane(getJContentPane());
this.setTitle("JFrame");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}

private JPanel getJContentPane() {


if (jContentPane == null) {
jContentPane = new JPanel();
/*
* Se agregan 4 botones al JFrame
*/
jContentPane.add(getJButton(), null);
jContentPane.add(getJButton1(), null);
jContentPane.add(getJButton2(), null);
jContentPane.add(getJButton3(), null);
}
return jContentPane;
}

private JButton getJButton() {


if (jButton == null) {
jButton = new JButton();
jButton.setText("showMessageDialog");
/*
* Con la llamada al método siguiente, se le
* da funcionalidad al botón. Se define una
* clase anónima que implementoa ActionListener,
* en la cual se hace una llamada al método estático

91
Java Avanzado Programa de Tecnología en Cómputo

* showMessageDialog de la clase JOptionPane. Esto


* es que cuando se presione el botón, se mostratá
* un mensaje de alerta.
*/
jButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(
null, "alert", "alert",
JOptionPane.ERROR_MESSAGE);
}
});
}
return jButton;
}

private JButton getJButton1() {


if (jButton1 == null) {
jButton1 = new JButton();
jButton1.setText("showConfirmDialog");
/*
* Se define de nuevo una clase anónima, pero
* en este caso al presionar el botón se
* mostrará un mensaje de confirmación
*/
jButton1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JOptionPane.showConfirmDialog(null,
"choose one", "choose one",
JOptionPane.YES_NO_OPTION);
}
});
}
return jButton1;
}
private JButton getJButton2() {
if (jButton2 == null) {
jButton2 = new JButton();

jButton2.setText("showOptionDialog");
/*
* En este caso se mostrará un cuadro de opciones
* con botones personalizados por el usuario
*/
jButton2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
Object[] options = { "Si", "No",
"Quien sabe" };
JOptionPane.showOptionDialog(null,
"Click OK para continuar", "Warning",
JoptionPane.DEFAULT_OPTION,
JOptionPane.WARNING_MESSAGE,
null, options, options[0]);
}
});
}
return jButton2;

92
Java Avanzado Programa de Tecnología en Cómputo

private JButton getJButton3() {


if (jButton3 == null) {
jButton3 = new JButton();
jButton3.setText("showInputDialog");
/*
* El cuadro de diálogo mostrado es un cuadro que
* contiene un textfield, lo que nos permitirá dar
* un valor de entrada.
*/
jButton3.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JOptionPane.showInputDialog("Coloque un
valor de entrata");
}
});
}
return jButton3;
}

public static void main(String[] args) {


Options o=new Options();

}
}

Tomando en cuenta lo anterior podemos utilizar esta clase para agregar


algunos de estos cuadros de diálogo a nuestro ejemplo de la agenda, por
ejemplo poniendo mensajes en los bloques catch, informando de las
excepciones lanzadas, o bien como confirmación del almacenamiento de un
contacto. Por ejemplo, veamos el método send() de la clase Formulario:
package gui.formulario;
...
public class Formulario extends JPanel implements ActionListener{
...
private void send() {
Contacto c=new Contacto();
c.setNombre(jTFNombre.getText());
c.setDireccion(jTFDireccion.getText());
c.setTelefono(jTFTelefono.getText());
c.setObservaciones(jTxtAObservaciones.getText());
c.setPais((String)jCmbBxPais.getSelectedItem());
c.setCiudad((String)jCmbBxCiudad.getSelectedItem());
try{
/*
* Se guarda el resultado del mensaje de confirmación
*/
int option=JOptionPane.showConfirmDialog(this,
"Desea agregar el contacto a la lista",
"Confirmacion",JOptionPane.YES_NO_OPTION);
/*
* Si se escogió la opción YES_OPTION, se prodece a
* guardar los datos en el archivo

93
Java Avanzado Programa de Tecnología en Cómputo

*/
if(option==JOptionPane.YES_OPTION)
c.almacenaContacto(this.file);

}catch(IOException ioe){
/*
* Se manda un mensaje de alerta en caso de que no se
* encuentre el archivo especificado
*/
JoptionPane.showMessageDialog(this,
"Archivo de contactos no encontrado");
}

}
...
}
JFileChooser

Básicamente consiste en un mecanismo simple para poder seleccionar un


archivo. Esta clase provee una GUI para navegar en el sistema de archivos, y
así ir escogiendo un archivo o directorio de la lista.

Podemos mencionar algunos métodos importantes de esta clase.

int showOpenDialog(Component parent). Muestra un cuadro de diálogo emergente


para abrir archivos.

int showSaveDialog(Component parent). Muestra un cuadro de diálogo emergente


para guardar archivos.

File getSelectedFile(). Regresa el archivo seleccionado.

File getCurrentDirectory() . Regresa el directorio actual.

Veamos el código de un sencillo editor de texto.


package gui;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;

/**
* Sencillo editor de texto plano que ejemplifica
* el uso de la clase JFileChooser
* @author Ricardo Castañeda M / PROTECO
*
*/
public class SeleccionaArchivo extends JFrame {

private JPanel jContentPane = null;

94
Java Avanzado Programa de Tecnología en Cómputo

private JPanel jPanel = null;


private JButton jButton = null;
private JButton jButton1 = null;
private JScrollPane jScrollPane = null;
private JTextArea jTextArea = null;

public SeleccionaArchivo() {
super("Editor de texto");
initialize();
}

private void initialize() {


this.setSize(400, 300);
this.setVisible(true);
this.setContentPane(getJContentPane());
this.setTitle("JFrame");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}

private JPanel getJContentPane() {


if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
jContentPane.add(getJPanel(),
java.awt.BorderLayout.NORTH);
jContentPane.add(getJScrollPane(),
java.awt.BorderLayout.CENTER);
}
return jContentPane;
}

private JPanel getJPanel() {


if (jPanel == null) {
jPanel = new JPanel();
jPanel.add(getJButton(), null);
jPanel.add(getJButton1(), null);
}
return jPanel;
}

/*
* Este botón llama a al método open() cada vez
* que sea presionado
*/
private JButton getJButton() {
if (jButton == null) {
jButton = new JButton();
jButton.setText("Abrir");
jButton.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e) {
open();
}
});

95
Java Avanzado Programa de Tecnología en Cómputo

return jButton;
}

/*
* Este botón llama a al método save() cada vez
* que sea presionado
*/
private JButton getJButton1() {
if (jButton1 == null) {
jButton1 = new JButton();
jButton1.setText("Guardar");
jButton1.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e) {
save();
}
});
}
return jButton1;
}

private JScrollPane getJScrollPane() {


if (jScrollPane == null) {
jScrollPane = new JScrollPane();
jScrollPane.setViewportView(getJTextArea());
}
return jScrollPane;
}

private JTextArea getJTextArea() {


if (jTextArea == null) {
jTextArea = new JTextArea();
}
return jTextArea;
}

private void save(){


/*
* Se comienza instanciando un objeto de la
* clase JFileChooser
*/
JFileChooser jfc=new JFileChooser();
/*
* Se muestra el cuadro de diálogo guardar y
* se almacena el resultado
*/
int opcion=jfc.showSaveDialog(this);
/*
* Se compara para verificar que el resultado
* sea afirmativo y se continua
*/
if(opcion==JFileChooser.APPROVE_OPTION){
/*
* Se utiliza el método getSelectedFile()
* para hacer referencia al archivo

96
Java Avanzado Programa de Tecnología en Cómputo

*/
File file=jfc.getSelectedFile();
/*
* A continuación se lee el contenido de la
* JTextArea y se guarda en el archivo
*/
try{
FileOutputStream fos=new FileOutputStream(file);
PrintWriter pw= new PrintWriter(fos);
pw.println(jTextArea.getText());
pw.close();
fos.close();
}catch(IOException ioe){
JoptionPane.showMessageDialog(null,
"Error al escribir en el archivo");
}
}
}

private void open(){


JFileChooser jfc=new JFileChooser();
/*
* Se utiliza el método showOpenDialog()
* para mostrar un cuadro de dialogo abrir
*/
int opcion=jfc.showOpenDialog(this);
if(opcion==JFileChooser.APPROVE_OPTION){
/*
* Se utiliza el método getSelectedFile()
* para obtener la referencia al archivo
*/
File file=jfc.getSelectedFile();
try{
/*
* A continuación se lee el contenido del
* archivo y se muestra en la JTextArea
*/
FileInputStream fis=new FileInputStream(file);
BufferedReader br = new BufferedReader(
new InputStreamReader(fis));
String linea=null;
jTextArea.setText("");
while( (linea=br.readLine())!=null ){
jTextArea.append(linea+"\n");
}
br.close();
fis.close();
}catch(IOException ioe){
JoptionPane.showMessageDialog(null,
"Error al escribir en el archivo");
}
}
}
public static void main(String[] args) {
new SeleccionaArchivo();
}

97
Java Avanzado Programa de Tecnología en Cómputo

Ahora implementemos lo anteriormente visto para terminar nuestro


programa de la agenda.

Lo primero es crear un JFrame, con un menú semejante al que utilizamos


anteriomente, con las siguientes funcionalidades

Menu Elemento Accion a realizar

Archivo Selecciona Se abrira un JFileChooser para el archivo en el


r archivo que se guardan y leeran los registros

Salir Se sale por completo de la agenda

Contactos Almacenar Se guardan los datos del formulario, en el archivo


contactos

Lista de Se abre una ventana de diálogo donde se listan


contactos los contactos leeidos del archivo

Y en el frame se muestra un formulario para poder ingresar registros.

Como se puede dar uno cuenta, lo que se propone es precisamente lo que ya


hicimos con algunas de las clases anteriores, ahora solo habrá que ir
unificando todas las clases para tener completo el programa.

Mostrar Imágenes y dibujar figuras en un contenedor: Clase Graphics

La clase Graphics es la clase abstracta base para todos los contextos gráficos
que permiten a una aplicación dibujar sobre los componente, así como en
imágenes sobre la pantalla.

En esta clase de declaran muchos métodos, que nos servirán para dibujar
una gran cantidad de figuras, incluso imágenes.

boolean drawImage( Image img, int x, int y, Color bgcolor, ImageObserver observer ).

boolean drawImage( Image img, int x, int y, ImageObserver observer )

boolean drawImage( Image img, int x, int y, int width, int height, Color bgcolor,
ImageObserver observer )

98
Java Avanzado Programa de Tecnología en Cómputo

Los métodos drawImage(), dibujan la imágen especificada en las


coordenadas especificadas en pixeles del contenedor. Ha que recordar que el
origen es la esquina superior izquierda del contenedor, y el valor de las
abscisas y ordenadas se incrementa a la derecha y hacia abajo
respectivamente

La interfaz ImageObserver no se tratará muy a fondo en este manual, pero


bastará por el momento con saber que es implementada por varias clases de
componentes y contenedores, que podremos utilizar para satisfacer este
parámetro.

Dibuja una línea que va del punto


void drawLine( int x1, int y1, int x2, int y2 ).
especificado por las primeras coordenadas, a otro especificado por las
segundas.

void drawOval(int x, int y, int width, int height). Dibuja el contorno de un óvalo.

void drawPoligon(int[] xPoint, int[] yPoints, int nPoints). Dibuja un polígono cerrado
definido por los arreglos de coordenadas.

Dibuja la secuencia de líneas


void drawPolyLine(int[] xPoint, int[] yPoints, int nPoints).
conectadas por los puntos especificados por los arreglos de coordenadas.

void drawRect(int x, int y, int width, int height). Dibuja el contorno del rectángulo
especificado.

Dibuja el texto especificado utilizando el


void drawString(String str, int x, int y).
contexto gráfico actual de fuentes y color.

void fillOval(int x, int y, int width, int height) . Dibuja el óvalo especificado.

void fillPoligon(int[] xPoint, int[] yPoints, int nPoints). Dibuja un polígono relleno
definido por los arreglos de coordenadas.

void fillRect(int x, int y, int width, int height) . Dibuja el rectángulo especificado.

Como se puede ver, esta clase tiene muchos métodos para cuestiones de
dibujo, sin embargo, ya que es una clase abstracta, no es posible crear
objetos a partir de ella directamente, por lo que habrá que buscar otra forma.

Si se revisa la clase Component, que es la clase padre de todos los


componentes y contenedores, tanto de AWT como de swing, se puede
observar que se define el método paint(Graphics g) , que se llama cuando el
contenido del componente debe ser pintado, y además el parámetro ya
contiene el contexto gráfico del contenedor.

99
Java Avanzado Programa de Tecnología en Cómputo

Entonces para poder pintar el componente, bastará con sobreescribir el


método paint() , utilizando el parámetro g, para poder dibujar sobre el
componente.

Otro método sobresaliente de la misma clase Component es el método repaint


() , que cuando se llama, vuelve a dibujar el componente.

Un método semejante al método paint() , es el método paintComponent(Graphics


g), definido en la clase JComponent, y que realiza una función semenjante a
la del método paint() , por lo que el método paintComponent() , puede ser
sobreescrito y utilizado en los componentes que hereden de la clase
JComponent.

Veamos un par de ejemplos, el primero dibujando una imágen a partir de un


archivo, y el segundo dibujando figuras con los métodos de la clase Graphics.
package gui.graphics;

import java.awt.*;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.io.File;

/**
* Esta clase muestra el uso de la
* clase Graphics para dibujar imagenes
* @author PROTECO
*
*/
public class Imagen extends JFrame {
Tabla2 figuras;

Container c;

JScrollPane panel;

public Imagen() {
super("Vamos a Dibujar");
figuras = new Tabla2();
c = getContentPane();
panel = new JScrollPane(figuras);
c.add(panel);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}

public static void main(String args[]) {


Imagen figura = new Imagen();
figura.setSize(300, 200);
figura.setVisible(true);
}
}
/*

100
Java Avanzado Programa de Tecnología en Cómputo

* La clase Tabla2 es la que contendrá la imágen


*/
class Tabla2 extends JPanel {
public void paint(Graphics g) {
try{
/*
* Se utiliza la clase ImageIO, para tener un
* objeto de tipo Image
*/
Image img=ImageIO.read(new File("proteco.jpg"));
g.drawString("Una imagen en un contenedor",45,30);
/*
* El objeto Image obtenido se le proporciona al
* metodo drawImage(), para tener la imágen en el
* contenedor
*/
g.drawImage(img,100,50,100,100,this);
}catch(Exception e){
e.printStackTrace();
}
}
}

En el siguiente ejemplo, se verá como se dibujan figuras en un contenedor.


package gui.graphics;

import javax.swing.*;
import java.awt.*;

/**
* Esta clase muestra el uso de la
* clase Graphics para dibujar figuras
* @author PROTECO
*
*/
public class Figuras extends JFrame {
Tabla figuras;

Container c;

JScrollPane panel;

public Figuras() {
super("Vamos a Dibujar");
figuras = new Tabla();
c = getContentPane();
panel = new JScrollPane(figuras);
c.add(panel);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}

public static void main(String args[]) {


Figuras figura = new Figuras();
figura.setSize(300, 200);
figura.setVisible(true);
}

101
Java Avanzado Programa de Tecnología en Cómputo

}
/*
* La clase Tabla es la que contendrá las figuras
*/
class Tabla extends JPanel {
public void dibujaCuadrado(Graphics g) {
/*
* Se utiliza el método setColor() para
* cambiar el colo de las figuras que se
* definan después
*/

g.setColor(Color.YELLOW);
/*
* Se hacen llamadas al los métodos
* drawwRect() y fillRect(), para
* dibujar el contorno de un rectángulo
* y un rectángulo con relleno
*/
g.drawRect(10, 10, 80, 80);
g.setColor(Color.RED);
g.fillRect(100, 10, 80, 80);
}

public void dibujaCirculo(Graphics g) {


g.setColor(Color.BLUE);
/*
* Se dibuja el contorno de un ovalo con
* el metodo drawOval()
*/
g.drawOval(200, 10, 80, 80);
}

public void escribe(Graphics g, String texto) {


/*
* Se escribe un texto con el metodo
* drawString()
*/
g.drawString(texto, 10, 120);
}

public void dibujaLinea(Graphics g) {


/*
* Se dibuja una linea con el metodo
* drawLine()
*/
g.drawLine(10, 130, 200, 130);
}
/*
* Se sobreescibe el método paint para así
* utilizar el contexto gráfico del contenedor
* actual, y asi poder dibujar las figuras
*/
public void paint(Graphics g) {
/*
* Se hacen llamadas a los demás metodos

102
Java Avanzado Programa de Tecnología en Cómputo

* definidos, indicando como parametro el


* objeto Graphics
*/
dibujaCuadrado(g);
dibujaCirculo(g);
escribe(g, "Estamos Dibujando en JAVA!!!");
dibujaLinea(g);
}
}

Aplicaciones embebidas en una página web: Clases Applet y JApplet

Los applets son GUI's hechas en java, que tienen la particularidad de poder
se incluidas en una página web, mediante la etiqueta <applet > de html, lo
que abre la posibilidad de tener complejas aplicaciones en una página web.

Debido a que los applets son programas hechos en java, son independientes
de la plataforma y del navegador, con el único requisito de que en el lado del
cliente se cuente con la JVM correspondiente.

Sin embargo, debido al esquema de seguridad de java, las aplicaciones de


este tipo, están imposibilitados para realizar operaciones de entrada/salida
en el lado del cliente. Es decir que en principio no puden crearse conexiones
o leer y escribir en archivos.

Aun así existe una forma de poder realizar este tipo de acciones, a travéz de
la creación de applets, firmados, lo cuál solo se comentará por el momento y
se remite al lector a la documentación correspondiente.

Para java un applet es un contenedor como los que hemos estado


manejando. Por lo tanto existe una clase Applet contenida en el paquete
java.applet, que nos representa a este tipo de contenedores, que a su vez es
una subclase de la clase Panel de AWT, por lo que se trabajará
preferentemente con la clase JApplet de swing, heredada de la citada clase
Applet, pero nos será útil por los métodos definidos en ella.

Para crear un applet, se siguen la misma metodología de las GUI's


anteriormente estudiadas, con las siguientes diferencias.

El método invocado por el browser (navegador) para informar que el applet


ha sido cargado en el sistema es el método init(), definido en la clase Applet.
Debido a esto es en este método en el cual se comenzará con la ejecución
del programa.

Debido a esto no tiene mucho sentido definir un método constructor, pues


como ya se comentó al cargar el applet, el navegador llama al método init().

103
Java Avanzado Programa de Tecnología en Cómputo

Veamos ahora como crear un applet básico.


//<applet code= "gui.applet.AppletBasico" width = 3 00 height = 20 0 > < / a p plet >
package gui.applet;

import java.awt.*;
import javax.swing.*;

/**
* Este ejemplo genera un applet básico heredado
* de la clase JApplet
* @author Ricardo Castañeda M. / PROTECO
*
*/
public class AppletBasico extends JApplet {

private JPanel jContentPane = null;

/*
*Se sobre escribe el método init() para
*inicializar el applet
*
*/
public void init() {
/*
* Se define un color de fondo y tamaño
* como se hace para otros contenedores
*/
this.setSize(300, 200);
this.setBackground(Color.ORANGE);
/*
* Se determina al JPanel como contenedor
*/
this.setContentPane(getJContentPane());
repaint();
}

/*
* Se se inicializa el JPanel
*/
private JPanel getJContentPane() {
if (jContentPane = = null) {
jContentPane = new JPanel();
jContentPane.setLayout(new GridLayout(5,2));
}
return jContentPane;
}

/*
* Se sobreescribe el metodo paint, para que
* dibuje algunos gráficos en el applet
*
*/
public void paint(Graphics g){
Font f=new Font("Arial",Font.ITALIC,30);

104
Java Avanzado Programa de Tecnología en Cómputo

for(int i=0;i < 150;i + = 3 ) {


if((i%2)! = 0)
g.setColor(Color.GREEN);
else
g.setColor(Color.BLUE);
g.drawRect(150, 50, i+10, i);
}
g.setFont(f);
g.drawString("Applet Basico", 10, 30);
}
}

Se puede observar que fuera de los puntos comentados, la creación de un


applet es idéntica a la de cualquier otra GUI. Pero bien ahora falta ver el
applet funcionando.

Debido a que un applet no tiene un método main(), el ejecutarlo mediante la


aplicación java o la aplicación javaw, provocará que se lanze una excepción
debido a la falta de este método, y aunque se definiera, un applet necesita
un contexto específico para poderse ejecutar, como lo es una ventanda de
un navegador. Por lo que se puede empezar probando la aplicación a través
de un navegador.

Para poder hacer esto es necesario conocer la etiqueta <applet > de html, la
cual puede tener los siguientes atributos

code. En él se especifica la clase principal de la aplicación, o bien la que


representa al applet que será mostrado en el navegador. En caso de que la
clase esté empaquetada, se especifican los paquetes, utilizando puntos como
separador, hasta que se llegue al nombre de la clase.

archive .
Este atributo se utiliza cuando se tienen las clases almacenadas en
un archivo *.jar, pues el nombre del archivo se especifica en este atributo.

width. Indica el ancho del applet en el navegador

height. Indica la altura del applet en el navegador

Ejemplo:

<applet code =" mipaqueta.Miclase" archive = " miJar.jar" width = 10 0 height = 5 0 > < / a pplet >

También podemos probar el applet mediante un programa incluido en el JDK,


llamado appletviewer. Para poder utilizar el appletviewer es necesario
especificar la etiqueta <applet > , comentada al principio del código.

La sintaxis del appletviewer es la siguiente:

105
Java Avanzado Programa de Tecnología en Cómputo

appletviewer <options > url(s)

Donde url, usualmente representa al archivo con el código fuente (*.java),


con la etiqueta <applet > , como ya se ha comentado, pues el appletviewer
obtiene la información esa etiqueta, para de hay ejecutar el archivo
correspondiente.

Veamos el código de la página html que llamaría al applet básico anterior.


<html>
<head>
<title>Applet basico</title>
</head>
<body>
<applet code="gui.applet.AppletBasico" width=300
height=200></applet>
</body>
</html>

Nótese como al estar empaquetada la clase AppletBasico, se indican tambien


los paquetes, además de que es importante cerrar la etiqueta applet.

Cargar imágenes en applets

Como ya se comentó, un applet ordinario no puede acceder al sistema de


archivos del cliente, por lo cual no se pueden visualizar imágenes como lo
hemos hecho mediante flujos. Sin embargo es posible leer estas imágenes
desde el servidor haciéndo referencia a ellos mediante su URL (Uniform
Resource Locator).

El URL es una cadena de caracteres con la cual se le asigna una direccion


única a cada uno de los recursos en internet. El URL combina el nombre de
la computadora, el directorio donde se encuentra, el nombre del archivo y el
protocolo utilizado. El formato general es :

protocolo://computadora/directorio/archivo

Java proporciona una clase del paquete java.net, que da soporte a los URL, y
es precisamente la clase homóloga URL cuyos constructores son los
siguientes:

106
Java Avanzado Programa de Tecnología en Cómputo

Con lo que podemos crear objetos de esta clase de varias formas.

Ahora podemos utilizar dos métodos definidos en la clase Applet que nos
dará la solución al problema.

URL getDocumentBase() .
Que nos regresa el URL del documento en el cual el
applet está embedido. Bien puede ser solo un directorio o un archivo *.jar.

Image getImage(URL url)

Image getImage(URL url, String name) .


Estos dos métodos nos regresan una
imágen, que bien podemos utilizar para el desarrollo de nuestro applet. La
diferencia es que en el primero el nombre de la imágen debe de ser parte del
URL, mientras que en el segundo, el URL hace referencia al directorio que
contiene la imágen, mientras que se le proporciona el nombre del archivo
como un parámetro aparte.

Veamos el ejemplo anterior, agregándole algunas líneas para poder dibujar la


imágen a partir de un archivo.
package gui.applet;
import javax.swing.*;
import java.net.URL;
import java.awt.*;
/**
* Esta clase muestra como dibujar una imágen
* en un applet a partir de un archivo en el
* servidor
* @author Ricardo Castañeda M / PROTECO
*
*/
public class AppletImage extends JApplet {

private URL url=null;


private Image image;

private JPanel jContentPane = null;

public void init() {

107
Java Avanzado Programa de Tecnología en Cómputo

/*
* Se utiliza el metodo getDocumentBase()
* para obtener el URL del directorio que
* contiene el applet
*/
url=getDocumentBase();
/*
* Se utiliza el método getImage() para
* obtener la imagen a partir del URL y
* el nombre del archivo
*/
image=getImage(url,"proteco.jpg");
this.setSize(300, 200);
this.setBackground(Color.ORANGE);
this.setContentPane(getJContentPane());
repaint();
}

private JPanel getJContentPane() {


if (jContentPane == null) {
jContentPane = new JPanel();
}
return jContentPane;
}

public void paint(Graphics g){


Font f=new Font("Arial",Font.ITALIC,30);
for(int i=0;i<150;i+=3){
if((i%2)!=0)
g.setColor(Color.GREEN);
else
g.setColor(Color.BLUE);
g.drawRect(150, 50, i+10, i);
}
g.setFont(f);
g.drawString("Applet con Imagen", 10, 30);
/*
* Se dibuja la imágen en el applet con el
* método drawImage() de la clase Graphics
*/
g.drawImage(image, 50, 50, 100, 100, this);
}
}

Crear hipervínculos en Applets.

Otra utilidad de la clase URL en los applets es la posibilidad de abrir páginas


web desde un programa de este tipo.

Para poder realizar esto hay que estudiar una interfaz llamada
AppletContext, que corresponde al entorno del applet, que bien puede ser el
documento que contiene al applet u otros applets en el mismo documento.
Esta interfaz es útil por ejemplo para comunicar entre si los applets del

108
Java Avanzado Programa de Tecnología en Cómputo

mismo documento, o bien hacer cambios en este mismo, como es el caso


que estudiamos ahora.

Dentro de esta interfaz, podemos mencionar al siguiente método


sobrecargado

void showDocument(URL url) .


Reemplaza la página web actual por la indicada en
el parámetro. Este método puede ser ignorado por contextos que no son
navegadores.

void showDocument(URL url, String target) .


Pide que el navegador o el appletviewer
muestre la página web indicada como argumento. El argumento target tiene
el mismo significado que el atributo target de la etiqueta <a href> de HTML.

Al llamar alguno de estos métodos podemos crear hipervínculos, semejantes


a los de HTML.

package gui.applet;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
/**
* Esta clase muestra un JTextField donde se
* le indicará un URL, el cual abrirá en un
* navegador aparte
* @author PROTECO
*
*/
public class AppletLink extends JApplet {

JButton enviar = new JButton("IR");

JTextField direccion = new JTextField(10);

public void init() {


Container cp = getContentPane();
cp.setLayout(new FlowLayout());
enviar.addActionListener(new Al());
cp.add(direccion);
cp.add(enviar);

class Al implements ActionListener {


public void actionPerformed(ActionEvent ev) {
/*
* Se obtiene el texto del JTextField
*/
String url = direccion.getText();

109
Java Avanzado Programa de Tecnología en Cómputo

try {
/*
* Con el texto obtenido se forma un URL
*/
URL u = new URL(url);
/*
* Se obtiene el AppletContext(), para
* llamar a su método showDocument()
*/
getAppletContext().showDocument(u, "_blank");

} catch (Exception ex) {


/*
* Se muestra el siguiente mensaje en caso
* de que la direccion sea mal formada
*/
JOptionPane.showMessageDialog(null,
"URL invalida");
}
}
}
}

110
Java Avanzado Programa de Tecnología en Cómputo

Program ación distribuida: paque t e java.ne t


La programación distribuida básicamente es desarrollar aplicaciones que nos
permitar comunicar varias computadoras conectadas en una red. Para este
caso utilizaremos específicamente el protocolo TCP/IP que es en el que se
basan la mayor parte de las redes, incluyendo Internet.

Conceptos básicos

Para entender lo necesario para nuestros fines de este protocolo se


requieren dos conceptos que veremos su utilidad más adelante.

Dirección IP

Una dirección IP es una serie de cuatro ternas de números separados por


puntos que representan a una computadora en una red. Algunas direcciones
o segmentos interesantes son:

127.0.0.1, que representa al host local, es la manera de referirnos a nuestra


misma computadora, usualmente para desarrollo o pruebas.

10.0.0.0 - 10.255.255.255 Este segmento de privadas de clase A, que no se


pueden ver desde internet, pero son útiles en redes locales

172.16.0.0- 172.31.255.255. Este es un segmento de direcciones privadas de


clase B

192.168.0.0- 192.168.255.255. Este es un segmento de direcciones privadas


de clase C

En java las direcciones IP se representan mediante la clase InetAddress,


cuyos métodos más interesantes son:

String getHostAddress(). Regresa la dirección IP en una presentación textual.

String getHostName(). Regresa el nombre para la dirección IP.

Puerto TCP

Un puerto es una numeración lógica que se asigna a las conexiones, tanto en


el origen como en el destino. En una computadora existen 65534 puertos,
pero por cuestiones de reservación de recursos es deseable utilizar a partir
del 1024 para nuestros programas. Además un puerto no puede ser ocupado

111
Java Avanzado Programa de Tecnología en Cómputo

por dos servicios a la vez, por lo que siempre se deberán de especificar


puertos disponibles para nuestra aplicaciones.

Entendiendo lo anterior es suficiente para poder empezar nuestra


aplicaciones, por que para poder establecer la comunicación entre dos
máquinas es indispensable indicar a que m áquina conectarse m ediante la
dirección IP, ade más de especificar el puerto al que se enviarán los datos.

¿Qué es u n socket?

Los sockets podemos verlos como un tipo de archivo especial (que de hecho
en sistemas Unix son archivos) por medio del cual podemos establecer la
comunicación. En un socket escribimos y leemos los datos que se envían o
reciben, y están muy relacionados con los puertos TCP, ya que precisamente
se asocia un socket con un puerto TCP para la interacción entre nuestro
programa y el protocolo TCP/IP.

Clase Socket y ServerSocket

La aplicaciones de este tipo suelen llamarse aplicaciones cliente- servidor,


donde se tiene un programa que ofrece algún servicio y atendiendo
peticiones, y uno o varios más que se comunican con el servidor haciendo
peticiones. En java para implementar este tipo de arquitectura tenemos dos
clases que son la clase Socket y la clase ServerSocket.

Comenzemos con la clase Socket. Esta clase implementa un socket del lado
del cliente, por lo que para construir un objeto de esta clase es necesario
especificar la dirección del host remoto, así como al puerto del host remoto
al cual se intentará comunicar. Veamos un constructor de esta clase que
realiza lo previamente escrito.

Al constructor anterior solo bastará especificar un String con la dirección


remota y un entero con en número del puerto para tener nuestro Socket que
se conectará al host remoto por acción del mismo constructor.

Ya teniendo el Socket y la conexión podemos mencionar los métodos


principales para establecer la comunicación.

Regresa un flujo de entrada desde el socket. Este


InputStream getInputStream().
método nos permitirá leer datos en el socket.

112
Java Avanzado Programa de Tecnología en Cómputo

OutputStream getOutputStream() .
Regresa un flujo de hacia desde el socket. Este
método nos permitirá escribir datos en el socket.

void close(). Cierra el socket. Nos permite terminar la comunicación.

InetAddress getInetAddress(). Devuelve la representación de la dirección IP del


host remoto.

int getPort() . Devuelve el puerto del host remoto.

InetAddress getLocalAddress() . Regresa la representación de la dirección IP local.

int getLocalPort() . Regresa el puerto local

Veamos un ejemplo de un cliente web.


package net;

import java.net.*;
import java.io.*;
/**
* Esta clase muestra un uso simple de la
* clase Socket
* @author PROTECO
*
*/
class ClienteWeb {
public static void main(String[] a) {
try {
/*
* Se crea un objeto de clase socket
* que se conectará a la dirección
* especificada desde la línea de comandos
* y al puerto 80 que usualmente es
* el puerto de web
*/
Socket s = new Socket(a[0], 80);
/*
* Se crea un objeto BufferedReader para
* poder trabajar con líneas de texto que se
* leen a partir del InputStream obtenido del
* socket
*/
BufferedReader in = new BufferedReader(
new InputStreamReader(
s.getInputStream()));
/*
* Se crea un objeto PrintWriter para poder
* escribir lineas de caracteres en el flujo
* de salida hacia el socket obtenido. Se
* utiliza el objeto OutputStreamWriter para
* convertir un flujo de caracteres a un flujo

113
Java Avanzado Programa de Tecnología en Cómputo

* de bytes.
*/
PrintWriter out = new PrintWriter(new OutputStreamWriter(
s.getOutputStream()), true);
/*
* Se imprime la siguiente cadena en el OutputStream
* del socket, para un servidor HTTP representa
* una petición GET desde el directorio raiz, es
* decir que el servidor no va a proporcionar
* el HTML, por medio de esa petición
*/
out.println("GET /");
String l = null;
/*
* Se utiliza el ciclo para leer el contenido
* del socket
*/
while ((l = in.readLine()) != null) {
System.out.println(l);
}
s.close();
} catch (ArrayIndexOutOfBoundsException aioobe) {
System.out.println("USO: java net.ClienteWeb URL");
} catch (Exception e) {
}
}
}

Como se puede ver en el código anterior, ya establecida la conexión entre


cliente y servidor, la comunicación se logra por medio de los flujos de
entrada y salida del socket, que se manejan de manera idéntica a como
manejamos la lectura y escritura de archivos, solo que esta vez utilizando los
métodos getInputStream() y getOutputStream de la clase Socket, para
obtener los flujos.

Ahora pasemos a la clase ServerSocket, que representa a un socket de tipo


servidor, encargado de escuchar peticiones que vengan de la red, para
después dar una respuesta a la petición. Dado que un Serve rSocket es un
socket que solo va a escuhar peticiones, solo es necesario especificar que
puerto va a escuchar para que pueda ser construido.

De esta clase es conveniente revisar los siguientes constructores:

Socket accept() .
Escucha por una conexión que va a ser hecha en este socket y
la acepta. Este es quizá el método más importante ya que nos permite
establecer la comunicación una vez aceptada la petición mediante el objeto
de tipo Socket que regresa el constructor.

114
Java Avanzado Programa de Tecnología en Cómputo

InetAddress getInetAddress() . Regresa la dirección local del ServerSocket.

int getLocalPort() . Regresa el puerto que el socket está escuchando.

Veamos un sencillo servidor web para ejemplificar el uso de esta clase.


package net;

import java.net.*;
import java.io.*;
/**
* Este ejemplo es un sencillo servidor web
* que escucha peticiones por el puerto 7777
* y muestra un mensaje en código HTML. Se puede
* ver el resultado por medio de un navegador
* Internet Explorer
* @author PROTECO
*
*/
class ServidorWeb {
public static void main(String[] a) throws Exception {
/*
* Se crea un ServerSocket que escuchará al
* puerto 7777
*/
ServerSocket ss = new ServerSocket(7777);
Socket s;
/*
* Se define un ciclo infinito para que el
* socket escuche siempre peticiones
*/
while (true) {
/*
* Al momento de aceptar una petición se
* crea un socket por medio del cual se
* lleva a cabo la comunicación entre cliente
* y servidor
*/
s = ss.accept();
/*
* Se define un PrintWriter para poder escribir
* lineas de cadenas de caracteres en el socket
*/
PrintWriter out = new PrintWriter(new OutputStreamWriter(
s.getOutputStream()), true);
/*
* Se imprime el mensaje en el flujo de salida del
* socket. Este mensaje es un texto de html que
* el navegador podrá interpretar
*/
out.println(" < h t m l > " +
"<body bgcolor =red > " +
"<h2 > < m a rq uee > " +
"Hola Mundo" +

115
Java Avanzado Programa de Tecnología en Cómputo

"</ marquee > < / h 2 > " +


"</body > " +
"</ht ml > ");
s.close();
}
}
}

De esto concluimos que una vez aceptada la petición, la comunicación se


realiza por medio de un Socket y el manejo de sus flujos de la misma manera
que ya habíamos comentado.

Servir a múltiples clientes: java.net y Threads

Retomando el ejemplo anterior, si se tiene la curiosidad de intentar entrar al


ServidorWeb desde más de un navegador uno podrá comprobar que el
resultado solo se mostrará en el primer navegador que haya hecho la
petición, y que solo hasta que este navegador cambie de página o sea
cerrado, ningún otro usuario podrá ver el contenido del servidor.

Esto se debe a tanto la aceptación de la petición que realiza el ServerSocket,


así como la atención de la misma que realiza el Socket devuelto por el
método accept() se llevan a cabo en un solo hilo que correspondería al que
atiende al primer cliente.

Para resolver esto se propone una implementación de un hilo que atienda a


cada cliente aceptado por el ServerSocket, es decir un Socket por cada hilo,
y un hilo por cada cliente. Veamos una posible solución para lo anterior,
modificando el código del ServidorWeb.

package net;
import java.net.*;
import java.io.*;
/**
* Esta clase implementa un servidor web
* multihilos, capaz de atender a múltiples
* clientes
* @author PROTECO
*
*/
public class ServidorWebThread extends Thread {

Socket s;
/*
* El socket especificado será utiliado
* para la comunicación entre el servidor
* y uno de los clientes
*/
public ServidorWebThread(Socket s) {

116
Java Avanzado Programa de Tecnología en Cómputo

this.s = s;
}
/*
* Al ejecutarse el hilo va a escribir el código
* de HTML en el socket utilizado para construir
* el objeto
*/
public void run() {
try {
PrintWriter out = new PrintWriter(new OutputStreamWriter(s
.getOutputStream()), true);
out.println(" < h t m l > " +
"<body bgcolor =red > " +
"<h2 > < m a rq uee > " +
"Hola Mundo" +
"</ marquee > < / h 2 > " +
"</body > " +
"</ht ml > ");
s.close();
} catch (IOException ioe) {
}
}

public static void main(String[] args) throws Exception {


ServerSocket ss = new ServerSocket(7777);
Socket s;
while (true) {
s = ss.accept();
/*
* Se crea un objeto de tipo ServidorWebThread
* a partir del socket devuelto por el método
* accept del ServerSocket por cada petición
* aceptada
*/
ServidorWebThread swt = new ServidorWebThread(s);
/*
* Se inicia la ejecución del hilo
*/
swt.start();
}
}
}

117
Java Avanzado Programa de Tecnología en Cómputo

Bibliografía
Páginas web

http://www.java.sun.com/

http://www.javahispano.com/

http://www.programacionencastellano.com/

118

Вам также может понравиться