Introducción a la programación en Java (PDF Download Available)

November 1, 2016 | Author: Anonymous | Category: Java
Share Embed


Short Description

Feb 7, 2018 - Full-Text Paper (PDF): Introducción a la programación en Java. ... Hay muchos libros sobre Java, los hay...

Description

Introducción a la Programación en Java

Héctor Antonio Villa Martínez Departamento de Matemáticas Universidad de Sonora

2

Presentación Estos apuntes representan un esfuerzo por reunir en un solo volumen el material de Java conforme se enseña en la materia de Introducción a la Computación I en la Universidad de Sonora. La materia es obligatoria para alumnos de tercer semestre de la Licenciatura en Ciencias de la Computación y es requisito para poderla cursar haber aprobado el curso de Programación de Computadoras. Hay muchos libros sobre Java, los hay para principiantes y para avanzados, la mayoría están en inglés y los pocos en español no son fáciles de encontrar y menos a buen precio. En general estos libros tratan de abarcar mucho más material (y a veces sin la profundidad necesaria) que el requerido para la asignatura. Los objetivos de la materia de Introducción a la Computación I son introducir al estudiante en la resolución de problemas algorítmicos y proporcionarle estrategias de solución, estructuras básicas de datos, así como análisis de complejidad algorítmica. Para lograr estos objetivos se utiliza una metodología conocida como programación orientada a objetos y para aplicar esta metodología se utiliza el lenguaje Java por ser orientado a objetos y porque el ambiente de desarrollo (compilador y manuales) es gratis. Los apuntes están distribuidos de la siguiente forma: En el capítulo 1 se presenta una breve historia y semblanza de Java y la forma de obtener el ambiente de desarrollo. El capítulo 2 es la única parte teórica en esta obra y muestra los principales conceptos de la programación orientada a objetos. En el capítulo 3 se describe la sintaxis de Java. El capítulo 4 muestra la forma de implementar en Java los conceptos de orientación a objetos aprendidos en el capítulo 2. El capítulo 5 se ocupa de los applets que son programas en Java que pueden correr en el Internet. El capítulo 6 presenta la forma de programar interfaces gráficas de usuario usando una librería llamada AWT. Por último, el capítulo 7 muestra el manejo básico de archivos en Java.

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

3

4

Índice 1 Introducción al lenguaje Java 1.1 Historia 1.2 Características 1.3 El ambiente de desarrollo 1.4 Applets y aplicaciones 2 Conceptos básicos de objetos 2.1 Introducción 3 Elementos del lenguaje 3.1 Introducción 3.2 Declaración de variables 3.3 Comentarios 3.4 Literales 3.5 Operadores 3.6 Instrucciones 4 Clases y objetos en Java 4.1 Introducción 4.2 Declaración de clases 4.3 Creación de objetos 4.4 Constructores 4.5 Asignación de objetos 4.6 Comparación de objetos 4.7 Métodos 4.8 Paso de parámetros a los métodos 4.9 Alcance de variables 4.10 Sobrecarga de métodos 4.11 Variables y métodos de clase 4.12 Protección de variables y métodos 4.13 Herencia 4.14 Sobreposición de métodos 5 Applets 5.1 Introducción 5.2 Ciclo de vida 5.3 Argumentos 5.4 Gráficas 5.5 Tipo de letra 5.6 Color 5.7 Imágenes 6 AWT 6.1 Introducción 6.2 Componentes básicos 6.3 Diseños 6.4 Eventos 6.5 Componentes avanzados

7 7 7 8 8 13 13 17 17 17 18 18 19 21 27 27 27 27 29 29 30 31 33 36 37 39 41 41 42 45 45 45 47 49 52 53 55 59 59 60 68 77 86

5

6.6 Aplicaciones con AWT 7 Archivos 7.1 Definición 7.2 Principales clases lectoras 7.3 Principales clases escritoras 7.4 Archivos de datos 7.5 Archivos remotos

105 109 109 109 113 118 121

6

Capítulo 1 Introducción al lenguaje Java 1.1 Historia Java es un lenguaje de propósito general, orientado a objetos y concurrente[1]. Tuvo sus inicios a principios de los 90s cuando Sun Microsystems decidió incursionar en el mercado de la electrónica de consumo y desarrollar programas para controlar dispositivos electrónicos y fundó una compañía subsidiaria llamada FirstPerson[2]. El primer proyecto de FirstPerson fue desarrollar una interface interactiva para controlar los aparatos eléctricos. Como parte de este objetivo un ingeniero de Sun llamado James Gosling definió el lenguaje Oak basándose en la sintaxis de C++ pero eliminando algunas características, como los apuntadores, que a su juicio hacen que la programación en C++ sea propensa a errores y agregando algunas otras, como la recolección automática de basura, para hacer la vida de los programadores más fácil. FirstPerson no tuvo el éxito esperado y cerró en 1994. Parecía que Oak también estaba condenado a desaparecer, sin embargo, Bill Joy cofundador de Sun, se dio cuenta que el Internet podía ser usado para disputar el monopolio de software de Microsoft. Después de un cambio de nombre, Java fue liberado en agosto de 1995.

1.2 Características Java es neutral a la arquitectura. El compilador toma un archivo fuente y genera un archivo objeto con códigos byte para una máquina virtual conocida como JVM (Java Virtual Machine). El intérprete ejecuta los códigos byte del archivo objeto. Por lo tanto es portable al nivel de código objeto. Java es totalmente orientado a objetos. La tecnología de objetos se presenta como el siguiente paso lógico de la programación procedural[3]. La programación procedural (por ejemplo en Fortran o C standard) enfatiza la especificación de tareas (procedimientos) para realizar una tarea. Cuando los sistemas se vuelven grandes y distribuidos, ya no es tan fácil definir estas tareas. Un método más natural es definir objetos y acciones entre objetos. Java soporta los mecanismos standard de la orientación a objetos tales como encapsulación, herencia y polimorfismo. Java es seguro. El chequeo de datos es muy estricto. Antes de que un código byte sea ejecutado es revisado por la JVM para detectar accesos no autorizados a la memoria. El manejo de la memoria es sencillo. Cada objeto se crea con el operador new y el programador no es responsable de liberar la memoria. Cuando un objeto ya no se utiliza, el recolector de basura automáticamente reclama ese espacio. Java es distribuido. Tiene librerías para programación con TCP/IP (el protocolo de comunicaciones standard de Internet). Java es concurrente. Tiene librerías para hacer programas con múltiples hilos de ejecución simultánea. 7

1.3 El ambiente de desarrollo Java se presenta en 3 distribuciones: la edición empresarial (enterprise edition), la edición standard (standard edition) y la edición micro (micro edition). La versión standard es la versión original de Java. La edición empresarial es igual de poderosa que la versión standard pero incluye librerías para programar aplicaciones grandes, distribuidas y con uso intensivo de bases de datos. Por último, la edición micro esta orientada a la programación de dispositivos con poca memoria, por ejemplo, teléfonos celulares o aparatos electrodomésticos inteligentes, un poco siguiendo el objetivo inicial de Oak. Para una introducción al lenguaje es suficiente trabajar con la edición standard. La versión actual de Java es la 1.3, también conocida como Java 2, versión 1.3, edición standard (J2SE 1.3). Sun Microsystems regala el ambiente de desarrollo conocido como Java 2 SDK (System Development Kit, kit de desarrollo de sistemas) o JDK (Java Development Kit, kit de desarrollo de Java) para Windows, Solaris y Linux en la dirección http://java.sun.com/j2se/1.3/ El ambiente de desarrollo consta del compilador, el intérprete de la máquina virtual también conocido como JRE (Java Run-time Environment) que se puede instalar por separado, si se desea, un programa para correr applets, el manual en línea y otras herramientas. Para instalar el ambiente de desarrollo en Windows hay que bajar el archivo auto-extraíble (extensión EXE) correspondiente al J2SE y el archivo comprimido (extensión ZIP) que corresponde a la documentación. El archivo EXE se ejecuta y por default Java se instalará en el directorio C:\jdk1.3. Luego hay que editar el archivo C:\Autoexec.bat y modificar las variables de ambiente PATH y CLASSPATH agregándoles los caminos C:\jdk1.3\bin y C:\jdk1.3\lib\classes.zip;C:\jdk1.3\lib\tools.jar respectivamente, por último el archivo ZIP de la documentación se descomprime en el directorio C:\jdk1.3. El manual se puede leer usando un navegador.

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

8

Una aplicación es un programa que corre localmente en la computadora donde está instalada. No tiene las restricciones de seguridad de los applets y tiene acceso a todos los recursos locales pero para correr necesita que por lo menos el JRE esté instalado. Hay que aclarar que fuera de la restricción que tienen los applets de estar incrustados en una página HTML y de que no pueden accesar recursos en la computadora donde corren, la programación de applets y aplicaciones es parecida y pueden usar cualquier librería del lenguaje por igual. 1.4.2 Mi primera aplicación Parar mostrar las diferencias y coincidencias entre applets y aplicaciones se verá un ejemplo sencillo. En el capítulo 2 comenzaremos a ver con detalle la sintaxis, por ahora basta con copiar el código y seguir los pasos para compilar y correr los programas. Paso 1. Abrir una ventana de MS-DOS. Paso 2. Usando un editor de texto (Edit o Notepad) crear un archivo con el nombre HolaMundo.java, e introducir el código que se muestra en el listado 1-1 teniendo cuidado de respetar las mayúsculas y minúsculas, tanto en el nombre del archivo como en el código. Listado 1-1 public class HolaMundo { public static void main (String args []) { System.out.println ("Hola mundo\n"); } } Paso 3. Compilar el programa con el comando javac HolaMundo.java. El compilador genera el archivo HolaMundo.class. Este es el archivo con los códigos byte que será ejecutado por el intérprete de la máquina virtual. Paso 4. Ejecutar el programa con el comando java HolaMundo. Ver figura 1-1.

9

Figura 1-1. Compilación y ejecución de una aplicación 1.4.3 Mi primer applet Paso 1. Abrir una ventana de MS-DOS. Paso 2. Usando un editor de texto (Edit o Notepad) crear un archivo con el nombre HolaMundoApplet.java, e introducir el código que se muestra en el listado 1-2 teniendo cuidado de respetar las mayúsculas y minúsculas, tanto en el nombre del archivo como en el código. Listado 1-2 import java.awt.Graphics; import java.applet.*; public class HolaMundoApplet extends Applet { public void paint (Graphics g) { g.drawString ("Hola mundo !!!", 5, 25); g.drawString ("Mi tamano es ancho: " + getWidth () + ", alto: " + getHeight (), 5, 35); } } Paso 3. Editar un archivo de texto que se llame HolaMundo.html y copiar el código del listado 1-3.

10

Listado 1-3 Mi primer applet Este es mi primer applet Paso 4. Compilar el archivo Java con el comando javac HolaMundoApplet.java. Paso 5. Accesar la página HolaMundo.html usando un navegador o el visor de applets del JDK (comando appletviewer). El resultado debe verse como la figura 1-2.

Figura 1-2. Ejecutando el applet con Netscape.

11

Referencias [1] The Java Language Specification. Second Edition. James Gosling, Bill Joy, Guy Steele and Gilad Bracha. Sun Microsystems Inc. 2000 [2] Tutorial de Java. Agustín Froufe. Disponible en http://server2.ok.cl/java/ [3] Understanding Object-Oriented: A Unifying Paradigm. Tim Korson and John McGregor. Communications of the ACM. (33), 9, septiembre 2000

12

Capítulo 2. Conceptos básicos de objetos 2.1 Introducción Es un paradigma de programación donde los elementos de los lenguajes son objetos y clases. Otros paradigmas de programación son: 1. Programación estructurada • Los elementos son estructuras o bloques de instrucciones de decisión y repetición, evitando el uso del goto. • Uso de procedimientos y funciones. • Principales exponentes: Pascal y C. 2. Programación funcional. • Los elementos son funciones. • Principales exponentes: LISP y Scheme. 3. Programación lógica. • Los elementos son hechos y reglas para derivar nuevos hechos. • Principal exponente: Prolog. Java no es el único lenguaje orientado a objetos. Otros lenguajes que también manejan este paradigma son C++, Smalltalk, Eiffel y varios más. La ventaja del paradigma orientado a objetos es que, en general, aumenta la productividad de los programadores haciendo que los sistemas sean modulares y promueve la reutilización del software y el ocultamiento de la información. Cada uno de estos conceptos se explica a continuación. Un objeto es una entidad del mundo real. Por ejemplo, mi automóvil es un objeto. La factura de mi computadora también es un objeto. Intuitivamente los objetos del mismo tipo se pueden agrupar en clases. Mi automóvil y el automóvil del vecino son dos instancias de algo que puede llamarse la clase de los automóviles. Se distinguen entre sí por que posiblemente son de distinto modelo, color y fabricante y porque tienen placa, número de serie y dueño diferente. Esas características que distinguen un objeto de otro se llaman atributos. Las clases definen no solo los atributos que los objetos de esa clase pueden tener, sino también su comportamiento esperado. Un automóvil puede estar prendido o apagado, puede estar acelerando o frenando, o puede tener una velocidad x en este momento. Todos los objetos de una misma clase tienen el mismo comportamiento y el mismo número de atributos, sin embargo, cada objeto tendrá valores distintos para esos atributos, lo que se conoce formalmente como estado del objeto y así podrá diferenciarse de otros objetos de la misma clase. En resumen, una clase es un prototipo que define las variables y los métodos comunes para los objetos de un mismo tipo. Se pueden representar objetos del mundo real usando objetos de software. Puede que nos interese representar un automóvil de la vida real como un objeto de software en un programa que juegue a las carreras. Sin embargo, también se pueden usar objetos de 13

software para representar objetos abstractos. Por ejemplo, un evento es un objeto que se utiliza para representar la acción que ocurre cuando un usuario oprime un botón dentro de un programa que tenga interface gráfica. Los lenguajes orientados a objetos implementan el estado de un objeto de software usando variables y el comportamiento por métodos. Un objeto de software podría modelar mi automóvil y decir que su velocidad actual es de 0Km/h y apagado. Estas variables y métodos se conocen como variables y métodos de instancia a diferencia de las variables y métodos de clase (también conocidos como variables y métodos estáticos) y que se explican abajo. Los métodos se pueden sobrecargar, es decir, se pueden definir varios métodos con el mismo nombre pero con distintos tipos de argumentos. La sobrecarga simplifica la programación, no es necesario definir un método para sumar enteros y otro para sumar reales. Al programar en un lenguaje orientado a objetos, lo primero es definir la clase, es decir, la plantilla o prototipo que tendrán los objetos. Para usar un objeto es necesario crearlo, en lenguaje técnico se dice instanciar la clase o crear una instancia de ella. Cuando se crea una instancia de una clase, se crea un objeto de ese tipo y se le asigna memoria para las variables de instancia. Luego se pueden invocar los métodos de instancia del objeto para hacer algo. Las instancias de la misma clase comparten la misma implementación de los métodos. Además de las variables y métodos de instancia se pueden definir variables y métodos de clase. Las variables y métodos de clase se pueden accesar desde una instancia de la clase o desde la misma clase. No es necesario crear una instancia de la clase para poder usar sus variables y métodos de clase. Los métodos de clase solo pueden accesar las variables de clase, no pueden usar las variables de instancia. Todas las instancias de una clase tienen la misma copia de las variables y métodos de clase, si un objeto cambia el valor de una variable de clase, el valor cambia para todo los objetos de ese tipo. Las variables de clase se utilizan en situaciones donde una variable tiene el mismo valor para todos los objetos que pertenecen a esa clase. Por ejemplo, supongamos que todos los automóviles tienen 4 ruedas. En este caso definir una variable de instancia para el número de ruedas sería ineficiente porque cada objeto tendría su copia de la variable y el valor sería el mismo en cada copia. En este caso es mejor declarar una variable de clase para el número de ruedas. Las clases promueven la reutilización del software. Los programadores pueden usar clases creadas por otros programadores para crear objetos. Otro concepto importante es la encapsulación. Este mecanismo permite esconder las variables de un objeto del mundo externo. Cuando usamos la palanca de cambios para cambiar de velocidad, no nos interesa el mecanismo interior, ni saber como funciona, lo único que tenemos que saber es a que velocidad vamos a pasar. De la misma manera, si usamos un objeto de software no debe interesarnos la forma en que está implementado sino solo conocer que método vamos a invocar. La interface de un objeto es la lista de métodos públicos que se pueden invocar. Así la implementación de un objeto puede cambiar sin afectar otras partes del programa, a esto se 14

le conoce como ocultamiento de información. La encapsulación también fomenta la modularidad, el código fuente de un objeto se puede desarrollar y mantener independientemente del código fuente de otros objetos. El último concepto del paradigma orientado a objetos que se revisará en este capítulo es la herencia. La herencia es un mecanismo que permite definir clases en función de otras clases. Por ejemplo, los pickups y los sedanes son tipos distintos de automóviles. En lenguaje técnico se dice que los pickups y los sedanes son subclases de la clase automóvil. De igual manera, se dice que la clase automóvil es la superclase de los pickups y sedanes. Cada subclase hereda el estado de la superclase en forma de variables. Los pickups y los sedanes comparten los estados, tienen número de serie, número de placa, color, etc. También, cada subclase hereda los métodos de la superclase. Los pickups y los sedanes comparten el comportamiento, pueden estar prendidos o apagados, tener una velocidad, etc. El estado y el comportamiento de las subclases no están limitados por el estado y el comportamiento de la superclase. Las subclases pueden agregar variables y métodos a los heredados de la superclase. Un sedan tiene 2 filas de asientos y una camioneta puede tener hasta 3. Las subclases pueden sobreponer métodos heredados y tener implementaciones especializadas para esos métodos. Si un pickup tiene sobremarcha (overdrive) la clase pickup puede sobreponer el método para cambiar velocidades y poder usarla. La herencia también ayuda a la reutilización del software. Un programador puede aprovechar clases programadas por otra persona para definir las suyas.

15

16

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

3.2 Declaración de variables Las variables se declaran igual que en C++, es decir, primero se pone el tipo y luego la variable o lista de variables. Cada variable deberá tener un tipo. Los tipos pueden ser primitivos, de clase o de arreglo. 3.2.1 Tipos primitivos Los tipos primitivos son los números enteros, números reales, booleanos y caracteres. Los enteros siempre tienen signo. Hay cuatro tipos de enteros. La diferencia está en el tamaño que ocupan y el rango que pueden guardar. Tipo byte short int long

Tamaño (bytes) 1 2 4 8

Rango -128 a 127 -32,768 a 32,767 -231 a 231 – 1 -263 a 263 – 1

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

Tamaño (bytes) Precisión 4 sencilla 8 doble

El tipo boolean representa variables que pueden tener uno de dos valores: true o false. El tipo char ocupa 2 bytes y puede guardar un carácter Unicode. 3.2.2 Tipos de clase Cada vez que se define una clase se está creando un nuevo tipo y se pueden declarar variables de ese tipo. Por ejemplo, es posible declarar que una variable es de tipo String porque en la librería standard ya está definida una clase llamada String.

17

3.2.3 Arreglos En Java los arreglos se declaran sin tamaño. Para poder usarlos es necesario asignarles un tamaño. Se puede lograr esto dándoles valores iniciales: int vector[] = {4, 2, 3, 10, 2}; o usando el operado new: int vector[]; vector = new int[10]; o en un solo paso: int vector[] = new int[10]; Los elementos de un arreglo se accesan mediante un índice que puede valer entre 0 y s – 1, donde s es el tamaño del arreglo. Usando cualquiera de las declaraciones anteriores, las siguientes instrucciones son válidas. vector[1] = 20; vector[2] = vector[0] + 10; vector[0]++; Los arreglos tienen asociada la variable length que indica el tamaño del arreglo. La forma de accesarla es nombre-del-arreglo.length. En el caso del arreglo vector se puede usar así: t = vector.length;

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

3.4 Literales El manejo de literales es igual que en C++. Las literales de string se delimitan entre comillas dobles: "hola mundo" Si se desea que el string tenga comillas dobles es necesario poner el carácter de escape '\'. "Juan dijo: \"hola mundo\""

18

Las literales de carácter se delimitan entre comillas sencillas: 'h', '$', '7'. Java maneja las mismas literales para caracteres no imprimibles que C++, por ejemplo '\n' para el retorno de carro y '\t' para el tabulador. Las literales enteras se pueden especificar en base 10, por ejemplo 259, pero también se pueden especificar en base 8 colocando un cero antes, por ejemplo 064 o en base 16 poniendo 0x, por ejemplo 0xA9. Las literales reales se pueden escribir en formato normal, por ejemplo 2.943 o en formato científico, por ejemplo 756.51E10 o 4.938E-2. Las literales booleanas son las palabras reservadas true y false.

3.5 Operadores Un operador es un símbolo especial que representa una operación y que se usa en una expresión. Una expresión es una instrucción que devuelve un valor. 3.5.1 Operadores aritméticos Java tiene los mismos operadores aritméticos que C++. El + para la suma, el – para la resta, el * para la multiplicación, el '/' para la división y el '%' para el módulo. Si todos los operandos en una expresión son enteros, la expresión regresa un entero. Basta que uno de los operandos sea real, para que el resultado sea real. Así la división 2/3 regresa 0 y 2/3.0 regresa 0.6666. 3.5.2 Operadores de asignación Java tiene los mismos operadores de asignación que C++. El símbolo = representa la asignación sencilla: a = 2; Los símbolos ++ y -- incrementan y decrementan en 1 el valor de la variable: a = 2; a++; // a vale ahora 3 También existen los operadores aritméticos con asignación como muestra la siguiente tabla:

19

Expresión x += y x -= y x *= y x /= y

Significado x=x+y x=x–y x=x*y x=x/y

3.5.3 Operadores de comparación Java maneja los mismos operadores de comparación que C++: menor que (=), igual que (==) y distinto que (!=). 3.5.4 Operadores lógicos Java utiliza 2 símbolos para la operación AND (Y), el & y el &&. La diferencia es que el operador & evalúa ambos lados de la expresión y el && puede que no evalúe la parte derecha si la parte izquierda es falsa (y por lo tanto ya sabe que toda la expresión será falsa). A este comportamiento se le conoce como evaluación en cortocircuito. El uso de uno u otro operador solo es relevante si la parte derecha de la expresión tiene efectos laterales como una asignación. Por ejemplo, si tenemos este segmento de código: int x = 2, a = 10; if ((a < 0) & (x++ > 0)) instrucciones El operador & evalúa la expresión de la izquierda (a < 0) y aunque es falsa evalúa la parte derecha y por lo tanto incremente el valor de x a 3. En cambio si usamos el operador &&: int x = 2, a = 10; if ((a < 0) && (x++ > 0)) instrucciones Al notar que la expresión izquierda es falsa, la evaluación hacer cortocircuito y no se evalúa la parte derecha. Por lo tanto el valor de x se mantiene en 2. La mayor parte de los autores recomienda usar el operador && y no poner asignaciones en las condiciones. Para la operación OR (O) también hay 2 operadores, el | y el ||. Al igual que en el AND, el | evalúa las dos partes de la expresión y el || no evalúa la parte derecha si la parte izquierda es verdadera y por lo tanto sabe que toda la expresión es verdadera. Para la operación XOR (OR exclusivo) el operador es el ^.

20

Para la operación NOT (no) el operador es el !. 3.5.5 Concatenación de strings En Java el operador + está sobrecargado, es decir, tiene más de un significado. Aplicado a números representa la suma, aplicado a strings representa la concatenación. 4 + 3 // es una suma "hola " + "mundo" // concatena los strings Si se mezcla un string y un número, el número se convierte a string. Un truco para convertir números a strings es concatenar el número a un string vacío. String s = "" + 3.14 // s tiene ahora el string "3.14" 3.5.6 Operador ? : El operador ? : funciona igual que en C++. La expresión: (z > 0 ? –1 : 1) Toma el valor –1 si z es mayor que 0 o 1 si z es menor o igual que 0. 3.5.7 Operador instanceof El operador instanceof sirve para preguntar si una variable es o no de un tipo en particular. El operador regresa verdadero o falso según el caso. int x; x instanceof int // regresa verdadero x instanceof double // regresa falso 3.5.8 Operador new El operador new crea nuevas instancias de clases. Esto se verá con más detalle en el capítulo siguiente.

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

21

3.6.1 Instrucción if Sintaxis: if (condición) instrucción; La condición se evalúa, si es verdadera se ejecuta la instrucción, si es falsa continúa con la siguiente instrucción. La palabra reservada else sirve para ejecutar una instrucción alterna si la condición es falsa. Sintaxis: if (condición) instrucción 1; else instrucción 2; La condición se evalúa, si es verdadera se ejecuta la instrucción 1, si es falsa se ejecuta la instrucción 2. El segmento de código siguiente muestra el uso de un if para decidir si un número entero es par o impar. if ((n % 2) == 0) System.out.println (n + " es par"); else System.out.println (n + " es impar"); 3.6.2 Instrucción for Sintaxis: for (inicio; condición; postcondición) instrucción; El flujo de control es el siguiente: 1. Se evalúa la expresión de inicio 2. Se evalúa la condición 3. Si la condición es falsa el ciclo termina. Si es verdadera ejecuta la instrucción (que puede ser un bloque de instrucciones encerrado entre corchetes), evalúa la postcondición y regresa al paso 2. El segmento de código imprime 10 veces el letrero en la pantalla: for (i = 0; i < 10; i++) System.out.println ("Hola mundo");

22

3.6.3 Instrucción while Sintaxis: while (condición) instrucción; La instrucción se ejecuta mientras la condición sea verdadera. Este segmento de código muestra el uso de un while para escribir el índice del primer elemento negativo de un arreglo o un mensaje de error si no existe ninguno. int x[] = {...}; int i = 0; boolean exito = false; while (!exito && (i < x.length)) if (x[i] < 0) exito = true; else i++; if (exito) System.out.println ("El primer numero negativo esta en" + i); else System.out.println ("No existe ningún numero negativo"); 3.6.4 Instrucción do while Sintaxis: do { instrucción; } while (condición); La instrucción se ejecuta mientras la condición sea verdadera. La diferencia con el ciclo while es que el do while primero ejecuta la instrucción y luego revisa la condición. Por lo tanto el do while siempre se hace por lo menos una vez. Este segmento de código también imprime 10 veces el letrero en la pantalla: i = 0; do { System.out.println ("Hola mundo"); i++; } while (i < 10);

23

3.6.5 Instrucción switch Sintaxis switch (expresión) { case valor 1 : instrucción 1; case valor 2 : instrucción 2; ... [default : instrucción n;] // el corchete cuadrado indica que es opcional } La expresión se evalúa y se compara con cada uno de los valores de la parte case. Si encuentra una coincidencia ejecuta las instrucciones a partir de ese caso. Si no encuentra ninguna coincidencia ejecuta la instrucción correspondiente a la parte default si es que está presente o continua con la siguiente instrucción al switch si no hay default. Se puede indicar que solo ejecute un caso usando la instrucción break (ver sección 3.6.7) como en el siguiente segmento de código, donde dado un número entero que representa el mes (1 es enero, 2 es febrero, ..., 12 es diciembre) asigna en la variable día el número de días del mes o –1 si el número de mes está fuera de rango. switch (mes) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: dia = 31; break; case 4: case 6: case 9: case 11: dia = 30; break; case 2: dia = 28; break; default: dia = -1; } 3.6.6 Instrucción return La instrucción return sirve para salir de un método. Si el método regresa un valor el return va seguido de una expresión que se convertirá en el valor del método. En el capítulo siguiente se explicarán con más detalle los métodos que regresan valores y se verán ejemplos del uso del return.

24

3.6.7 Instrucción break La instrucción break sirve para salir de un switch. El break también se puede utilizar para terminar un ciclo for, while o do while como en el ejemplo siguiente que escribe 10 veces el letrero "Hola mundo" en la pantalla: int i = 0; while (true) { System.out.println ("Hola mundo"); i++; if (i == 10) break; } Si el ciclo está anidado dentro de otro ciclo, la ejecución continúa con el ciclo externo. 3.6.8 Instrucción continue La instrucción continue suspende la ejecución de un ciclo y continúa con la siguiente iteración. Este segmento de código muestra el uso del continue dentro de un ciclo para contar los elementos positivos de un arreglo. int cuenta = 0; for (int i = 0; i < vector.length; i++) { if (vector[i] < 0) continue; cuenta++; } System.out.println ("El arreglo tiene " + cuenta + "elementos positivos");

25

26

Capítulo 4 Clases y objetos en Java. 4.1 Introducción Al programar en Java siempre se tienen que definir por lo menos una clase. El archivo que contiene esa clase debe llamarse exactamente igual que la clase (con todo y mayúsculas) y debe tener la extensión .java. Es posible programar varias clases en un archivo, pero solo una de ellas podrá ser pública (eso por ahora no tiene mayor inconveniente) y el archivo deberá llamarse igual que la clase pública. Cada clase tiene variables y métodos definidos. Los métodos se programan de una manera muy semejante a los procedimientos y funciones de C++. Las aplicaciones deben tener un método llamado main (excepto en los applets) de tipo estático (static), público (public) y void y con un argumento de tipo arreglo de strings (String[]). En el caso de los applets, como se verá en el capítulo correspondiente, deben definir, ya sea un método llamado init o bien uno llamado paint.

4.2 Declaración de clases El listado 4-1 muestra el código para una clase llamada Punto que representa un punto en un plano. Listado 4-1 public class Punto { public int x = 0; public int y = 0; } Este código está creando una clase, y a la vez un nuevo tipo, llamada Punto. La clase Punto tiene dos variables enteras de instancia x y y. La palabra public significa que cualquier otra clase puede accesar libremente esas variables.

4.3 Creación de objetos Se puede crear un objeto de la clase Punto instanciando la clase con el operador new. Cuando se crea un objeto de tipo Punto se asigna memoria para el objeto y para sus variables x y y. Además, dentro del objeto x y y se inicializan a 0. El listado 4-2 muestra como otra clase, llamada pruebaPunto utiliza la clase Punto que se acaba de declarar para dos objetos de tipo Punto que representan a los puntos en el plano (20, 10) y (5, 7).

27

Listado 4-2 public class pruebaPunto { public static void main (String args[]) { Punto p1 = new Punto (); p1.x = 20; p1.y = 10; Punto p2 = new Punto (); p2.x = 5; p2.y = 7; System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y); System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y); } } La clase Punto no tiene ningún método main por lo tanto no puede correr por sí sola. La clase pruebaPunto si lo tiene y por lo tanto es la que puede correr. Para poder correr este ejemplo hay que copiar el código en 2 archivos llamado Punto.java y pruebaPunto.java respetando las mayúsculas y minúsculas. Por ahora los 2 archivos deben estar en el mismo directorio, se compilan por separado, usando el comando javac y se corre la clase pruebaPunto (es la que tiene el main) invocando al intérprete con el comando java como se muestra en la figura 4-1.

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

28

4.4 Constructores. Los constructores son métodos que se utilizan para inicializar objetos cuando son creados. Los constructores siempre tienen el mismo nombre que la clase y se invocan de manera automática al usar el operador new. Puede haber varios constructores con el mismo nombre pero con distintos argumentos (sobrecarga del constructor). Al momento de crear un objeto nuevo Java sabe que constructor invocar, si hay más de uno, por el número y el tipo de argumentos que se pasen en el new. El listado 4-3 muestra la clase Punto con un constructor. El listado 4-4 muestra como la clase pruebaPunto invoca al constructor de Punto cuando crea los objetos. Listado 4-3 public class Punto { public int x; public int y; Punto (int x, int y) { this.x = y; this.y = y; } } Listado 4-4 public class pruebaPunto { public static void main (String args[]) { Punto p1 = new Punto (20, 10); Punto p2 = new Punto (5, 7); System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y); System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y); } }

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

Listado 4-5 public class asigna {

29

public static void main (String args[]) { Punto p1 = new Punto (20, 10); Punto p2 = p1; p1.x = 2; p1.y = 8; System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y); System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y); } } La salida del programa es: El punto 1 es x = 2 y = 8 El punto 2 es x = 2 y = 8 Como vemos el valor de las variables de instancia de p2 cambiaron sin haberlo indicado en forma explícita. Al momento de asignar p1 a p2 las dos variables se convirtieron en el mismo objeto. La solución a este problema es crear un nuevo objeto para p2 (hay que recordar que al crear un nuevo objeto se le asigna memoria) y luego copiar las variables de instancia de p1. El listado 4-6 muestra una forma de hacerlo. Listado 4-6 public class asigna { public static void main (String args[]) { Punto p1 = new Punto (20, 10); Punto p2 = new Punto (p1.x, p1.y); p1.x = 2; p1.y = 8; System.out.println ("El punto 1 es x = " + p1.x + " y = " + p1.y); System.out.println ("El punto 2 es x = " + p2.x + " y = " + p2.y); } } La salida de este programa es: El punto 1 es x = 2 y = 8 El punto 2 es x = 20 y = 10

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

public static void main (String args[]) { Punto p1 = new Punto (20, 10); Punto p2 = new Punto (20, 10); if (p1 == p2) System.out.println ("Son iguales"); else System.out.println ("Son diferentes"); } } La salida de este programa siempre es: Son diferentes La razón es que el operador == checa si p1 y p2 ocupan la misma área de memoria no si tienen los mismos valores para los atributos. La solución para comparar dos objetos es comparar atributo por atributo y se muestra en el listado 4-8. Después, cuando se vea la forma de agregarle métodos a las clases, veremos que una solución más sencilla y elegante sería agregarle un método que compare a la clase Punto. Listado 4-8 public class compara { public static void main (String args[]) { Punto p1 = new Punto (20, 10); Punto p2 = new Punto (20, 10); if ((p1.x == p2.x) && (p1.y == p2.y)) System.out.println ("Son iguales"); else System.out.println ("Son diferentes"); } } La salida de este programa es: Son iguales

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

31

Veamos ahora la declaración de una clase llamada Rectángulo que representa un rectángulo en el plano. La clase tiene el constructor sobrecargado y se ha programado un método que calcula el área del rectángulo. Listado 4-9 public class Rectangulo { public int ancho; public int alto; public Punto origen; Rectangulo (int w, int h, Punto x) { ancho = w; alto = h; origen = x; } Rectangulo (int w, int h) { this (w, h, new Punto (0, 0)); } public int area () { return alto * ancho; } } Este código declara otra clase, y por lo tanto otro tipo, llamada Rectángulo que contiene dos variables de instancia de tipo entero ancho y alto y una tercera variable origen de tipo Punto. Hay que notar como el nombre de la clase Punto se utiliza en la declaración de una variable como el tipo de la variable. Se puede usar el nombre de una clase donde sea que se pueda usar un tipo primitivo. Así como ancho es un entero y alto también es un entero, origen es un Punto. Por otro lado, Rectángulo tiene un Punto. Esta distinción entre es un y tiene un es importante porque solo un objeto que es un punto puede ser usado cuando se necesita un Punto. La clase Rectángulo tiene dos constructores. Tienen el mismo nombre pero distinta firma. La firma de un método es el nombre del método y la lista de los tipos de los argumentos que recibe. La clase define también un método llamado área. Este método tiene un modificador public para indicar que puede ser llamado desde otra clase, tiene un tipo de regreso int para indicar que regresa un valor entero, luego viene el nombre y entre paréntesis la lista de argumentos que en este caso está vacía. El cuerpo del método se encierra entre corchetes { y }. Como el

32

método regresa un valor entero debe tener una instrucción return seguida de una expresión entera. El listado 4-10 muestra la forma de utilizar la clase Rectángulo. El programa crea dos instancias de la clase Rectángulo y cada objeto tiene su propia copia de las variables de instancia y acceso al método área. Hay que notar como el método área se invoca usando la notación punto y que es necesario crear un objeto para poder llamar a un método de instancia. Listado 4-10 public class pruebaRectangulo { public static void main (String args[]) { Rectangulo r1 = new Rectangulo (10, 5); Rectangulo r2 = new Rectangulo (7, 9); System.out.println ("El área de r1 es: " + r1.area ()); System.out.println ("El área de r2 es: " + r2.area ()); } } La salida del programa es: El área de r1 es: 50 El área de r2 es: 63

4.8 Paso de parámetros a los métodos Los parámetros de tipos primitivos (int, float, char, etc), se pasan por valor. El método hace una copia del parámetro y cualquier cambio de valor se hace sobre la copia y no sobre el original. Analicemos el programa del listado 4-11. Listado 4-11 public class Foo { public void cambia (int x) { x = 4; System.out.println ("En cambia x es: " + x); } public static void main (String args[]) { int x = 2; Foo f = new Foo (); System.out.println ("Antes x es: " + x); f.cambia (x); System.out.println ("Despues x es: " + x); } } 33

La salida del programa es: Antes x es: 2 En cambia x es: 4 Despues x es: 2 El método cambia hizo una copia de x. Cuando le asignó el valor 4 lo hizo a la copia y no al original, al momento de terminar el método la copia de x desaparece y solo queda el original. Esto quedará más claro en la sección denominada alcance de variables. Los parámetros de tipo no primitivos (arreglos y clases), se pasan por dirección. El método tiene la dirección del parámetro y cualquier cambio de valor se hace sobre el original. En el ejemplo del listado 4-12 se muestra como el método cambia tiene acceso al argumento original sin importar si tiene o no el mismo nombre que dentro del método. Listado 4-12 public class Foo2 { public void cambia (int x[]) { x[0] = 10; x[1] = 5; } public static void main (String args[]) { int z[] = {1, 2, 3, 4, 5}; System.out.print ("Antes z es: "); for (int i = 0; i < z.length; i++) System.out.print (z[i] + " "); System.out.println (); cambia (z); System.out.print ("Despues z es: "); for (int i = 0; i < z.length; i++) System.out.print (z[i] + " "); } } La salida del programa es: Antes z es: 1 2 3 4 5 Despues z es: 10 5 3 4 5 Si se necesita cambiar un parámetro se puede utilizar un método que regrese un valor y asignarlo a la variable original como se muestra en el listado 4-13.

34

Listado 4-13 public class Foo3 { public int cambia (int x) { x = 4; return x; } public static void main (String args[]) { int x = 2; Foo3 f = new Foo3 (); System.out.println ("Antes x es: " + x); x = f.cambia (x); System.out.println ("Despues x es: " + x); } } La salida del programa es: Antes x es: 2 Despues x es: 4 El ejemplo anterior funciona si solamente se necesita cambiar un parámetro, si se necesita que cambien 2 o más argumentos entonces se puede usar una clase auxiliar que envuelva a esas variables y pasar como argumento una instancia de esa clase. En el listado 4-14 se muestra una forma de hacerlo. El listado 4-15 muestra la clase auxiliar. Listado 4-14 public class Foo4 { public void cambia2 (FooAux x) { x.a = 4; x.b = 10; } public static void main (String args[]) { FooAux z = new FooAux (100, 200); Foo4 f = new Foo4 (); System.out.println ("Antes a es: " + z.a + " b es: " + z.b); f.cambia2 (z); System.out.println ("Despues a es: " + z.a + " b es: " + z.b); } } 35

Listado 4-15 public class FooAux { int a; int b; FooAux (int a, int b) { this.a = a; this.b = b; } } La salida del programa es: Antes a es: 100 b es: 200 Despues a es: 4 b es: 10

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

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

Puede haber variables con el mismo nombre, siempre y cuando tengan distintos alcances. Las variables con alcances más locales inhiben el acceso a las variables más globales. Cuando se hace referencia a una variable, se busca primero entre las locales al bloque, luego entre las locales al método y al final entre las globales a la clase. El listado 4-16 muestra un ejemplo de un programa con variables de los 3 tipos. La salida del programa es: antes: a es 1, b es 2, c es 3 en el for, b es 0 en el for, b es 1 en el for, b es 2 despues: a es 1, b es 2, c es 10 El objetivo del ejercicio es mostrar como la variable global b no cambió su valor a pesar de haber sido usada en el ciclo for y como el valor de c si se alteró.

36

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

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

37

Listado 4-17 public class Fecha { int dia; int mes; int year; // sobrecarga del constructor Fecha (int d, int m, int y) { dia = d; mes = m; year = y; } Fecha (int d, int m) { this (d, m, 2002); } Fecha (int d) { this (d, 10, 2002); } void cambiaDia (int d) { dia = d; } void cambiaMes (int m) { mes = m; } // sobrecarga del metodo void cambiaYear (int y) { year = y; } void cambiaYear () { year = 2002; }

38

Listado 4-17 Continuación void pintaCorto () { System.out.println (dia + "/" + mes + "/" + year); } public static void main (String args[]) { Fecha f1 = new Fecha (20, 2, 2005); System.out.print ("Fecha:"); f1.pintaCorto (); f1.cambiaYear (2100); System.out.print ("Fecha:"); f1.pintaCorto (); f1.cambiaYear (); System.out.print ("Fecha:"); f1.pintaCorto (); } } La salida del programa es: Fecha: 20/2/2005 Fecha: 20/2/2100 Fecha: 20/2/2002

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

39

Listado 4-18 public class FamPerez { String nombre; static String apellido = "Perez"; int edad; FamPerez (String n, int e) { nombre = n; edad = e; } public static void main (String args[]) { FamPerez papa, mama, hijo1, hijo2; papa = new FamPerez ("Ramon", 40); mama = new FamPerez ("Maria", 35); hijo1 = new FamPerez ("Monchito", 10); hijo2 = new FamPerez ("Lupita", 7); System.out.println ("antes: nombre: " + papa.nombre + ", apellido: " + papa.apellido + ", edad: " + papa.edad); hijo1.apellido = "Lopez"; System.out.println ("despues: nombre: " + papa.nombre + ", apellido: " + papa.apellido + ", edad: " + papa.edad); } } La salida del programa es: antes: nombre: Ramon, apellido: Perez, edad: 40 despues: nombre: Ramon, apellido: Lopez, edad: 40 Los métodos de clase están disponibles sin necesidad de crear una instancia de la clase. Para invocarlos se usa la notación punto, pero en lugar del objeto se pone la clase. Por ejemplo, la librería de Java tiene una clase Math que define un conjunto de operaciones matemáticas que se pueden utilizar desde cualquier programa. Los métodos de la clase Math son métodos de clase, están declarados con la palabra reservada static. Por esa razón, si se desea calcular el coseno de un número basta con invocar así el método. double y = Math.cos (76); Los métodos de clase no pueden accesar variables de instancia, por lo tanto, una regla para decidir si un método debe ser de instancia o de clase es analizar si queremos que el método tenga acceso o no a las variables de instancia. Por lo general los métodos de utilería (como el que se presenta en el listado 4-19) que no modifican el estado del objeto son los que se programan como métodos de clase.

40

Listado 4-19 public class Conversion { static double gra2rad (double grados) { return (Math.PI * grados) / 180; } public static void main (String args[]) { System.out.println ("120 grados son " + Conversion.gra2rad (120) + " radianes"); } }

4.12 Protección de variables y métodos En el capítulo 2 aprendimos que una de las tareas fundamentales de un objeto es encapsular sus datos para ocultarlos de la vista de los demás y limitar su manipulación. El encapsulamiento separa el diseño de la implementación y minimiza la cantidad de información que una clase necesita conocer de otra para poder hacer su trabajo. Además reduce la cantidad de cambios que se necesitarán de hacer si la implementación interna cambia. Como regla general, es conveniente ocultar las variables de instancia crear métodos especiales para accesar o modificar esas variables. Los métodos que dependan de la forma en que está implementada una clase también deberán estar ocultos. Es cuestión de diseño el decidir que métodos son parte de la interface que la clase presenta al mundo externo y cuales no. En Java es muy sencillo proteger variables y métodos. Lo único que hay que hacer es poner en la declaración la palabra reservada private. Por ejemplo: public class Foo { private int foo; .... private float calculaFoo () ...

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

41

Por ejemplo, un applet es una subclase de la clase java.applet.Applet. Por eso, al programar un applet ponemos la línea: public class fooApplet extends java.applet.Applet { en la definición de la clase. Es recomendable que en el constructor de la subclase se invoque al constructor de la superclase. Esto se logra llamando al método super. El listado 4.20 muestra la implementación de la clase Cuadrado que extiende a la clase Rectángulo vista en el listado 4.9. La clase Cuadrado hereda el método área de Rectángulo y por lo tanto no necesita programarlo. Además agrega un nuevo método. Listado 4-20 public class Cuadrado extends Rectangulo { private int lado; Cuadrado (int lado) { super (lado, lado); this.lado = lado; } public double diagonal () { return Math.sqrt (lado * lado + lado * lado); } public static void main (String args[]) { Cuadrado c = new Cuadrado (2); System.out.println ("Area: " + c.area ()); System.out.println ("Diagonal: " + c.diagonal ()); } } La salida del programa es: Area: 4 Diagonal: 2.828427

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

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

43

Listado 4-21 Continuación public static void main (String args[]) { Empleado juan = new Empleado ("Juan", 5000); Jefe luis = new Jefe ("Luis", 5500, 1500); System.out.println ("Nombre: " + juan.getNombre () + " Sueldo: " + juan.getSueldo ()); System.out.println ("Nombre: " + luis.getNombre () + " Sueldo: " + luis.getSueldo ()); } } La salida del programa es: Nombre: Juan Sueldo: 5000 Nombre: Luis Sueldo: 7000

44

Capítulo 5 Applets 5.1 Introducción Como vimos en la sección 1.4.1 un applet es un programa en Java que se incrusta en una página HTML para agregarle funcionalidad. Como por seguridad los applets están limitados a no poder accesar el medio ambiente de la computadora donde corren su principal utilidad es para hacer interfaces gráficas de aplicaciones de bases de datos. Por supuesto la base de datos debe residir en la computadora de donde vino el applet. También se pueden programar juegos, programas interactivos que complementen un texto en línea o que ilustren un concepto en particular, simulaciones, etc.

5.2 Ciclo de vida Los applets tienen un ciclo de vida durante el cual realizan algunas actividades. Cada actividad tiene asignado un método que el navegador llama en el momento adecuado. Las principales actividades son: • • • • •

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

Es importante recalcar que ninguno de estos métodos es invocado en forma explícita. El navegador los invoca en forma automática dependiendo de si el applet necesita inicializarse o si está comenzando o si necesita volver a pintar lo que el applet haya dibujado. Un applet debe extender a la clase Applet. Esta clase proporciona implementaciones vacías de los 5 métodos. Es responsabilidad del programador de applets sobreponer los métodos que vaya a utilizar. No es necesario sobreponer todos, en la práctica casi siempre solo se sobreponen o el método init() o el paint(), rara vez los dos a la vez.

45

El listado 5-1 muestra el código de un applet sencillo que solo sobrepone el método paint(). El listado 5-2 tiene el código de la página HTML que invoca al applet y la figura 5-1 muestra la salida que se genera al cargar la página HTML en un navegador. Listado 5-1 import java.awt.Graphics; import java.awt.Font; import java.awt.Color; import java.applet.*; public class HolaAgain extends Applet { Font f = new Font ("TimesRoman", Font.BOLD, 36); public void paint (Graphics g) { g.setFont (f); g.setColor (Color.red); g.drawString ("Hola mundo !!!", 5, 40); } } Listado 5-2 Hola Mundo !!! Saludos desde Sonora: Como se puede ver en el listado 5-2 la etiqueta es la que incrusta el applet en la página HTML. Esta etiqueta puede tener varios argumentos, los principales son: • • • •

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

46

Figura 5-1

5.3 Argumentos Los applets pueden recibir argumentos desde el archivo HTML con la etiqueta , la cual tiene un atributo para el nombre del argumento y otro para el valor. La etiqueta va dentro de la etiqueta . Los argumentos se pasan cuando el applet se inicializa. Dentro del método init() se puede recuperar los valores de los argumentos usando el método getParameter(). Este método recibe como argumento un string con el nombre del argumento y devuelve un string con el valor correspondiente a ese argumento o el valor null si el argumento no existe. El listado 5-3 muestra el código de un applet que lee tres argumentos y escribe un mensaje en la pantalla. El listado 5-4 presenta la página HTML que carga al applet y le pasa los valores de los tres argumentos y la figura 5-2 la salida cuando se carga la página HTML. Listado 5-3 import java.awt.Graphics; import java.awt.Font; import java.awt.Color; import java.applet.*; public class HolaAgainP extends Applet { Font f = new Font ("TimesRoman", Font.BOLD, 36); String desde, para, mssg;

47

Listado 5-3 Continuación public void init () { desde = getParameter ("desde"); if (desde == null) desde = "aqui"; para = getParameter ("para"); if (para == null) para = "alla"; mssg = getParameter ("mensaje"); if (mssg == null) mssg = "no hay mensaje"; } public void paint (Graphics g) { g.setFont (f); g.setColor (Color.red); g.drawString ("Desde " + desde + " para " + para, 5, 40); g.setColor (Color.green); g.drawString ("El mensaje es: " + mssg, 5, 80); } } Listado 5-4 Hola Mundo !!! Saludos desde Sonora:

48

Figura 5-2

5.4 Gráficas La librería de Java incluye la clase Graphics para hacer dibujos sencillos. Esta clase tiene métodos para dibujar líneas, rectángulos, círculos, polígonos y arcos. En los applets el método paint recibe un objeto de tipo Graphics. Al dibujar sobre este objeto se dibuja dentro del applet y los resultados se desplegarán en la pantalla. El sistema de coordenadas está en pixeles. El origen, es decir, el punto (0,0) está en la esquina superior izquierda del área reservada para el applet en la página HTML. La X crece hacia la derecha en forma horizontal y la Y crece hacia abajo en forma vertical. La clase Graphics tiene los siguientes métodos. Hay que aclarar que todos los argumentos son de tipo int. • • • •

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

49



• •

• • •

• •



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

El listado 5-5 muestra un applet que realiza un dibujo utilizando los métodos de la clase Graphics. La figura 5-3 presenta la salida del applet. Listado 5-5 import java.awt.Graphics; import java.applet.*; public class Lampara extends Applet { public void paint (Graphics g) {

50

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

Figura 5-3

51

5.5 Tipo de letra Para cambiar el tipo de letra hay que crear una instancia de la clase Font. Font f = new Font ("TimesRoman", Font.BOLD, 24); El primer argumento es el nombre del tipo de letra. Los tipos de letra disponibles se pueden saber usando esta instrucción: String fontlist[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames (); o esta para versiones atrasadas de Netscape y Explorer que usan el JDK 1.1 String fontlist[] = this.getToolkit ().getFontList (); El segundo argumento es el estilo, puede valer Font.PLAIN (estilo normal), Font.BOLD (estilo en negritas) o Font.ITALIC (estilo en cursiva o itálico). Se pueden mezclar dos o más estilos con un el signo +, por ejemplo se puede indicar Font.BOLD + Font.ITALIC. El tercer argumento es el tamaño en puntos del tipo de letra y depende de cada tipo de letra. Para cambiar el tipo de letra se invoca al método: setFont (f) donde f es un objeto de tipo Font. El listado 5-6 muestra un ejemplo del uso de los tipos de letras y la figura 5-4 la salida del applet. Listado 5-6 import java.awt.Font; import java.awt.Graphics; import java.applet.*; public class ManyFonts extends Applet { public void paint (Graphics g) { Font f1 = new Font ("Times Roman", Font.PLAIN, 18); Font f2 = new Font ("Times Roman", Font.BOLD, 18); Font f3 = new Font ("Times Roman", Font.ITALIC, 18); Font f4 = new Font ("Times Roman", Font.BOLD + Font.ITALIC, 18); g.setFont (f1); g.drawString ("Este es texto normal", 10, 25);

52

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

Figura 5-4

5.6 Color Para cambiar el color hay que crear una instancia de la clase Color: Color c = new Color (r, g, b); donde r, g y b es la cantidad de rojo, verde y azul respectivamente, en el rango de 0 a 255. El (0,0,0) representa el negro y el (255, 255, 255) el blanco. En total hay 2563 = 16,777,216 colores distintos. Se puede usar alguno de los colores ya definidos en la clase Color como Color.white, Color.red, Color.black, Color.orange, Color.blue, Color.gray, Color.yellow, etc.

53

Hay 3 funciones para cambio de color: • • •

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

El listado 5-3 muestra la forma de cambiar el color al texto usando el método setColor. El listado 5-7 presenta un ejemplo donde se usa el método setBackground para cambiar el color del fondo y setColor para dibujar figuras de distinto color. La figura 5-5 muestra la salida del applet. Listado 5-7 import java.awt.Graphics; import java.applet.*; import java.awt.Color; public class pruebaColor extends Applet { public void paint (Graphics g) { Color c1 = new Color (120, 30, 220); Color c2 = new Color (40, 240, 200); setBackground (Color.lightGray); g.setColor (c1); g.drawString ("Ovalo hueco", 5, 20); g.drawOval (5, 30, 100, 60); g.drawString ("Circulo hueco", 150, 20); g.drawOval (150, 30, 60, 60); g.setColor (c2); g.drawString ("Ovalo lleno", 5, 120); g.fillOval (5, 130, 100, 60); g.setColor (Color.red); g.drawString ("Circulo lleno", 150, 120); g.fillOval (150, 130, 60, 60); } }

54

Figura 5-5

5.7 Imágenes Un applet pueden desplegar imágenes en formato GIF y JPEG. Lo primero es crear una instancia de la clase Image. Image mi_imagen = getImage (URL, nombre); donde URL es la dirección donde está la imagen y nombre es el nombre del archivo que contiene la imagen. Para la variable URL hay 3 opciones: a) Poner la dirección absoluta. Si la imagen esta en: http://www.foo.com/images/foo.gif, entonces poner: Image imagen = getImage (new URL ("http://www.foo.com/images", "foo.gif"); b) Usar la dirección relativa con respecto a donde está la página HTML. Si el directorio images está abajo de donde está la página, entonces poner: Image imagen = getImage (getDocumentBase (), "images/foo.gif");

55

c) Usar la dirección relativa a donde está el applet compilado (archivo .class). Si el directorio images está abajo de donde está el código objeto, entonces poner: Image imagen = getImage (getCodeBase (), "images/foo.gif"); Para desplegar la imagen: drawImage (imagen, x, y, ancho, alto, donde); donde imagen es un objeto de tipo Image, x y y son las coordenadas de la esquina superior izquierda de la imagen. Las variables ancho y alto indica el ancho y el alto en pixeles con que se va a desplegar la imagen. Por último, la variable donde es el nombre del objeto que va a actuar como observador de la imagen que en el caso de los applets es el mismo applet. El listado 5-8 presenta un applet que despliega una imagen en la pantalla, los métodos getWidth y getHeight regresan el ancho y el alto con los que se generó la imagen originalmente. Luego dibuja la imagen con un tamaño de 25%, 50%, 100% y 150% con respecto al tamaño original. Por último, distorsiona la imagen haciendo que tenga la mitad del ancho original y una vez y media la altura original. La figura 5-6 muestra la salida de este applet. Listado 5-8 import java.awt.Graphics; import java.applet.*; import java.awt.Image; public class Bart extends Applet { Image bart; public void init () { bart = getImage (getCodeBase (), "bart_id.gif"); } public void paint (Graphics g) { int ancho = bart.getWidth (this); int alto = bart.getHeight (this); int xpos = 10, ypos = 10; // 25 % g.drawImage (bart, xpos, ypos, ancho / 4, alto / 4, this); // 50 % xpos += (ancho / 4) + 10; g.drawImage (bart, xpos, ypos, ancho / 2, alto / 2, this);

56

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

Figura 5-6

57

58

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

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

59

los applets, la programación de interfaces gráficas de usuario usando AWT es igual en applets que en aplicaciones. Cada componente corresponde a una clase. Para utilizar un componente primero hay que crear un objeto de ese tipo y luego agregarlo al contenedor usando el método add.

6.2 Componentes básicos 6.2.1 Etiquetas Una etiqueta es un texto no editable que despliega un letrero en la pantalla. Para definir una etiqueta hay que crear un objeto de tipo Label indicando el texto de la etiqueta y opcionalmente una alineación: Label etiqueta1 = new Label ("Hola"); Label etiqueta2 = new Label ("Hola", Label.CENTER); Si en algún momento se desea cambiar el texto de la etiqueta se puede hacer con el método setText. etiqueta1.setText ("Ahora valgo esto"); El listado 6-1 presenta un ejemplo de un applet que despliega 2 etiquetas. La figura 6-2 muestra la salida del programa. Listado 6-1 import java.awt.*; import java.applet.*; public class lbl extends Applet { Label lbl = new Label ("Soy una etiqueta"); Label lbl2 = new Label ("Soy otra etiqueta", Label.RIGHT); public void init () { add (lbl); add (lbl2); } }

60

Figura 6-2 6.2.2 Botones Un botón es un componente gráfico que activa algún evento cuando se le oprime usando el ratón. Para definir un botón hay que crear un objeto de tipo Button indicando el texto que desplegará el botón: Button btn = new Button ("Continuar"); Luego habría que indicarle la acción que realizará al ser oprimido pero eso se verá más adelante en la sección 6.4. Si en algún momento se desea cambiar el texto del botón se puede invocar al método setLabel. btn.setLabel ("No continúo"); El listado 6-2 presenta un applet que despliega dos botones. La figura 6-3 muestra la salida de este applet.

61

Listado 6-2 import java.awt.*; import java.applet.*; public class btn extends Applet { Button btn1 = new Button ("Soy un botón"); Button btn2 = new Button ("Soy otro botón"); public void init () { add (btn1); add (btn2); } }

Figura 6-3 6.2.3 Checkboxes Un checkbox es un componente que tiene dos estados: marcado o no marcado. Los checkboxes se pueden agrupar de tal forma que solo un checkbox de ese grupo pueda estar activado a la vez. Para definir un checkbox hay que crear un objeto de tipo Checkbox indicando un texto como etiqueta y opcionalmente un booleano para indicar si de entrada está marcada o no: Checkbox cb1 = new Checkbox ("Fútbol"); Checkbox cb2 = new Checkbox ("Hockey", true); // marcado de inicio 62

Si se desea agrupar varios checkboxes, primero hay que crear un objeto de tipo CheckboxGroup y luego al momento de crear los checkboxes se indica que pertenecen al mismo grupo. CheckboxGroup cbg = new CheckboxGroup (); Checkbox cb1 = new Checkbox ("Fútbol", true, cbg); Checkbox cb2 = new Checkbox ("Hockey", false, cbg); Checkbox cb3 = new Checkbox ("Béisbol", false, cbg); Para saber si un checkbox está marcado o no se utiliza el método getState. if (cb2.getState ()) ... Para marcar (o desmarcar) desde un programa un checkbox se usa el método setState pasando un argumento booleano que indique el nuevo estado. cb1.setState (false); // desmarca el checkbox cb1 El listado 6-3 presenta un applet que despliega 5 checkboxes no agrupados y por lo tanto más de uno puede estar activado. La figura 6-4 muestra la salida. En cambio el applet del listado 6-4 declara 2 grupos de checkboxes. En cada grupo solo uno de ellos puede estar activado. La figura 6-5 muestra la salida de este último applet. Listado 6-3 import java.awt.*; import java.applet.*; public class cb extends Applet { Checkbox cb1 = new Checkbox ("opcion 1"); Checkbox cb2 = new Checkbox ("opcion 2"); Checkbox cb3 = new Checkbox ("opcion 3"); Checkbox cb4 = new Checkbox ("opcion 4", true); Checkbox cb5 = new Checkbox ("opcion 5"); public void init () { add (cb1); add (cb2); add (cb3); add (cb4); add (cb5); } }

63

Figura 6-4 Listado 6-4 import java.awt.*; import java.applet.*; public class cbg extends Applet { Label lbl_c = new Label ("Estado civil"); Label lbl_n = new Label ("Nacionalidad"); CheckboxGroup cbg1 = new CheckboxGroup (); CheckboxGroup cbg2 = new CheckboxGroup (); Checkbox cb_s = new Checkbox ("soltero", false, cbg1); Checkbox cb_c = new Checkbox ("casado", true, cbg1); Checkbox cb_v = new Checkbox ("viudo", false, cbg1); Checkbox cb_m = new Checkbox ("mexicano", true, cbg2); Checkbox cb_e = new Checkbox ("extranjero", false, cbg2); public void init () { add (lbl_c); add (cb_s); add (cb_c); add (cb_v); add (lbl_n); add (cb_m); add (cb_e); } }

64

Figura 6-5 6.2.4 Choices Un choice es un menú del cual se puede escoger solo una opción. Las listas (que se verán sección 6.5.4 permiten seleccionar más de una opción). Para usar un choice hay que declarar un objeto de tipo Choice y luego usar el método add para agregar la lista de opciones. Choice ch_deportes = new Choice (); ch_deportes.add ("Fútbol"); ch_deportes.add ("Béisbol"); ch_deportes.add ("Hockey"); ch_deportes.add ("Fútbol Americano"); Para saber que opción fue seleccionada se usa el método getSelectedIndex que regresa un número entre 0 y n – 1, donde n es el número de elementos del choice, indicando el índice del elemento seleccionado. if (ch_deportes.getSelectedIndex () == 0) ... Si se desea seleccionar una opción desde un programa se utiliza el método select pasando como argumento el índice de la opción que se desea quede seleccionada. ch_deportes.select (2);

65

El listado 6-5 presenta un applet que despliega un choice de 5 opciones. La figura 6-6 muestra la salida. Listado 6-5 import java.awt.*; import java.applet.*; public class chs extends Applet { Choice ch_deportes = new Choice (); public void init () { ch_deportes.add ("Fútbol"); ch_deportes.add ("Beísbol"); ch_deportes.add ("Hockey"); ch_deportes.add ("Fútbol Americano"); ch_deportes.add ("Lacrosse"); add (ch_deportes); } }

Figura 6-6

66

6.2.5 Campos de texto Un campo de texto es un componente que permite capturar y editar una línea de texto. Para usar un campo de texto hay que crear un objeto de tipo TextField indicando el número de caracteres de ancho y opcionalmente un texto inicial. TextField tf1 = new TextField (20); // tamaño aproximado del campo TextField tf2 = new TextField ("Pon tu nombre"); TextField tf3 = new TextField ("Escribe algo", 10); Se puede hacer que un campo de texto oculte el texto mientras se escribe en él usando el método setEchoChar, pasando como argumento el carácter que se desea desplegar en lugar del texto que se escribe. tf2.setEchoChar ('*'); // desplegará asteriscos Para obtener el texto que se escribió en un campo de texto se usa el método getText. String texto = tf1.getText (); Para cambiar el texto desde el programa se usa el método setText indicando en el argumento el nuevo texto. tf3.setText ("Ahora despliego esto"); El listado 6-6 presenta un applet con 3 campos de texto. El tercero oculta con asteriscos los caracteres que se le escriban. La figura 6-7 muestra la salida del programa. Listado 6-6 import java.awt.*; import java.applet.*; public class tf extends Applet { TextField tf1 = new TextField (10); TextField tf2 = new TextField ("Nombre de la ciudad",30); TextField tf3 = new TextField (15); public void init () { add (tf1); add (tf2); tf3.setEchoChar ('*'); add (tf3); } }

67

Figura 6-7

6.3 Diseños 6.3.1 Definición Los diseños determinan la forma en que los componentes se distribuyen en la pantalla. Cada elemento contenedor (ventanas, diálogos, paneles, lienzos, applet) puede (y debe) tener su propio diseño. Solo puede haber un diseño activo a la vez por contenedor. AWT tiene cuatro diseños básicos. Cada diseño corresponde a una clase. Para usar un diseño primero hay que instanciar una de las clases y luego invocar el método setLayout con pasando como argumento el objeto del tipo de diseño. 6.3.2 Diseño de flujo El diseño de flujo distribuye los componentes de izquierda a derecha por renglones. Los renglones se pueden alinear a la izquierda, al centro o a la derecha. Para usar un diseño de flujo es necesario crear un objeto de la clase FlowLayout. Opcionalmente se le puede indicar una alineación y espacios verticales y horizontales entre componentes. FlowLayout fl1 = new FlowLayout (); // alineado al centro por default FlowLayout fl2 = new FlowLayout (FlowLayout.CENTER); FlowLayout fl3 = new FlowLayout (FlowLayout.LEFT, 30, 10);

68

El listado 6-7 se presenta un applet que agrega 6 botones de acuerdo a un diseño de flujo centrado y con un espacio entre componentes de 30 pixeles en forma horizontal y de 10 verticales. La figura 6-8 muestra la salida del applet, es importante notar como los botones se colocan por líneas en el área reservada para el applet mientras quepan. Si el tamaño de la ventana cambia, los botones se acomodan al nuevo tamaño y puede que ocupen más o menos renglones. Listado 6-7 import java.awt.*; import java.applet.*; public class fl extends Applet { FlowLayout fl = new FlowLayout (FlowLayout.CENTER, 30, 10); Button btn1 = new Button ("Uno"); Button btn2 = new Button ("Dos"); Button btn3 = new Button ("Tres"); Button btn4 = new Button ("Cuatro"); Button btn5 = new Button ("Cinco"); Button btn6 = new Button ("Seis"); public void init () { setBackground (Color.lightGray); setLayout (fl); add (btn1); add (btn2); add (btn3); add (btn4); add (btn5); add (btn6); } }

69

Figura 6-8 6.3.3 Diseño de parrilla El diseño de parrilla divide el contenedor en una cuadrícula de m renglones y n columnas. En cada celda cabe un componente. Los componentes se van agregando por renglones de izquierda a derecha. Para usar un diseño de parrilla es necesario crear un objeto de la clase GridLayout indicando el número de renglones y de columnas. En forma opcional se puede indicar el espacio en pixeles entre componentes. GridLayout gl1 = new GridLayout (4, 2); // 4 renglones 2 columnas GridLayout gl2 = new GridLayout (4, 2, 30, 10); El listado 6-8 presenta un applet que especifica una parrilla de 4 columnas y 2 renglones, con un espacio entre componentes de 40 pixeles en forma horizontal y 30 en forma vertical. La figura 6-9 muestra la salida del programa. Listado 6-8 import java.awt.*; import java.applet.*; public class gl2 extends Applet { GridLayout gl = new GridLayout (4, 2, 40, 30); Label lbl_nombre = new Label ("Introduzca su nombre:");

70

Figura 6-8 Continuación Label lbl_telefono = new Label ("Introduzca su telefono:"); Label lbl_passwd = new Label ("Introduzca su password:"); TextField tf_nombre = new TextField (20); TextField tf_telefono = new TextField (10); TextField tf_passwd = new TextField (20); Button btn_continuar = new Button ("Continuar"); Button btn_cancelar = new Button ("Cancelar"); public void init () { setBackground (Color.lightGray); setLayout (gl); tf_passwd.setEchoChar ('*'); add (lbl_nombre); add (tf_nombre); add (lbl_telefono); add (tf_telefono); add (lbl_passwd); add (tf_passwd); add (btn_continuar); add (btn_cancelar); } }

Figura 6-9

71

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

Figura 6-10 Para usar un diseño de borde hay que crear un objeto de tipo BorderLayout especificando opcionalmente el espacio horizontal y vertical entre pixeles BorderLayout bl1 = new BorderLayout (); BorderLayout bl2 = new BorderLayout (20, 40); Al momento de agregar el componente se agrega un argumento extra al método add para indicar la zona en donde se colocará el componente. add ("North", componente); // coloca el componente al norte El listado 6-9 presenta el applet que generó la pantalla que se ilustra en la figura 6-10.

72

Listado 6-9 import java.awt.*; import java.applet.*; public class bl extends Applet { BorderLayout bl = new BorderLayout (); Button btn1 = new Button ("Norte"); Button btn2 = new Button ("Centro"); Button btn3 = new Button ("Sur"); Button btn4 = new Button ("Oeste"); Button btn5 = new Button ("Este"); public void init () { setLayout (bl); add ("North", btn1); add ("Center", btn2); add ("South", btn3); add ("West", btn4); add ("East", btn5); } } 6.3.5 Diseño de parrilla global Al igual que el diseño de parrilla normal, el diseño de parrilla global divide el contenedor en una cuadrícula de m renglones y n columnas, y en cada celda cabe un componente. Las diferencias son que una celda puede ocupar más de un renglón o más de una columna y que los componentes no se agregan por renglones sino que hay que indicar la columna y el renglón de la celda donde va a colocarse el componente. Para usar un diseño de parrilla global se usan dos clases: la clase GridBagLayout para el diseño en general y la clase GridBagConstraints para definir las restricciones de cada componente. GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Para cada componente que se vaya a agregar primero se definen sus restricciones, luego se le avisa al diseño cuáles son esas restricciones y por último se agrega el componente. La clase GridBagConstraints define 10 variables para especificar las restricciones: • • •

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

73

• • • • •

• •

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

Una vez que se especifica cada restricción se le avisa al diseño con el método setConstraints pasando como parámetro el componente y el objeto de tipo GridBagConstraints. gbl.setConstraints (componente, gbc); Por último se agrega el componente con el método add. add (componente); El listado 6-10 presenta un applet que utiliza el diseño de parrilla global para colocar los componentes en 3 renglones y 2 columnas. El componente del tercer renglón ocupa las dos columnas porque su restricción gridwidth vale 2. La figura 6-11 muestra la salida. Listado 6-10 import java.awt.*; import java.applet.*; public class gbl extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Label lbl_nombre = new Label ("Introduzca su nombre:"); Label lbl_telefono = new Label ("Introduzca su telefono:"); Label lbl_passwd = new Label ("Introduzca su password:"); TextField tf_nombre = new TextField (20); TextField tf_telefono = new TextField (10); TextField tf_passwd = new TextField (20);

74

Listado 6-10 Continuación Button btn_continuar = new Button ("Continuar"); public void init () { setLayout (gbl); // Etiqueta de nombre gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST; gbl.setConstraints (lbl_nombre, gbc); add (lbl_nombre); // Textfield de nombre gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (tf_nombre, gbc); add (tf_nombre); // Etiqueta de telefono gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST; gbl.setConstraints (lbl_telefono, gbc); add (lbl_telefono); // Textfield de telefono gbc.gridx = 1; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (tf_telefono, gbc); add (tf_telefono); // Etiqueta de password gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 1; gbc.gridheight = 1;

75

Listado 6-10 Continuación gbc.weightx = 0; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST; gbl.setConstraints (lbl_passwd, gbc); add (lbl_passwd); // Textfield de password gbc.gridx = 1; gbc.gridy = 2; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (tf_passwd, gbc); tf_passwd.setEchoChar ('*'); add (tf_passwd); // Boton de continuar gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 10; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; gbl.setConstraints (btn_continuar, gbc); add (btn_continuar); } }

76

Figura 6-11

6.4 Eventos 6.4.1 Introducción Un evento es una acción que se genera al cambiar el estado de un componente de AWT. Los eventos es la forma que tiene un programa para enterarse de que ocurrió algo y reaccionar conforme al suceso. Un evento puede ser una entrada de usuario (movimientos o clicks del ratón, pulsaciones de teclas), cambios en el medio ambiente del sistema (abrir, cerrar o mover una ventana) o cualquier otra actividad que pudiera afectar la operación del programa. Java soporta varios tipos de eventos. Cada vez que ocurre alguna acción de las que se listan a continuación se genera un evento que puede ser atrapado por un programa: • • • • •

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

77

• •

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

Para que un programa atienda un evento se necesita asignar al componente un oidor o escuchador (listener en la terminología de Java). Dentro del oidor se escribe el código que se desea ejecutar cuando ocurra el evento. Hay dos estrategias para definir oidores: la primera es usar una clase que tenga un solo método por cada tipo de evento que atienda todos los eventos que se generen de ese tipo. La segunda estrategia consiste en declarar oidores anónimos, uno por cada componente que pueda generar el evento en particular. La ventaja de la primera estrategia es que el manejo de eventos se centraliza en un solo método. La desventaja es que ese método debe ser capaz de distinguir cuál componente fue el que generó el evento. Respecto a la segunda estrategia, la ventaja es que no es necesario distinguir nada, cada componente tiene su propio oidor y el código se vuelve más legible y elegante. La desventaja es que la cantidad de código generado se vuelve mayor. En mi opinión la ventaja de los oidores anónimos sobrepasa la desventaja y por eso en este curso se definen oidores anónimos para el manejo de eventos. En las secciones siguientes vamos a revisar los eventos más utilizados que son los de botón, de choice y de checkbox. 6.4.2 Eventos de botón Los botones generan un evento de acción al ser oprimidos con el ratón. El programa debe registrar un oidor por cada botón en que esté interesado. A esta acción se le conoce como registrar el oidor y se hace con el método addActionListener. Button b1 = new Button ("Uno"); Button b2 = new Button ("Dos"); b1.addActionListener ( new java.awt.event.ActionListener () { public void actionPerformed (ActionEvent e) { aquí va el código que se ejecutará cuando el botón b1 se oprima } }); b2.addActionListener ( new java.awt.event.ActionListener () { public void actionPerformed (ActionEvent e) { aquí va el código que se ejecutará cuando el botón b2 se oprima } }); 78

Lo que está haciendo el código anterior es crear una instancia anónima de la clase ActionListener para cada botón y registrarla como oidor de ese botón usando su método addActionListener. La instancia anónima está sobreponiendo el método actionPerformed que por definición es el método que la máquina virtual de Java invoca cuando se genera un evento de acción que en este caso se genera al oprimir el botón. El listado 6-11 presenta el código de un applet que tiene dos campos de texto y dos botones, uno tiene la etiqueta de Continuar y otro de Limpiar. El botón de Continuar copia el texto que se haya tecleado en el campo de texto superior al inferior. El botón de Limpiar borra el texto de ambos campos de texto. La figura 6-12 muestra la salida del programa. Listado 6-11 import java.awt.*; import java.awt.event.*; import java.applet.*; public class eventos_btn extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Label lbl_algo = new Label ("Escribe algo:"); TextField tf_algo = new TextField (20); Label lbl_esto = new Label ("Esto escribiste:"); TextField tf_esto = new TextField (20); Button btn_continuar = new Button ("Continuar"); Button btn_limpiar = new Button ("Limpiar"); public void init () { setLayout (gbl); // Etiqueta de algo gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST; gbl.setConstraints (lbl_algo, gbc); add (lbl_algo); // Textfield de algo gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (tf_algo, gbc); add (tf_algo);

79

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

80

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

Figura 6-12 6.4.3 Eventos de choice Un choice genera un evento cuando se selecciona una opción. Al igual que en el caso de los botones hay que registrar un oidor, solo que en el caso de los choices el registro se hace con el método addItemListener, el oidor debe ser del tipo ItemListener y el método que se sobrepone es itemStateChanged. Choice ch = new Choice (); ch.add ("opción 1"); .... ch.add ("opción n"); ch.addItemListener ( new java.awt.event.ItemListener () { public void itemStateChanged (ItemEvent e) { código que se desea que se ejecute cuando se seleccione una opción }}); 81

El listado 6-12 presenta un applet que tiene un choice y un campo de texto. Cada vez que se selecciona una opción del choice el campo de texto despliega el nombre de la opción. La figura 6-13 muestra la salida del applet una vez que se seleccionó la opción 4. Listado 6-12 import java.awt.*; import java.awt.event.*; import java.applet.*; public class eventos_chs extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Choice c = new Choice (); Label lbl_opcion = new Label ("La opcion fue: "); TextField tf_opcion = new TextField (20); public void init () { setLayout (gbl); c.add ("opcion 1"); c.add ("opcion 2"); c.add ("opcion 3"); c.add ("opcion 4"); c.add ("opcion 5"); // Choice gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST; gbl.setConstraints (c, gbc); add (c); // Etiqueta de opcion gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST; gbl.setConstraints (lbl_opcion, gbc); add (lbl_opcion); // Textfield de opcion gbc.gridx = 1; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE;

82

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

Figura 6-13 6.4.4 Eventos de checkbox Los checkboxes generan un evento al ser marcados o desmarcados. Al igual que en el caso de los choices hay que registrar un oidor con el método addItemListener, el oidor debe ser del tipo ItemListener y el método que se sobrepone es itemStateChanged. Checkbox cb1 = new Choice ("opción 1"); cb1.addItemListener ( new java.awt.event.ItemListener () { public void itemStateChanged (ItemEvent e) { 83

código que se desea que se ejecute al marcar o desmarcar el checkbox 1 }}); El listado 6-13 presenta un applet que tiene dos checkboxes y un campo de texto. Cada vez que se marca o se desmarca un checkbox se escribe un mensaje alusivo a la acción en el campo de texto. La figura 6-14 presenta la salida después de marcar el primer checkbox. Listado 6-13 import java.awt.*; import java.awt.event.*; import java.applet.*; public class eventos_cb extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Checkbox cb1 = new Checkbox ("opcion 1"); Checkbox cb2 = new Checkbox ("opcion 2"); Label lbl_mssg = new Label ("Mensaje: "); TextField tf_mssg = new TextField (20); public void init () { setLayout (gbl); // Checkbox 1 gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST; gbl.setConstraints (cb1, gbc); add (cb1); // Checkbox 2 gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 50; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (cb2, gbc); add (cb2); // Etiqueta de mensaje gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0; gbc.weighty = 30; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.EAST;

84

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

85

Figura 6-14

6.5 Componentes avanzados 6.5.1 Paneles Un panel es un componente contenedor y como tal puede contener otros componentes de AWT. Se utilizan para eliminar la restricción que imponen los diseños de que solo cabe un componente por celda o por zona y poder programas interfaces gráficas complejas. Un panel tiene su propio diseño y sus propios componentes como botones, campos de texto, etc. Los paneles a su vez pueden contener otros paneles que a su vez tengan otros componentes. No hay restricción en el número de paneles ni en el nivel de anidación. Parar usar un panel hay que crear un objeto de tipo Panel. Panel p = new Panel (); Como se dijo anteriormente los paneles pueden tener su propio diseño especificado con el método setLayout. p.setLayout (diseño); Todo lo que se ha dicho sobre agregar componentes con el método add es válido para los paneles. p.add (botón); p.add (checkbox);

86

El uso de paneles puede mejorar el diseño de una interface gráfica evitando que los componentes se vean alineados como si estuvieran en una parrilla gigante. Una regla de diseño especifica que los componentes que están relacionados entre sí se coloquen juntos en un panel. El listado 6-14 presenta un applet que utiliza 3 paneles en un diseño de parrilla vertical. El panel de en medio tiene a su vez dos paneles para que sus componentes no se amontonen en un solo lado. La figura 6-15 muestra la salida del programa. Listado 6-14 import java.awt.*; import java.applet.*; public class testPanel extends Applet { Panel pnl_1 = new Panel (); Panel pnl_2 = new Panel (); Panel pnl_3 = new Panel (); Panel pnl_4 = new Panel (); Panel pnl_5 = new Panel (); Checkbox cb_1 = new Checkbox ("Opcion uno"); Checkbox cb_2 = new Checkbox ("Opcion dos"); Checkbox cb_a = new Checkbox ("Opcion A"); Checkbox cb_b = new Checkbox ("Opcion B"); Checkbox cb_c = new Checkbox ("Opcion C"); Label lbl_1 = new Label ("Soy etiqueta"); Choice ch_1 = new Choice (); Choice ch_2 = new Choice (); Button btn_1 = new Button ("Soy un boton"); Button btn_2 = new Button ("Soy otro boton"); public void init () { setBackground (Color.lightGray); setLayout (new GridLayout (3, 1)); add (pnl_1); add (pnl_2); add (pnl_5); // panel 1 pnl_1.setLayout (new FlowLayout (FlowLayout.LEFT)); pnl_1.add (cb_1); pnl_1.add (cb_2); // panel 2 pnl_2.setLayout (new BorderLayout ()); // panel 3 pnl_3.setLayout (new GridLayout (3, 1)); pnl_3.add (cb_a); pnl_3.add (cb_b);

87

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

Figura 6-15 6.5.2 Lienzos Un lienzo es un componente cuya principal utilidad es desplegar gráficas o imágenes. Debido a que no puede contener otros componentes por lo general no se usa en forma directa sino a través de programar una clase que extienda a la clase Canvas. public miCanvas extends Canvas {

88

La clase Canvas define un método paint vacío. La clase que extienda a Canvas sobrepone el método paint para dibujar o desplegar una imagen de la misma forma en que lo hace el método paint de un applet. Si se revisa el manual de Java encontraremos que tanto la clase Canvas como la clase Applet son subclases de la clase Component y de ella heredan el método paint. La clase Component representa objetos que pueden ser desplegados en la pantalla y pueden interactuar con el usuario. Otras subclases de Component son precisamente las clases que definen los componentes que ya vimos como Panel, Button, TextField, etc. El listado 6-15 presenta un applet que del lado izquierdo coloca un lienzo donde dibuja una lámpara sobre una mesa y del lado derecho un panel donde hay dos choices que controlan el color con que se dibujan la mesa y la lámpara. La clase miCanvas es la que realmente hace el dibujo en su método paint y además tiene que definir dos métodos para que el applet le avise si cambia el color. La figura 6-16 muestra la salida del applet. Por otra parte el listado 6-16 presenta un applet que utiliza un lienzo para desplegar una imagen GIF. El método paint de la clase idCanvas es el que despliega la imagen. La figura 6-17 muestra la salida de este applet. Listado 6-15 import java.applet.*; import java.awt.*; import java.awt.event.*; public class testCanvas extends Applet { miCanvas mc; Panel pnl_1; Label lbl_1 = new Label ("La lampara de Aladino"); Label lbl_2 = new Label ("Color de la mesa"); Choice color_mesa = new Choice (); Label lbl_3 = new Label ("Color de la lampara"); Choice color_lampara = new Choice (); public void init () { setLayout (new BorderLayout ()); lbl_1.setFont (new Font ("TimesRoman", Font.BOLD, 18)); add ("North", lbl_1); add ("Center", mc = new miCanvas ()); add ("East", pnl_1 = new Panel ()); pnl_1.setLayout (new GridLayout (4, 1)); pnl_1.add (lbl_2); color_mesa.add ("Negro"); color_mesa.add ("Rojo"); color_mesa.add ("Azul"); color_mesa.add ("Verde"); pnl_1.add (color_mesa); pnl_1.add (lbl_3);

89

Listado 6-15 Continuación color_lampara.add ("Negro"); color_lampara.add ("Rojo"); color_lampara.add ("Azul"); color_lampara.add ("Verde"); pnl_1.add (color_lampara); // oidores color_mesa.addItemListener ( new java.awt.event.ItemListener () { public void itemStateChanged (ItemEvent e) { mc.cambiaColorMesa (color_mesa.getSelectedIndex ()); } }); color_lampara.addItemListener ( new java.awt.event.ItemListener () { public void itemStateChanged (ItemEvent e) { mc.cambiaColorLampara (color_lampara.getSelectedIndex ()); } }); } } class miCanvas extends Canvas { int color_mesa = 0; int color_lampara = 0; public void paint (Graphics g) { switch (color_mesa) { case 0 : g.setColor (Color.black); break; case 1 : g.setColor (Color.red); break; case 2 : g.setColor (Color.blue); break; case 3 : g.setColor (Color.green); break; } g.fillRect (0, 250, 290, 290); // dibuja la mesa switch (color_lampara) { case 0 : g.setColor (Color.black); break; case 1 : g.setColor (Color.red); break; case 2 : g.setColor (Color.blue); break; case 3 : g.setColor (Color.green); break; } g.drawLine (125, 250, 125, 160); g.drawLine (175, 250, 175, 160); // dibuja la base g.drawArc (85, 157, 130, 50, -65, 312); // dibuja la cubierta

90

Listado 6-15 Continuación g.drawArc (85, 87, 130, 50, 62, 58); g.drawLine (85, 177, 119, 89); g.drawLine (215, 177, 181, 89); g.fillArc (78, 120, 40, 40, 63, -174); // dibuja las motas g.fillOval (120, 96, 40, 40); g.fillArc (173, 100, 40, 40, 110, 180); } public void cambiaColorMesa (int c) { color_mesa = c; repaint (); } public void cambiaColorLampara (int c) { color_lampara = c; repaint (); } }

Figura 6-16

91

Listado 6-16 import java.awt.*; import java.applet.*; public class Licencia extends Applet { Panel pnl_east = new Panel (); Panel pnl_north = new Panel (); Panel pnl_1 = new Panel (); Panel pnl_2 = new Panel (); Panel pnl_3 = new Panel (); Label lbl_dmv = new Label ("Department of Motor Vehicles"); Label lbl_number = new Label ("B47U89RE243"); Label lbl_nombre = new Label ("BART JO-JO SIMPSON"); Label lbl_calle = new Label ("742 Evergreen Terrace"); Label lbl_ciudad = new Label ("Springfield, U.S.A."); Label lbl_sex1 = new Label ("Sex"); Label lbl_height1 = new Label ("Height"); Label lbl_eyes1 = new Label ("Eyes"); Label lbl_weight1 = new Label ("Weight"); Label lbl_sex2 = new Label ("M"); Label lbl_height2 = new Label ("4'0"); Label lbl_eyes2 = new Label ("blue"); Label lbl_weight2 = new Label ("85"); public void init () { setBackground (Color.black); setForeground (Color.yellow); setLayout (new BorderLayout ()); Image foto = getImage (getCodeBase (), "bart_id.gif"); // agrega la foto idCanvas cvs_foto = new idCanvas (foto); add ("Center", cvs_foto); pnl_north.setLayout (new BorderLayout ()); lbl_dmv.setFont (new Font ("TimesRoman", Font.BOLD, 18)); pnl_north.add ("Center", lbl_dmv); // agrega el titulo add ("North", pnl_north); pnl_east.setLayout (new BorderLayout ()); pnl_1.setLayout (new BorderLayout ()); pnl_1.add ("Center", lbl_number); // agrega la descripción pnl_east.add ("North", pnl_1); pnl_2.setLayout (new GridLayout (3, 1)); pnl_2.add (lbl_nombre); pnl_2.add (lbl_calle); pnl_2.add (lbl_ciudad); pnl_east.add ("Center", pnl_2); pnl_3.setLayout (new GridLayout (2, 4)); pnl_3.add (lbl_sex1);

92

Listado 6-16 Continuación pnl_3.add (lbl_height1); pnl_3.add (lbl_eyes1); pnl_3.add (lbl_weight1); pnl_3.add (lbl_sex2); pnl_3.add (lbl_height2); pnl_3.add (lbl_eyes2); pnl_3.add (lbl_weight2); pnl_east.add ("South", pnl_3); add ("East", pnl_east); } } class idCanvas extends Canvas { Image image; idCanvas (Image image) { super (); this.image = image; } public void paint (Graphics g) { g.drawImage(image, 0, 0, 100, 100, this); g.drawRect(0, 0, 100, 100); } }

Figura 6-17

93

6.5.3 Áreas de texto Un área de texto es un componente que se utiliza para leer datos de entrada de manera semejante al campo de texto. La diferencia es que, mientras el campo de texto puede leer solo una línea de texto, el área de texto puede leer varias. Si el texto es más grande que el tamaño con el que se despliega el área de texto automáticamente se colocan barras de scroll horizontales o verticales según sea el caso. Para utiliza un área de texto hay que crear un objeto de tipo TextArea pasando como argumentos al constructor en forma opcional un string inicial, un tamaño preferido de desplegado en renglones y columnas y un indicador para que despliegue o no barras de scroll de inicio. TextArea ta1 = new TextArea (); // el tamaño se deja al diseño TextArea ta2 = new TextArea (10, 60); // 10 renglones y 60 columnas TextArea ta3 = new TextArea ("hola mundo", 10, 60); // texto inicial TextArea ta4 = new TextArea ("hola", 10, 60, TextArea.SCROLLBARS_NONE); A pesar de que el texto esté en varias líneas, el área de texto lo trata como un string, es decir, como si fuera una sola cadena de caracteres. Cuando desde un programa se altera el texto de un área de texto es responsabilidad del programador insertar saltos de línea ('\n') dentro del string para que el texto se visualice por renglones. El texto capturado se puede recuperar llamando al método getText. String s = ta1.getText (); Se puede asignar texto desde el programa con el método setText. ta2.setText ("Esto es una línea\nEsto es otra línea"); El listado 6-17 presenta un applet con dos áreas de texto. Después de escribir un texto cualquiera en la primera área de texto, al oprimir el botón de Cuenta, la segunda área de texto despliega el número de líneas y de caracteres que se escribieron. La figura 6-18 muestra la salida del programa. Listado 6-17 import java.awt.*; import java.awt.event.*; import java.applet.*; public class ta extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); TextArea ta1 = new TextArea ("Escribe algo"); TextArea ta2 = new TextArea ("Soy una textarea", 5, 25); Button btn = new Button ("Cuenta"); 94

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

95

Figura 6-18 6.5.4 Listas Una lista es similar al choice que permite seleccionar de entre varias opciones de una lista con dos diferencias: su presentación es en forma de lista y permite seleccionar más de una opción. Para usar una lista hay que crear un objeto de tipo List pasando como argumentos opcionales el número de opciones que estarán visibles y un indicador si la liste permite o no opciones múltiples. List lst1 = new List (); // permite escoger una opción List lst2 = new List (5); // cinco opciones visibles a la vez List lst3 = new List (5, true); // permite opciones múltiples Las opciones se agregan con el método add. lst2.add ("opción uno"); lst2.add ("opción dos"); 96

En las listas que permiten la selección de únicamente un elemento el método getSelectedIndex devuelve la posición del elemento seleccionado. int opción = lst1.getSelectedIndex (); En las listas que permiten múltiples selecciones el método getSelectedIndexes devuelve un arreglo de enteros con las posiciones de los elementos seleccionados. int opciones[] = lst3.getSelectedIndexes (); El listado 6-18 presenta un applet con un choice, una lista que permite seleccionar un solo elemento y una lista que permite selección múltiple. Al oprimir el botón, en la parte inferior se despliegan las opciones que fueron seleccionadas en el choice y en las listas. La figura 619 muestra la salida del programa. Listado 6-18 import java.awt.*; import java.awt.event.*; import java.applet.*; public class lst extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Choice chs_1 = new Choice (); List lst_1 = new List (); // una sola seleccion List lst_2 = new List (4, true); // seleccion multiple Button btn_revisar = new Button ("Revisar"); Label lbl_1 = new Label ("En el choice la opcion es: "); Label lbl_2 = new Label ("En la lista 1 la opcion es: "); Label lbl_3 = new Label ("En la lista 2 las opciones son:

");

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

97

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

98

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

Figura 6-19

99

6.5.5 Frames Un frame es una ventana que, dependiendo del sistema operativo, cuenta con un título, un menú de barra, indicadores para minimizar, maximizar o cerrar, y otras características típicas de las ventanas. Un frame es un componente contenedor y como tal puede tener su propio diseño y contener otros componentes de AWT. Para usar un frame hay que crear un objeto de tipo Frame pasando como parámetro el título de la ventana. Frame frm = new Frame ("titulo de la ventana"); El frame puede tener su propio diseño y componentes. frm.setLayout (new GridLayout (4, 1)); frm.add (new Button ("soy un botón")); frm.add (new Label ("soy una etiqueta")); Se le puede asignar un tamaño en pixeles usando el método setSize o indicarle las coordenadas de su esquina superior izquierda con setLocation. frm.setSize (400, 300); // 400 pixeles de ancho y 300 pixeles de alto frm.setLocation (200, 100); // 200 en x y 100 en y Cuando se crea un nuevo frame, éste es invisible. El método setVisible hace que el frame sea visible o de vuelta invisible mediante un argumento booleano que puede valer true (la ventana se hace visible) o false (el frame se hace invisible). frm.setVisible (true); // hace visible el frame El método dispose elimina el frame y libera la memoria. Se invoca cuando el frame ya no va a ser utilizado. frm.dispose (); Por default el indicador de cerrar ventana, que en Windows tiene una marca de cruz y está en la parte superior derecha del frame, esta inactivo. Es necesario crear un oidor de la clase WindowAdapter y registrarlo usando el método addWindowListener para indicarle al frame que se destruya al oprimir ese indicador. frm.addWindowListener ( new java.awt.event.WindowAdapter () { public void windowClosing (WindowEvent e) { 100

frm.dispose (); } }); El listado 6-19 presenta un applet que crea un frame, le agrega una etiqueta, un campo de texto y un botón y luego lo hace visible. La figura 6-20 muestra la salida de este programa. Listado 6-19 import java.awt.*; import java.awt.event.*; import java.applet.*; public class frm extends Applet { Frame frm_1 = new Frame ("Mi ventanita"); Panel pnl_1 = new Panel (); GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Label lbl_nombre = new Label ("Nombre"); TextField tf_nombre = new TextField (20); Button btn_ok = new Button ("OK"); Label lbl_applet = new Label ("aqui es el applet"); public void init () { // layout del applet setBackground (Color.lightGray); setLayout (new FlowLayout (FlowLayout.LEFT)); add (lbl_applet); // layout de la ventana frm_1.setLayout (gbl); // etiqueta de nombre gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 30; gbc.weighty = 80; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (lbl_nombre, gbc); frm_1.add (lbl_nombre); // textfield de nombre gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 70; gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.WEST; gbl.setConstraints (tf_nombre, gbc); frm_1.add (tf_nombre);

101

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

Figura 6-20

102

6.5.6 Diálogos Un diálogo es semejante a un frame en el sentido que es una ventana que puede tener su propio diseño y contener a otros componentes. Las diferencias son que los diálogos necesitan tener un frame ligado a ellos, no tienen indicadores de maximizar ni de minimizar y pueden operar en forma modal. Un diálogo modal impide que sean accesadas otras ventanas hasta que el diálogo sea cerrado. Por estos motivos y por lo general, los diálogos se utilizan en forma modal y para avisar de errores en el programa o pedir datos al usuario. Para usar un diálogo hay que crear un objeto de tipo Dialog pasando como argumento obligatorio el frame al cual están ligados y opcionalmente un argumento string con el título del diálogo y una variable booleana para indicar si el diálogo es modal o no modal. Dialog dlg1 = new Dialog (frame); // el diálogo es no modal por default Dialog dlg2 = new Dialog (frame, true); // el diálogo es modal Dialog dlg3 = new Dialog (frame, "titulo", true); // tiene título y es modal Los métodos setLayout, add, setSize, setLocation, setVisible, dispose y addWindowListener tienen la misma funcionalidad que en los frames. Para que un applet pueda usar un diálogo necesita hacer referencia a la ventana del navegador que lo contiene. Una forma de conseguirlo es obtener los componentes padres del applet, usando el método getParent, hasta encontrar un objeto de tipo Frame y pasar como argumento ese objeto al constructor del diálogo. Object tmp = getParent (); while (! (tmp instanceof Frame)) tmp = ((Component) tmp).getParent (); dlg1 = new Dialog ((Frame) tmp, "título", true); El listado 6-20 presenta un applet que abre un diálogo modal y por lo tanto se bloquea. Al cerrar el diálogo el programa puede continuar y abre un frame. La figura 6-21 muestra la salida del programa antes de cerrar el diálogo. Listado 6-20 import java.awt.*; import java.awt.event.*; import java.applet.*; public class dlgm extends Applet { GridBagLayout gbl = new GridBagLayout (); GridBagConstraints gbc = new GridBagConstraints (); Label lbl_error = new Label ("El frame no se abrira hasta cerrar este dialogo"); Label lbl_hola = new Label ("Hola"); Button btn_ok = new Button ("Cerrar"); Frame frm_1 = new Frame ("Ventanita");

103

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

104

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

Figura 6-21

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

Al estar los applets incrustados en una página HTML y correr en un navegador se prestan a que cualquier persona pueda correrlos sin necesidad de tener instalado el JDK en su computadora.



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

105

Sin embargo, como ya sabemos que por razones de seguridad, los applets tienen la restricción de que no pueden accesar los recursos locales de la computadora donde están corriendo y por eso muchas veces se tienen que programar aplicaciones. La programación de aplicaciones usando AWT es muy parecida a la programación de applets usando AWT. Hay tres diferencias: • • •

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

El listado 6-21 presenta la versión aplicación del applet que se presentó en el listado 6-16 y que despliega una imagen utilizando un lienzo. Se puede ver que el código es prácticamente el mismo en las dos versiones. La figura 6-22 muestra la salida del programa. Listado 6-21 import java.awt.*; import java.awt.event.*; public class DMV extends Frame { Panel pnl_east = new Panel (); Panel pnl_north = new Panel (); Panel pnl_1 = new Panel (); Panel pnl_2 = new Panel (); Panel pnl_3 = new Panel (); Label lbl_dmv = new Label ("Department of Motor Vehicles"); Label lbl_number = new Label ("B47U89RE243"); Label lbl_nombre = new Label ("BART JO-JO SIMPSON"); Label lbl_calle = new Label ("742 Evergreen Terrace"); Label lbl_ciudad = new Label ("Springfield, U.S.A."); Label lbl_sex1 = new Label ("Sex"); Label lbl_height1 = new Label ("Height"); Label lbl_eyes1 = new Label ("Eyes"); Label lbl_weight1 = new Label ("Weight"); Label lbl_sex2 = new Label ("M"); Label lbl_height2 = new Label ("4'0"); Label lbl_eyes2 = new Label ("blue"); Label lbl_weight2 = new Label ("85"); DMV () { setBackground (Color.lightGray); setLayout (new BorderLayout ());

106

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

107

Listado 6-21 Continuación class fotoCanvas extends Canvas { Image image; public fotoCanvas (Image image, Container padre) { super (); this.image = image; } public void paint (Graphics g) { g.drawImage(image, 0, 0, 100, 100, this); g.drawRect(0, 0, 100, 100); } }

Figura 6-22

108

Capítulo 7 Archivos 7.1 Definición En Java un flujo es una secuencia de bytes sin formato. Su origen puede ser un archivo en disco, un socket de comunicación de entrada, un arreglo, el teclado, etc. Su destino puede ser otro archivo de disco, un socket de comunicación de salida, otro arreglo, la pantalla, la impresora, etc. En general los flujos no tienen porque saber cuál es el origen ni el destino de la información que reciben, son las clases específicas las que determinan el origen y el destino. La ventaja de hacerlo así es que se pueden agregar orígenes y destinos de información sin que las clases generales tengan que cambiar. En este capítulo en particular vamos a estudiar los flujos que tiene que tienen como origen y como destino un archivo. Un archivo es una secuencia de bytes que se guarda en disco y que puede actuar como origen o como destino de un flujo. Los bytes no tienen ningún formato ni tamaño de registro. El programador es el encargado de ordenar y darle sentido a esa secuencia. La librería de entrada y salida de Java (java.io) divide las clases que manejan los flujos en dos grandes categorías: las clases lectoras que se ocupan de los flujos de entrada y las clases escritoras que se ocupan de los flujos de salida. Un vistazo rápido al manual nos muestra que la librería define más de 40 clases entre lectoras y escritoras, sin embargo, para efectos de una introducción al manejo de archivos en Java es suficiente con aprender a usar cuatro clases especificas de archivos (dos lectoras y dos escritoras) y seis clases generales de flujos (tres de cada clase). Al final del capítulo presentaremos otra clase general lectora que funciona para resolver un problema en particular.

7.2 Principales clases lectoras La clase FileInputStream es una clase específica que obtiene bytes de un archivo. En su constructor recibe como argumento el nombre físico de un archivo y crear un flujo de bytes. Define el método read que puede lee una secuencia de bytes y lo guarda en un arreglo de bytes. Esta clase es útil si lo que se busca es tratar a un archivo como una secuencia de bytes. El siguiente segmento de código ilustra la forma en que se puede leer un archivo en bloques de 1000 bytes. byte buffer[] = new byte[1000]; FileInputStream fd_in = new FileInputStream ("archivo.dat"); while (fd_in.read (buffer) != -1) { ... } fd_in.close ();

109

La clase InputStreamReader es una clase general que convierte un flujo de bytes en un flujo de caracteres. Define el método read que puede leer un carácter o un arreglo de caracteres. Esta clase se utiliza si sabemos que el flujo de entrada es de caracteres de texto. Como la clase es general y acepta cualquier flujo de bytes de entrada (no solo con origen en un archivo de disco) hay que pasarle como argumento en el constructor la clase específica ligada al origen de los datos y que en este caso es FileInputStream. El siguiente segmento de código ilustra la forma en que se lee un archivo de caracteres: InputStreamReader fd_in = new InputStreamReader ( new FileInputStream ("archivo.dat")); int c; while ((c = fd_in.read ()) != -1) { procesa el carácter } fd_in.close (); La clase BufferedReader recibe un flujo de caracteres e implementa un buffer para poder leer líneas de texto. Define el método readLine para leer una línea de texto. Esta clase se utiliza si sabemos que el archivo es de texto y está escrito en líneas separadas por retornos de carro. Esta clase es general, no está ligada a ningún origen en particular y por lo tanto hay que pasarle como argumento en su constructor un flujo de caracteres. Para el manejo de archivos hay dos formas para construir un objeto de tipo BufferedReader. Una es utilizar un objeto InputStreamReader creado sobre un objeto de tipo FileInputStream: BufferedReader fd_in = new BufferedReader ( new InputStreamReader ( new FileInputStream ("archivo.dat"))); La ventaja de hacerlo así es que se puede manejar el tamaño del buffer e incluso cambiar la codificación de los caracteres. La codificación de caracteres es la forma que tiene Java de convertir bytes de 8 bits en caracteres Unicode de 16 bits. Hay que recordar que en Java el tipo char ocupa 16 bits y utiliza el código Unicode que a su vez es un superconjunto del popular código ASCII de 7 bits. La otra forma es aceptar el tamaño del buffer y la codificación predefinidos, lo cuál muchas veces es lo más conveniente, y para estos casos se puede usar la clase FileReader que como argumento en el constructor se le pasa el nombre del archivo. BufferedReader fd_in = new BufferedReader ( new FileReader ("archivo.dat")); El listado 7-1 presenta un programa que lee un archivo de texto y lo despliega en la pantalla línea por línea.

110

Listado 7-1 import java.io.*; public class fileCharReader { public static void main (String args[]) { BufferedReader fd = null; String linea = ""; try { fd = new BufferedReader (new FileReader ("texto.txt")); } catch (FileNotFoundException e) { System.out.println ("No pude abrir el archivo texto.txt"); } try { while ((linea = fd.readLine ()) != null) System.out.println (linea); fd.close (); } catch (IOException e) { System.out.println ("Error al leer"); } } } En Java no hay una instrucción directa para leer datos del teclado. El teclado se considera como origen de un flujo de datos, está asociado con la variable System.in de tipo InputStreamReader y se trata de la misma manera que cualquier flujo. El listado 7-2 presenta un programa que lee del teclado una línea de texto y un entero. Listado 7-2 import java.io.*; public class teclado { public static void main (String args[]) { BufferedReader kbd = null; String linea = "", s; int n; // abre un archivo asignado al teclado kbd = new BufferedReader (new InputStreamReader (System.in)); try { // los strings se leen facil System.out.print ("escribe un string: ");

111

Listado 7-2 Continuación linea = kbd.readLine (); // los numeros hay que convertirlos de string a numero System.out.print ("escribe un entero: "); s = kbd.readLine (); n = new Integer (s).intValue (); System.out.println ("El string es: " + linea + " y el entero es: " + n); } catch (IOException e) { System.out.println ("Error de I/O"); } } } La clase DataInputStream permite leer tipos primitivos (enteros, reales, booleanos) de forma portable. Define métodos como readInt para leer un entero, readFloat para leer un real, readBoolean para leer un booleano, etc. Esta clase se utiliza si lo que se está leyendo es un archivo de datos binarios. Esta clase es general, no está ligada a ningún origen de datos en particular y por eso, para leer de un archivo, hay que mandarle como argumento en su constructor un objeto de tipo FileInputStream. El siguiente segmento de código muestra la forma en que se lee un archivo de datos que se sabe trae 1000 enteros. DataInputStream fd_in = new DataInputStream ( new FileInputStream ("archivo.dat")); int dato; for (int i = 0; i < 1000; i++) { dato = fd_in.readInt (); procesa el dato } El listado 7-3 presenta un programa que lee un archivo de datos en el cuál se sabe que vienen 5 enteros, 3 reales y un booleano en ese orden. Listado 7-3 import java.io.*; public class fileBinReader { public static void main (String args[]) { int a = 0; double b = 0; boolean c = false; DataInputStream dis = null;

112

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

7.3 Principales clases escritoras La clase FileOutputStream es una clase específica que escribe una secuencia de bytes a un archivo en disco. Define el método write que recibe como argumento un arreglo de bytes. Esta clase se utiliza si estamos tratando a un archivo como una secuencia de bytes. Para abrir un archivo para escritura hay que crear un objeto de tipo FileOutputStream pasando como argumento al constructor el nombre del archivo. FileOutputStream fd_out = new FileOutputStream ("archivo.dat"); El listado 7-4 presenta un programa que recibe el nombre de un archivo, que puede ser de texto o binario y genera una copia.

113

Listado 7-4 import java.io.*; public class fileCopyBin { public void copia (String infile, String outfile) { FileInputStream fd_in = null; FileOutputStream fd_out = null; try { fd_in = new FileInputStream (infile); } catch (FileNotFoundException e) { System.out.println ("El archivo " + infile + " no existe"); return; } try { fd_out = new FileOutputStream (outfile); } catch (IOException e) { System.out.println ("No pude crear el archivo " + outfile); return; } try { byte buffer[] = new byte[1000]; int num; while ((num = fd_in.read (buffer)) != -1) fd_out.write (buffer, 0, num); fd_in.close (); fd_out.close (); } catch (IOException e) { System.out.println ("Error de I/O " + e); return; } System.out.println ("Copiado"); } public static void main (String args[]) { fileCopyBin fcb = new fileCopyBin (); if (args.length > 1) fcb.copia (args[0], args[1]); else System.out.println ("Uso: java fileCopyBin arch1 arch2"); } }

114

La clase OutputStreamWriter es una clase general que convierte un flujo de caracteres en un flujo de bytes. Define el método write para escribir a la salida un carácter o un arreglo de caracteres. Esta clase se utiliza si sabemos que el flujo de salida es de caracteres de texto. Como la clase es general y acepta cualquier flujo de bytes de salida (no solo con destino en un archivo de disco) hay que pasarle como argumento en el constructor la clase específica ligada al destino de los datos y que en este caso es FileOutputStream. Los caracteres que recibe la clase se convierten a bytes de acuerdo a una codificación de caracteres en específico y cada invocación al método write provoca que el convertidor de codificación sea invocado. Por esta razón el manual de Java recomienda no usar esta clase por separado, más bien como intermedio de la clase BufferedWriter que veremos a continuación. La clase BufferedWriter escribe texto a un flujo de salida que acepte caracteres proporcionando un buffer para la escritura eficiente de caracteres, arreglos y strings. Define el método write para escribir una línea de texto y el método newLine para escribir un salto de línea de acuerdo al sistema operativo. Esta clase se utiliza si sabemos que el archivo es de texto y está escrito en líneas separadas por retornos de carro. Esta clase es general, no está ligada a ningún destino en particular y por lo tanto hay que pasarle como argumento en su constructor un flujo de caracteres. Para el manejo de archivos hay dos formas para construir un objeto de tipo BufferedWriter. Una es utilizar un objeto OutputStreamReader creado sobre un objeto de tipo FileOutputStream: BufferedWriter fd_out = new BufferedWriter ( new OutputStreamWriter ( new FileOutputStream ("archivo.dat"))); La ventaja de hacerlo así es que se puede manejar el tamaño del buffer e incluso cambiar la codificación de los caracteres. La otra forma es aceptar el tamaño del buffer y la codificación predefinidos, lo cuál muchas veces es lo más conveniente, y para estos casos se puede usar la clase FileWriter que como argumento en el constructor se le pasa el nombre del archivo. BufferedWriter fd_out = new BufferedWriter ( new FileWriter ("archivo.dat")); El listado 7-5 presenta una aplicación que copia un archivo de texto por líneas.

115

Listado 7-5 import java.io.*; public class fileCharCopy { public void copia (String infile, String outfile) { BufferedReader fd_in = null; BufferedWriter fd_out = null; String linea = ""; try { fd_in = new BufferedReader (new FileReader (infile)); } catch (FileNotFoundException e) { System.out.println ("No pude abrir el archivo" + infile); } try { fd_out = new BufferedWriter (new FileWriter (outfile)); } catch (IOException e) { System.out.println ("No pude crear el archivo" + outfile); } try { while ((linea = fd_in.readLine ()) != null) { fd_out.write (línea, 0, linea.length ()); fd.newLine (); } fd_in.close (); fd_out.close (); } catch (IOException e) { System.out.println ("Error al leer"); } } } public static void main (String args[]) { fileCharCopy fcc = new fileCharCopy (); fcc.copia (args[0], args[1]); } } La clase DataOutputStream permite a una aplicación escribir tipos primitivos (enteros, reales, booleanos) de forma portable. Define métodos como writeInt para escribir un entero, writeFloat escribir un real, writeBoolean para escribir un booleano, etc. Esta clase se utiliza si se desea generar un archivo de datos binarios. Esta clase es general, no está ligada a

116

ningún destino de datos en particular y por eso, para escribir en un archivo, hay que mandarle como argumento en su constructor un objeto de tipo FileOutputStream. El siguiente segmento de código muestra la forma en que se genera un archivo de datos enteros que están almacenados en el arreglo datos. DataOutputStream fd_out = new DataOutputStream ( new FileOutputStream ("archivo.dat")); int datos [] = { ... }; for (int i = 0; i < datos.length; i++) fd_out.writeInt (); El listado 7-6 presenta una aplicación que escribe un archivo de datos con 5 enteros, 3 reales y un booleano en ese orden. Este programa genera el archivo de datos que lee la aplicación que se presentó en el listado 7-3 Listado 7-6 import java.io.*; public class fileBinWriter { public static void main (String args[]) { int a[] = {10, 6, 5, 12, 5}; double b[] = {3.1415926, 2.8494, 10.834}; boolean c = true; DataOutputStream dos = null; try { dos = new DataOutputStream (new FileOutputStream ("datos.dat")); } catch (FileNotFoundException e) { System.out.println ("No pude crear el archivo"); } try { for (int i = 0; i < a.length; i++) dos.writeInt (a[i]); for (int i = 0; i < b.length; i++) dos.writeDouble (b[i]); dos.writeBoolean (c); dos.close (); } catch (IOException e) { System.out.println ("Error al escribir"); } } }

117

7.4 Archivos de datos En muchas aplicaciones se requiere leer archivos de datos que son generados por otras aplicaciones y que vienen en formatos especiales. Por ejemplo, un manejador de bases de datos o una hoja de cálculo permiten exportar datos en formatos de columna fija o separados por coma. Los archivos de columna fija son aquellos donde los datos están alineados de tal forma que cada renglón representa un conjunto de datos afines (lo que se conoce como un registro) y cada dato representa un valor particular y siempre comienzan en la misma columna. La figura 7-1 muestra el contenido de un archivo de datos de este tipo. Cada renglón representa un alumno y las columnas representan su apellido paterno, apellido materno, primer nombre y carrera respectivamente y están alineados para que siempre comiencen en la misma columna. Teorito Homero Kate Clara

Veloz Here Verde Luna

Jaime Elmer Elsa Soyla

IEC LCC ISC LCC

Figura 7-1 Para leer un archivo de columna fija se lee una línea de texto usando el método readLine de la clase BufferedReader y luego se extraen los valores con el método de la clase String substring. Opcionalmente al resultado se le puede aplicar el método trim que elimina los caracteres blancos que haya al principio y al final de un string. El listado 7-7 presenta una aplicación que lee un archivo con los datos que se muestran en la figura 7-1 y los escribe en la pantalla. Listado 7-7 import java.io.*; public class archi1 { public static void main (String args[]) { String in_filename = "alumnos.dat"; BufferedReader fd_in = null; try { fd_in = new BufferedReader (new FileReader (in_filename)); } catch (FileNotFoundException e) { System.out.println ("El archivo " + in_filename + " no existe"); return; }

118

Listado 7-7 Continuación try { String linea; while ((linea = fd_in.readLine ()) != null) { String ap_pat = linea.substring (0, 14).trim (); String ap_mat = linea.substring (14, 24).trim (); String npila = linea.substring (24, 36).trim (); String carrera = linea.substring (36, linea.length ()).trim (); System.out.println (ap_pat + " " + ap_mat + " " + npila + " " + carrera); } fd_in.close (); } catch (IOException e) { System.out.println ("Error de I/O " + e); return; } } } La salida del programa es: Teorito Veloz Jaime IEC Homero Here Elmer LCC Kate Verde Elsa ISC Clara Luna Soyla LCC Los archivos de datos separados por comas son aquellos donde los datos, dentro de los renglones, vienen separados por comas. La figura 7-2 muestra el contenido de un archivo de datos separados por coma. Cada renglón representa los extremos de una línea recta. Las dos primeras columnas son las coordenadas del punto (x1, y1) y las siguientes dos columnas son las coordenadas del punto (x2, y2). 3.345, 73.983, 89, 90.2 43.9, 29.38, 38.0, 82.9 4.6, 6.932, 2.93, 2.98 6.93, 12.93, 0.84, 3.98 Figura 7-2 Para leer un archivo de datos separados por comas se podría proceder leyendo una línea del archivo usando el método readLine y luego buscar las posiciones de las comas y copiar con el método substring la parte que está entre las comas. Afortunadamente la librería de entrada y salida de Java tiene una clase llamada StreamTokenizer que toma un flujo de entrada y lo separa en "tokens" o palabras, permitiendo que los tokens se puedan leer uno a la vez. La clase reconoce palabras, números, espacios en blanco, strings y comentarios.

119

Una aplicación típicamente crea una instancia de esta clase y luego invoca repetidamente al método nextToken hasta que regrese el valor TT_EOF indicando que no hay mas tokens disponibles. El listado 7-8 presenta una aplicación que lee un archivo con los datos que se muestran en la figura 7-2 y escribe la suma de las columnas. Listado 7-8 import java.io.*; public class archi2 { public static void main (String args[]) { String in_filename = "datos.dat"; double sx1 = 0, sx2 = 0, sy1 = 0, sy2 = 0; BufferedReader fd_in = null; try { fd_in = new BufferedReader (new FileReader (in_filename)); } catch (FileNotFoundException e) { System.out.println ("El archivo " + in_filename + " no existe"); return; } try { StreamTokenizer st = new StreamTokenizer (fd_in); while ((st.nextToken ()) != st.TT_EOF) { double x1 = st.nval; sx1 += x1; st.nextToken (); st.nextToken (); double y1 = st.nval; sy1 += y1; st.nextToken (); st.nextToken (); double x2 = st.nval; sx2 += x2; st.nextToken (); st.nextToken (); double y2 = st.nval; sy2 += y2; } fd_in.close (); } catch (IOException e) { System.out.println ("Error de I/O " + e); return; } System.out.println ("Suma de x1: " + sx1 + " Suma de y1: " + sy1); System.out.println ("Suma de x2: " + sx2 + " Suma de y2: " + sy2); } }

120

La salida de este programa es: Suma de x1: 58.775 Suma de y1: 123.225 Suma de x2: 130.77 Suma de y2: 180.06

7.5 Archivos remotos En lugar de abrir un archivo local algunas veces es conveniente poder abrir un archivo que esté almacenado en algún servidor de páginas web. Hacerlo en Java es muy sencillo, como las clases generales de flujos no están ligadas a ningún origen en particular, lo único que se requiere es obtener un origen ligado al archivo en red y utilizarlo como argumento en el constructor de la clase que nos interese. La librería de red de Java (java.net) tiene la clase URL para representar una dirección en Internet y la clase URLConnection para representar una conexión a una dirección dada. Para abrir un archivo remoto lo primero es crear un objeto de tipo URL pasando como argumento al constructor la dirección completa del archivo. URL mi_url = new URL (http://www.foo.com/archivo.txt); Luego hay que crear un objeto del tipo URLConnection invocando al método openConnection sobre el objeto de tipo URL y haciendo la conexión mediante el método connect. URLConnection conn = mi_url.openConnection (); conn.connect (); La clase URLConnection define el método getInputStream que asocia la conexión con un flujo de entrada. Es este método el que se le pasa a una clase lectora general de flujo para crear un flujo asociado al archivo remoto. BufferedReader fd_in = new BufferedReader ( new InputStreamReader ( conn.getInputStream ())); En este momento se puede proceder a leer el archivo por líneas de la misma manera que si fuera local. El listado 7-9 presenta un applet que lee un archivo remoto de un servidor de páginas web. Hay que recordar que los applets tienen la restricción de que no pueden conectarse con ninguna computadora que no sea donde estaban, en otras palabras el applet y el archivo deben estar en la misma computadora. Las aplicaciones no tienen esa restricción. La figura 7-3 muestra la salida del applet.

121

Listado7-9 import java.awt.*; import java.net.*; import java.io.*; import java.applet.*; public class display extends Applet { URL mi_url; TextArea ta = new TextArea ("Leyendo ..."); GridLayout gl = new GridLayout (1, 1); URLConnection conn = null; BufferedReader data = null; String linea; StringBuffer buffer = new StringBuffer (); public void init () { try { mi_url = new URL ("http://148.225.83.24/cursoJava/neruda.txt"); } catch (MalformedURLException e) {} setLayout (gl); add (ta); try { conn = this.mi_url.openConnection (); conn.connect (); ta.setText ("Conexion abierta ..."); data = new BufferedReader (new InputStreamReader (conn.getInputStream ())); ta.setText ("Leyendo datos ..."); while ((linea = data.readLine ()) != null) buffer.append (linea + "\n"); ta.setText (buffer.toString ()); } catch (IOException e) { System.out.println ("Error de I/O: " + e.getMessage ()); } } }

122

Figura 7-3

123

View more...

Comments

Copyright © 2017 DATENPDF Inc.