You are on page 1of 41

Java desde Cero dice que se crea una instancia de la clase, o un

Con ésta comienzo una serie de notas sobre Java, objeto propiamente dicho. Por ejemplo, una clase
especialmente para aquellos que quieren podría ser
comenzar a "autos", y un auto dado es una instancia de la
conocerlo y usarlo. Esto se originó en un interés clase.
que surgió en algunos de los suscriptores del La ventaja de esto es que como no hay programas
mailing list de que actúen modificando al objeto, éste se
desarrolladores de web, y que pongo a disposición mantiene en
también del de webmasters. cierto modo independiente del resto de la
Seguramente muchos de ustedes sabrán mucho aplicación. Si es necesario modificar el objeto (por
más sobre Java que yo, y les agradeceré todo tipo ejemplo, para
de darle más capacidades), esto se puede hacer sin
comentarios o correcciones. tocar el resto de la aplicación… lo que ahorra
La idea es dar una guía ordenada para el estudio mucho tiempo
de este lenguaje, muy poderoso y de gran http://www.cybercursos.net
coherencia, aunque Página 4
todavía adolece de algunas limitaciones que de desarrollo y debugging! En Java, inclusive, ni
seguramente se irán superando con el tiempo. siquiera existen las variables globales! (Aunque
Qué es Java parezca
Java es un lenguaje originalmente desarrollado difícil de aceptar, esto es una gran ventaja desde
por un grupo de ingenieros de Sun, utilizado por el punto de vista del desarrollo).
Netscape En cuanto a la herencia, simplemente significa
posteriormente como base para Javascript. Si bien que se pueden crear nuevas clases que hereden
su uso se destaca en el Web, sirve para crear todo de otras
tipo de preexistentes; esto simplifica la programación,
aplicaciones (locales, intranet o internet). porque las clases hijas incorporan
Java es un lenguaje: automáticamente los
· de objetos métodos de las madres. Por ejemplo, nuestra
· independiente de la plataforma clase "auto" podría heredar de otra más general,
Algunas características notables: "vehículo", y
· robusto simplemente redefinir los métodos para el caso
· gestiona la memoria automáticamente particular de los automóviles… lo que significa
· no permite el uso de técnicas de programación que, con una
inadecuadas buena biblioteca de clases, se puede reutilizar
· multithreading mucho código inclusive sin saber lo que tiene
· cliente-servidor adentro.
· mecanismos de seguridad incorporados Un ejemplo simple
· herramientas de documentación incorporadas Para ir teniendo una idea, vamos a poner un
Lenguaje de Objetos ejemplo de una clase Java:
Por qué puse "de" objetos y no "orientado a" public class Muestra extends Frame {
objetos? Para destacar que, al contrario de otros // atributos de la clase
lenguajes como Button si;
C++, no es un lenguaje modificado para poder Button no;
trabajar con objetos sino que es un lenguaje // métodos de la clase:
creado para public Muestra () {
trabajar con objetos desde cero. De hecho, TODO Label comentario = new Label("Presione un
lo que hay en Java son objetos. botón", Label.CENTER);
Qué es un objeto? si = new Button("Sí");
Bueno, se puede decir que todo puede verse no = new Button("No");
como un objeto. Pero seamos más claros. Un add("North", comentario);
objeto, desde add("East", si);
nuestro punto de vista, puede verse como una add("West", no);
pieza de software que cumple con ciertas }}
características: Esta clase no está muy completa así, pero da una
· encapsulamiento idea… Es una clase heredera de la clase Frame
· herencia (un tipo de
Encapsulamiento significa que el objeto es auto- ventana) que tiene un par de botones y un texto.
contenido, o sea que la misma definición del Contiene dos atributos ("si" y "no"), que son dos
objeto incluye objetos del
tanto los datos que éste usa (atributos) como los tipo Button, y un único método llamado Muestra
procedimientos (métodos) que actúan sobre los (igual que la clase, por lo que es lo que se llama
mismos. un
Cuando se utiliza programación orientada a constructor).
objetos, se definen clases (que definen objetos Independiente de la plataforma
genéricos) y la Esto es casi del todo cierto…
forma en que los objetos interactúan entre ellos, a En realidad, Java podría hacerse correr hasta
través de mensajes. Al crear un objeto de una sobre una Commodore 64! La realidad es que
clase dada, se para utilizarlo en
todo su potencial, requiere un sistema operativo permiten acceder a archivos en forma remota (via
multithreading (como Unix, Windows95, OS/2…). URL por ejemplo).
Cómo es esto? Porque en realidad Java es un Además, con el JDK (Java Development Kit) vienen
lenguaje interpretado… al menos en principio. incorporadas muchas herramientas, entre ellas un
Al compilar un programa Java, lo que se genera es generador automático de documentación que,
un seudocódigo definido por Sun, para una con un poco de atención al poner los comentarios
máquina en las clases,
genérica. Luego, al correr sobre una máquina crea inclusive toda la documentación de las
dada, el software de ejecución Java simplemente mismas en formato HTML!
interpreta las El Java Development Kit
instrucciones, emulando a dicha máquina Todo lo que puedan pedir para desarrollar
genérica. Por supuesto esto no es muy eficiente, aplicaciones en Java está en:
por lo que tanto · http://java.sun.com/aboutJava/index.html
Netscape como Hotjava o Explorer, al ejecutar el En particular, deberían bajarse el JDK y el API
código por primera vez, lo van compilando Documentation de:
(mediante un ·
JIT: Just In Time compiler), de modo que al crear http://java.sun.com/java.sun.com/products/JDK/1.0
por ejemplo la segunda instancia de un objeto el .2/index.html
código ya (También les puede interesar en particular el Tool
esté compilado específicamente para la máquina Documentation y alguno de los otros paquetes de
huésped. la página)
Además, Sun e Intel se han puesto de acuerdo Nota: en este site también hay un tutorial de Java,
para desarrollar procesadores que trabajen aunque es un poco difícil de seguir para el
directamente en principiante.
Java, con lo que planean hacer máquinas muy El JDK (versión 1.0.2) está disponible para
baratas que puedan conectarse a la red y ejecutar SPARC/Solaris, x86/Solaris, MS-Windows 95/NT, y
aplicaciones MacOS.
Java cliente-servidor a muy bajo costo. También está disponible el fuente para el que
El lenguaje de dicha máquina genérica es público, quiera adaptarlo para otro sistema operativo, y he
y si uno quisiera hacer un intérprete Java para leído por ahí
una que hay una versión dando vueltas para Linux y
Commodore sólo tendría que implementarlo y HP-UX.
pedirle a Sun la aprobación (para que verifique Básicamente, el JDK consiste de:
que cumple con · el compilador Java, javac
los requisitos de Java en cuanto a cómo interpreta · el intérprete Java, java
cada instrucción, la seguridad, etc.) · un visualizador de applets, appletviewer
Algunas características… · el debugger Java, jdb (que para trabajar necesita
Entre las características que nombramos nos conectarse al server de Sun)
referimos a la robustez. Justamente por la forma · el generador de documentación, javadoc
en que está También se puede bajar del mismo site un
diseñado, Java no permite el manejo directo del browser que soporta Java (y de hecho está escrito
hardware ni de la memoria (inclusive no permite totalmente en
modificar Java), el Hotjava.
http://www.cybercursos.net Para instalarlo simplemente hay que
Página 5 descompactar el archivo (sugiero que creen un
valores de punteros, por ejemplo); de modo que directorio java para eso),
se puede decir que es virtualmente imposible pero tengan en cuenta NO DESCOMPRIMIR el
colgar un archivo classes.zip!
programa Java. El intérprete siempre tiene el Importante para los usuarios de Windows95:
control. todas estas aplicaciones deben ejecutarse desde
Inclusive el compilador es suficientemente una ventana
inteligente como para no permitir un montón de DOS. En particular, utilizan nombres largos y
cosas que podrían distinguen mayúsculas de minúsculas, así que
traer problemas, como usar variables sin tengan en cuenta
inicializarlas, modificar valores de punteros esto que es fuente de muchos errores.
directamente, acceder a Una cosa muy importante: para que todo ande
métodos o variables en forma incorrecta, utilizar bien aceitado, agreguen:
herencia múltiple, etc. · el directorio de los programas en el path (ej:
Además, Java implementa mecanismos de c:\java\bin)
seguridad que limitan el acceso a recursos de las · las variables de entorno:
máquinas donde · CLASSPATH=.;C:\java\lib\classes.zip
se ejecuta, especialmente en el caso de los · HOMEDRIVE=C:
Applets (que son aplicaciones que se cargan · HOMEPATH=\
desde un servidor y se · HOME=C:\
ejecutan en el cliente). con los valores adecuados a su entorno.
También está diseñado específicamente para Noten que en CLASSPATH agregué el directorio
trabajar sobre una red, de modo que incorpora actual (.), para poder compilar y ejecutar desde
objetos que cualquier
directorio.
Empecemos de una vez! <INPUT TYPE="text" NAME="resultado"
Bueno, suponiendo que hayan instalado todo, y SIZE=15>
antes de comenzar a programar en Java, una <BR>
pequeña </FORM>
aclaración : </BODY>
http://www.cybercursos.net </HTML>
Página 6 Básicamente, el código se encuadra entre los tags
En realidad se puede decir que hay tres Javas por <SCRIPT>…</SCRIPT>, y los parámetros se
ahí: pasan al
· Javascript: es una versión de Java directamente mismo mediante un form (<FORM>…</FORM>).
interpretada, que se incluye como parte de una El lenguaje utilizado es muy parecido al C++, y
página HTML, lo que lo hace muy fácil y cómodo básicamente el código se ejecuta mediante una
para aplicaciones muy pequeñas, pero que en acción de un botón (…
realidad tiene muchas limitaciones: ONCLICK="calcula(this.form)").
· no soporta clases ni herencia Al presionar el botón, se llama a la función
· no se precompila calcula con el parámetro this.form, que se
· no es obligatorio declarar las variables refiere al form al que
· verifica las referencias en tiempo de ejecución pertenece el botón.
· no tiene protección del código, ya que se baja en http://www.cybercursos.net
ascii Página 7
· no todos los browsers lo soportan La función asigna al valor del campo resultado
completamente; Explorer, por ejemplo, no soporta del form que se le pasa como parámetro
las (form.resultado.value) el resultado de evaluar el
últimas adiciones de Netscape, como las valor de la expresión del campo expr de dicho
imágenes animadas. form
· Java standalone: programas Java que se ejecutan (eval(form.expr.value)).
directamente mediante el intérprete java. Hay MUCHOS ejemploes de Javascript en:
· Applets: programas Java que corren bajo el · http://www.c2.net/~andreww/javascript/
entorno de un browser (o del appletviewer) incluyendo decenas de calculadoras, juegos y
En sí los dos últimos son el mismo lenguaje, pero otras yerbas!
cambia un poco la forma en que se implementa el Allí también encontrarán la documentación y un
objeto tutorial sobre Javascript.
principal (la aplicación). Vamos a ver cómo crear Las clases en Java
las aplicaciones para que, sin cambios, se puedan Bueno, antes que nada conviene saber que en
ejecutar Java hay un montón de clases ya definidas y
casi igual en forma standalone o como applet (en utilizables.
realidad hay cosas que los applets no pueden Éstas vienen en las bibliotecas estándar:
hacer, como · java.lang - clases esenciales, números, strings,
acceder a archivos sin autorización). objetos, compilador, runtime, seguridad y threads
Javascript (es
No vamos a detenernos mucho en Javascript, por el único paquete que se incluye automáticamente
las limitaciones antedichas; si les interesa en todo programa Java)
podemos · java.io - clases que manejan entradas y salidas
dedicarnos un poco a este lenguaje en el futuro. · java.util - clases útiles, como estructuras
Por ahora, sólo un ejemplo sencillo: genéricas, manejo de fecha, hora y strings,
Calculadora en Javascript: número
<HTML> aleatorios, etc.
<HEAD> · java.net - clases para soportar redes: URL, TCP,
<SCRIPT LANGUAJE="Javascript"> UDP, IP, etc.
function calcula(form) { · java.awt - clases para manejo de interface
if (confirm("¿Está seguro?")) gráfica, ventanas, etc.
form.resultado.value = · java.awt.image - clases para manejo de
eval(form.expr.value) imágenes
else · java.awt.peer - clases que conectan la interface
alert("Vuelva a intentarlo...") gráfica a implementaciones dependientes de la
} plataforma (motif, windows)
</SCRIPT> · java.applet - clases para la creación de applets y
</HEAD> recursos para reproducción de audio.
<BODY> Para que se den una idea, los números enteros,
<FORM> por ejemplo, son "instancias" de una clase no
Introduzca una expresión: redefinible,
<INPUT TYPE="text" NAME="expr" Integer, que desciende de la clase Number e
SIZE=15> implementa los siguientes atributos y métodos:
<INPUT TYPE="button" NAME="Boton" public final class java.lang.Integer extends
VALUE="Calcular" java.lang.Number {
ONCLICK="calcula(this.form)"> // Atributos
<BR> public final static int MAX_VALUE;
Resultado: public final static int MIN_VALUE;
// Métodos Constructores
public Integer(int value); comentario termina al final de la línea).
public Integer(String s); Veamos un ejemplo:
// Más Métodos // Implementación de un contador sencillo
public double doubleValue(); // GRABAR EN UN ARCHIVO "Contador.java" (OJO
public boolean equals(Object obj); CON LAS MAYUSCULAS!)
public float floatValue(); // COMPILAR CON: "javac Contador.java" (NO
public static Integer getInteger(String nm); OLVIDAR EL .java!)
public static Integer getInteger(String nm, int val); // ESTA CLASE NO ES UNA APLICACION, pero nos
public static Integer getInteger(String nm, Integer va a servir enseguida
val); public class Contador { // Se define la clase
public int hashCode(); Contador
public int intValue(); // Atributos
public long longValue(); int cnt; // Un entero para guardar el valor actual
public static int parseInt(String s); // Constructor // Un método constructor…
public static int parseInt(String s, int radix); public Contador() { // …lleva el mismo nombre
public static String toBinaryString(int i); que la clase
public static String toHexString(int i); cnt = 0; // Simplemente, inicializa (1)
public static String toOctalString(int i); }
public String toString(); // Métodos
public static String toString(int i); http://www.cybercursos.net
http://www.cybercursos.net Página 9
Página 8 public int incCuenta() { // Un método para
public static String toString(int i, int radix); incrementar el contador
public static Integer valueOf(String s); cnt++; // incrementa cnt
public static Integer valueOf(String s, int radix); return cnt; // y de paso devuelve el nuevo valor
} }
Mucho, no? public int getCuenta() { // Este sólo devuelve el
Esto también nos da algunas ideas: valor actual
· la estructura de una clase return cnt; // del contador
· caramba, hay métodos repetidos! }
De la estructura enseguida hablaremos; en }
cuanto a los métodos repetidos (como parseInt Cuando, desde una aplicación u otro objeto, se
por ejemplo), al crea una instancia de la clase Contador,
llamarse al método el compilador decide cuál de mediante la
las implementaciones del mismo usar basándose instrucción:
en la new Contador()
cantidad y tipo de parámetros que le pasamos. el compilador busca un método con el mismo
Por ejemplo, parseInt("134") y parseInt("134",16), nombre de la clase y que se corresponda con la
al llamada en
compilarse, generarán llamados a dos métodos cuanto al tipo y número de parámetros. Dicho
distintos. método se llama Constructor, y una clase puede
Estructura de una clase tener más de
Una clase consiste en: un constructor (no así un objeto o instancia, ya
algunas_palabras class nombre_de_la_clase que una vez que fue creado no puede recrearse
[algo_más] { sobre sí
[lista_de_atributos] mismo).
[lista_de_métodos] En tiempo de ejecución, al encontrar dicha
} instrucción, el intérprete reserva espacio para el
Lo que está entre [ y ] es opcional… objeto/instancia,
Ya veremos qué poner en "algunas_palabras" y crea su estructura y llama al constructor.
"algo_más", por ahora sigamos un poco más. O sea que el efecto de new Contador() es,
La lista de atributos (nuestras viejas variables precisamente, reservar espacio para el contador e
locales) sigue el mismo formato de C: se define inicializarlo en
primero el tipo cero.
y luego el nombre del atributo, y finalmente el ";". En cuanto a los otros métodos, se pueden llamar
public final static int MAX_VALUE desde otros objetos (lo que incluye a las
; aplicaciones) del
También tenemos "algunas_palabras" adelante, mismo modo que se llama una función desde C.
pero en seguida las analizaremos. Por ejemplo, usemos nuestro contador en un
En cuanto a los métodos, también siguen la programa bien sencillo que nos muestre cómo
sintaxis del C; un ejemplo: evoluciona:
public int incContador() { // declaración y // Usemos nuestro contador en una mini-
apertura de { aplicación
cnt++; // instrucciones, separadas por ";" // GRABAR EN UN ARCHIVO "Ejemplo1.java" (OJO
return(cnt); CON LAS MAYUSCULAS!)
} // cierre de } // COMPILAR CON: "javac Ejemplo.java" (NO
Finalmente, se aceptan comentarios entre /* y */, OLVIDAR EL .java!)
como en C, o bien usando // al principio del // EJECUTAR CON: "java Ejemplo1" (SIN el .java)
comentario (el
import java.io.*; // Uso la biblioteca de g.drawString
entradas/salidas (String.valueOf(laCuenta.getCuenta()), 20, 65 );
public class Ejemplo1 { // IMPORTANTE: Nombre g.drawString
de la clase (String.valueOf(laCuenta.incCuenta()), 20, 80 );
// igual al nombre del archivo! }
// entero para asignarle el valor del contador e }
imprimirlo Ahora es necesario crear una página HTML para
// aunque en realidad no me hace falta. poder visualizarlo. Para esto, crear y luego cargar
static int n; el archivo
// y una variable tipo Contador para instanciar el ejemplo2.htm con un browser que soporte Java (o
objeto… bien ejecutar en la ventana DOS: "appletviewer
static Contador laCuenta; ejemplo2.htm"):
// ESTE METODO, MAIN, ES EL QUE HACE QUE <HTML>
ESTO SE COMPORTE <HEAD>
// COMO APLICACION. Es donde arranca el <TITLE>Ejemplo 2 - Applet Contador</TITLE>
programa cuando ejecuto "java Ejemplo1" </HEAD>
// NOTA: main debe ser public & static. <BODY>
public static void main ( String args[] ) { <applet code="Ejemplo2.class" width=170
System.out.println ("Cuenta… "); // Imprimo el height=150>
título </applet>
laCuenta = new Contador(); // Creo una instancia </BODY>
del Contador </HTML>
System.out.println (laCuenta.getCuenta()); // 0 - Para terminar este capítulo, observemos las
Imprimo el valor actual (cero!) diferencias entre la aplicación standalone y el
n = laCuenta.incCuenta(); // 1 - Asignación e applet:
incremento · La aplicación usa un método main, desde donde
System.out.println (n); // Ahora imprimo n arranca
laCuenta.incCuenta(); // 2 - Lo incremento (no uso · El applet, en cambio, se arranca desde un
el valor… constructor (método con el mismo nombre que la
System.out.println (laCuenta.getCuenta()); // …de clase)
retorno) y lo imprimo Además:
System.out.println (laCuenta.incCuenta()); // 3 - · En la aplicación utilizamos System.out.println
Ahora todo en un paso! para imprimir en la salida estándar
} · En el applet necesitamos "dibujar" el texto sobre
} un fondo gráfico, por lo que usamos el método
En el capítulo III vamos a analizar este programa g.drawString dentro del método paint (que es
en detalle. Por ahora veamos la diferencia con un llamado cada vez que es necesario redibujar el
applet que applet)
haga lo mismo: Con poco trabajo se pueden combinar ambos
http://www.cybercursos.net casos en un solo objeto, de modo que la misma
Página 10 clase sirva para
// Applet de acción similar a la aplicación utilizarla de las dos maneras:
Ejemplo1 http://www.cybercursos.net
// GRABAR EN ARCHIVO: "Ejemplo2.java" Página 11
// COMPILAR CON: "javac Ejemplo2.java" // Archivo: Ejemplo3.java
// PARA EJECUTAR: Crear una página HTML como // Compilar con: javac Ejemplo3.java
se indica luego import java.applet.*;
import java.applet.*; import java.awt.*;
import java.awt.*; import java.io.*;
public class Ejemplo2 extends Applet { public class Ejemplo3 extends Applet {
static int n; static int n;
static Contador laCuenta; static Contador laCuenta;
// Constructor… public Ejemplo3 () {
public Ejemplo2 () { laCuenta = new Contador();
laCuenta = new Contador(); }
} public static void main(String args[]) {
// El método paint se ejecuta cada vez que hay laCuenta = new Contador();
que redibujar paint();
// NOTAR EL EFECTO DE ESTO CUANDO SE }
CAMBIA DE TAMAÑO LA public static void paint () {
// VENTANA DEL NAVEGADOR! System.out.println ("Cuenta...");
public void paint (Graphics g) { System.out.println (laCuenta.getCuenta());
g.drawString ("Cuenta...", 20, 20); n = laCuenta.incCuenta();
g.drawString System.out.println (n);
(String.valueOf(laCuenta.getCuenta()), 20, 35 ); laCuenta.incCuenta();
n = laCuenta.incCuenta(); System.out.println (laCuenta.getCuenta());
g.drawString (String.valueOf(n), 20, 50 ); System.out.println (laCuenta.incCuenta());
laCuenta.incCuenta(); }
public void paint (Graphics g) {
g.drawString ("Cuenta...", 20, 20); Página 13
g.drawString [public] [final | abstract] class Clase [extends
(String.valueOf(laCuenta.getCuenta()), 20, 35 ); ClaseMadre] [implements Interfase1 [,
n = laCuenta.incCuenta(); Interfase2 ]…]
g.drawString (String.valueOf(n), 20, 50 ); o bien, para interfaces:
laCuenta.incCuenta(); [public] interface Interfase [extends
g.drawString InterfaseMadre1 [, InterfaseMadre2 ]…]
(String.valueOf(laCuenta.getCuenta()), 20, 65 ); Como se ve, lo único obligatorio es class y el
g.drawString nombre de la clase. Las interfases son un caso de
(String.valueOf(laCuenta.incCuenta()), 20, 80 ); clase
} particular que veremos más adelante.
} Public, final o abstract
Esta clase puede ejecutarse tanto con "java Definir una clase como pública (public) significa
Ejemplo3" en una ventana DOS, como cargarse que puede ser usada por cualquier clase en
desde una página cualquier
HTML con: paquete. Si no lo es, solamente puede ser
<applet code="Ejemplo3.class" width=170 utilizada por clases del mismo paquete (más
height=150> sobre paquetes luego;
</applet> básicamente, se trata de un grupo de clases e
Notar que conviene probar el applet con el interfaces relacionadas, como los paquetes de
appletviewer ("appletviewer ejemplo3.htm"), ya biblioteca
que éste indica en incluídos con Java).
la ventana DOS si hay algún error durante la Una clase final (final) es aquella que no puede
ejecución. Los browsers dejan pasar muchos tener clases que la hereden. Esto se utiliza
errores, básicamente por
simplemente suprimiendo la salida a pantalla del razones de seguridad (para que una clase no
código erróneo. pueda ser reemplazada por otra que la herede), o
Notar que en todo este desarrollo de las clases por diseño de la
Ejemplo1, Ejemplo2 y Ejemplo3, en ningún aplicación.
momento Una clase abstracta (abstract) es una clase que
volvimos a tocar la clase Contador! puede tener herederas, pero no puede ser
http://www.cybercursos.net instanciada. Es,
Página 12 literalmente, abstracta (como la clase Number
Estructura de clases definida en java.lang). ¿Para qué sirve? Para
Vamos a comenzar analizando la clase Contador, modelar
para ir viendo las partes que forman una clase conceptos. Por ejemplo, la clase Number es una
una por una y clase abstracta que representa cualquier tipo de
en detalle. Este capítulo va a ser un poco aburrido números (y
por lo exhaustivo (aunque algunos puntos más sus métodos no están implementados: son
complicados abstractos); las clases descendientes de ésta,
como las excepciones y los threads los dejaremos como Integer o Float,
para después), pero me parece bueno tener un sí implementan los métodos de la madre Number,
resumen y se pueden instanciar.
completo de la sintaxis desde ahora. Por lo dicho, una clase no puede ser final y
Luego iremos armando pequeñas aplicaciones abstract a la vez (ya que la clase abstract
para probar cada cosa. requiere
Recordemos la definición de la clase Contador: descendientes…)
// Implementación de un contador sencillo ¿Un poco complejo? Se va a entender mejor
public class Contador { cuando veamos casos particulares, como las
// Atributos interfases (que por
int cnt; definición son abstractas ya que no implementan
// Constructor sus métodos).
public Contador() { Extends
cnt = 0; La instrucción extends indica de qué clase
} desciende la nuestra. Si se omite, Java asume que
// Métodos desciende de la
public int incCuenta() { superclase Object.
cnt++; Cuando una clase desciende de otra, esto
return cnt; significa que hereda sus atributos y sus métodos
} (es decir que, a
public int getCuenta() { menos que los redefinamos, sus métodos son los
return cnt; mismos que los de la clase madre y pueden
}} utilizarse en
Declaración de la clase forma transparente, a menos que sean privados
La clase se declara mediante la línea public class en la clase madre o, para subclases de otros
Contador. En el caso más general, la declaración paquetes,
de una protegidos o propios del paquete). Veremos la
clase puede contener los siguientes elementos: calificación de métodos muy pronto, a no
http://www.cybercursos.net desesperar!
Implements Finalmente, los atributos miembros de la clase
Una interfase (interface) es una clase que pueden ser atributos de clase o atributos de
declara sus métodos pero no los implementa; instancia; se dice
cuando una clase que son atributos de clase si se usa la palabra
implementa (implements) una o más interfases, clave static: en ese caso la variable es única para
debe contener la implementación de todos los todas las
métodos (con instancias (objetos) de la clase (ocupa un único
las mismas listas de parámetros) de dichas lugar en memoria). Si no se usa static, el sistema
interfases. crea un lugar
Esto sirve para dar un ascendiente común a nuevo para esa variable con cada instancia (o sea
varias clases, obligándolas a implementar los que es independiente para cada objeto).
mismos métodos y, La declaración sigue siempre el mismo esquema:
por lo tanto, a comportarse de forma similar en [private |protected|public] [static] [final]
cuanto a su interfase con otras clases y [transient] [volatile] Tipo NombreVariable [=
subclases. Valor];
Interface Private, protected o public
Una interfase (interface), como se dijo, es una Java tiene 4 tipos de acceso diferente a las
clase que no implementa sus métodos sino que variables o métodos de una clase: privado,
deja a cargo la protegido, público o por
implementación a otras clases. Las interfases paquete (si no se especifica nada).
pueden, asimismo, descender de otras interfases De acuerdo a la forma en que se especifica un
pero no de otras atributo, objetos de otras clases tienen distintas
clases. posibilidades de
Todos sus métodos son por definición abstractos y accederlos:
sus atributos son finales (aunque esto no se Acceso desde: private protected public
indica en el (package)
cuerpo de la interfase). la propia clase S S S S
Son útiles para generar relaciones entre clases subclase en el mismo
que de otro modo no están relacionadas paquete N S S S
(haciendo que otras clases en el
implementen los mismos métodos), o para mismo paquete
distribuir paquetes de clases indicando la NSSS
estructura de la interfase subclases en otros
pero no las clases individuales (objetos paquetes N X S N
anónimos). otras clases en otros
Si bien diferentes clases pueden implementar las paquetes N N S N
mismas interfases, y a la vez descender de otras S: puede acceder
clases, esto N: no puede acceder
no es en realidad herencia múltiple ya que una X: puede acceder al atributo en objetos que
clase no puede heredar atributos ni métodos de pertenezcan a la subclase, pero no en los que
una interface; y pertenecen a la clase
las clases que implementan una interfase pueden madre. Es un caso especial ; más adelante
no estar ni siquiera relacionadas entre sí. veremos ejemplos de todo esto.
http://www.cybercursos.net Static y final
Página 14 Como ya se vio, static sirve para definir un
El cuerpo de la clase atributo como de clase, o sea único para todos los
El cuerpo de la clase, encerrado entre { y }, es la objetos de la
lista de atributos (variables) y métodos clase.
(funciones) que En cuanto a final, como en las clases, determina
constituyen la clase. que un atributo no pueda ser sobreescrito o
No es obligatorio, pero en general se listan redefinido. O sea:
primero los atributos y luego los métodos. no se trata de una variable, sino de una
Declaración de atributos constante.
En Java no hay variables globales; todas las Transient y volatile
variables se declaran dentro del cuerpo de la Son casos bastante particulares y que no habían
clase o dentro de un sido implementados en Java 1.0.
método. Las variables declaradas dentro de un Transient denomina atributos que no se graban
método son locales al método; las variables cuando se archiva un objeto, o sea que no forman
declaradas en el parte del
cuerpo de la clase se dice que son miembros de la estado permanente del mismo.
clase y son accesibles por todos los métodos de la http://www.cybercursos.net
clase. Página 15
Por otra parte, además de los atributos de la Volatile se utiliza con variables modificadas
propia clase se puede acceder a todos los asincrónicamente por objetos en diferentes
atributos de la clase de threads (literalmente
la que desciende; por ejemplo, cualquier clase "hilos", tareas que se ejecutan en paralelo);
que descienda de la clase Polygon hereda los básicamente esto implica que distintas tareas
atributos npoints, pueden intentar
xpoints e ypoints.
modificar la variable simultáneamente, y volatile Algo más respecto a los arreglos: ya que Java
asegura que se vuelva a leer la variable (por si gestiona el manejo de memoria para los mismos,
fue y lanza
modificada) cada vez que se la va a usar (esto es, excepciones si se intenta violar el espacio
en lugar de usar registros de almacenamiento asignado a una variable, se evitan problemas
como buffer). típicos de C como
Los tipos de Java acceder a lugares de memoria prohibidos o fuera
Los tipos de variables disponibles son del lugar definido para la variable (como cuando
básicamente 3: se usa un
· tipos básicos (no son objetos) subíndice más grande que lo previsto para un
· arreglos (arrays) arreglo…).
· clases e interfases Y los métodos…
Con lo que vemos que cada vez que creamos una Los métodos, como las clases, tienen una
clase o interfase estamos definiendo un nuevo declaración y un cuerpo.
tipo. La declaración es del tipo:
Los tipos básicos son: [private |protected|public] [static] [abstract]
Tipo Tamaño/Formato Descripción [final] [native ] [synchronized] TipoDevuelto
byte 8-bit complemento a 2 Entero de un byte NombreMétodo
short 16-bit complemento a 2 Entero corto ( [tipo1 nombre1[, tipo2 nombre2 ]…] ) [throws
int 32-bit complemento a 2 Entero excepción1 [,excepción2]… ]
long 64-bit complemento a 2 Entero largo A no preocuparse: poco a poco aclararemos todo
float 32-bit IEEE 754 Punto flotante, precisión con ejemplos.
simple http://www.cybercursos.net
double 64-bit IEEE 754 Punto flotante, precisión Página 16
doble Básicamente, los métodos son como las funciones
char 16-bit caracter Unicode Un caracter de C: implementan, a través de funciones,
boolean true, false Valor booleano (verdadero o operaciones y
falso) estructuras de control, el cálculo de algún
Los arrays son arreglos de cualquier tipo (básico parámetro que es el que devuelven al objeto que
o no). Por ejemplo, existe una clase Integer; un los llama. Sólo
arreglo de pueden devolver un valor (del tipo TipoDevuelto),
objetos de dicha clase se notaría: aunque pueden no devolver ninguno (en ese caso
Integer vector[ ]; TipoDevuelto es void). Como ya veremos, el valor
Los arreglos siempre son dinámicos, por lo que no de retorno se especifica con la instrucción return,
es válido poner algo como: dentro
Integer cadena[5]; del método.
Aunque sí es válido inicializar un arreglo, como Los métodos pueden utilizar valores que les pasa
en: el objeto que los llama (parámetros), indicados
int días[ ] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, con tipo1
31, 30, 31 }; nombre1, tipo2 nombre2… en el esquema de la
char letras[ ] = { 'E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', declaración.
'O', 'N', 'D' }; Estos parámetros pueden ser de cualquiera de los
String nombres[ ] = new String[12]; tipos ya vistos. Si son tipos básicos, el método
Nota al margen: no confundir un String (cadena recibe el
de caracteres) con un arreglo de caracteres! Son valor del parámetro; si son arrays, clases o
cosas bien interfases, recibe un puntero a los datos
distintas! (referencia). Veamos un
Ya hablaremos más adelante de las clases String y pequeño ejemplo:
StringBuffer. public int AumentarCuenta(int cantidad) {
En Java, para todas las variables de tipo básico se cnt = cnt + cantidad;
accede al valor asignado a la misma directamente return cnt;
(no se }
conoce la dirección de memoria que ocupa). Para Este método, si lo agregamos a la clase
las demás (arrays, clases o interfases), se accede Contador, le suma cantidad al acumulador cnt.
a través de En detalle:
un puntero a la variable. El valor del puntero no · el método recibe un valor entero (cantidad)
es accesible ni se puede modificar (como en C); · lo suma a la variable de instancia cnt
Java no · devuelve la suma (return cnt)
necesita esto y además eso atentaría contra la ¿Cómo hago si quiero devolver más de un valor?
robustez del lenguaje. Por ejemplo, supongamos que queremos hacer un
De hecho, en Java no existen los tipos pointer, método
struct o union. Un objeto es más que una dentro de una clase que devuelva la posición del
estructura, y las mouse.
uniones no se hacen necesarias con un método Lo siguiente no sirve:
de programación adecuado (y además se evita la void GetMousePos(int x, int y) {
posibilidad de x = ….; // esto no sirve!
acceder a los datos incorrectamente). y = ….; // esto tampoco!
}
porque el método no puede modificar los · Declaración de variables locales
parámetros x e y (que han sido pasados por valor, · Asignaciones a variables
o sea que el http://www.cybercursos.net
método recibe el valor numérico pero no sabe Página 18
adónde están las variables en memoria). · Operaciones matemáticas
La solución es utilizar, en lugar de tipos básicos, · Llamados a otros métodos:
una clase: · dentro de la clase
class MousePos { public int x, y; } · de instancia, de otras clases
y luego utilizar esa clase en nuestro método: · de clase, de cualquier clase
void GetMousePos( MousePos m ) { · Estructuras de control
m.x = ……; · Excepciones (try, catch, que veremos más
m.y = ……; adelante)
} Declaración de variables locales
El resto de la declaración Las variables locales se declaran igual que los
Public, private y protected actúan atributos de la clase:
exactamente igual para los métodos que para los Tipo NombreVariable [= Valor];
atributos, así que veamos Ej: int suma;
el resto. float precio;
Los métodos estáticos (static), son, como los Contador laCuenta;
atributos, métodos de clase; si el método no es Sólo que aquí no se declaran private, public, etc.,
static es un sino que las variables definidas dentro del método
método de instancia. El significado es el mismo sólo son
que para los atributos: un método static es accesibles por él.
compartido por Las variables pueden inicializarse al crearse:
todas las instancias de la clase. Ej: int suma = 0;
Ya hemos hablado de las clases abstractas; los float precio = 12.3;
métodos abstractos (abstract) son aquellos de Contador laCuenta = new Contador ( );
los que se da la Asignaciones a variables
declaración pero no la implementación (o sea que Se asigna un valor a una variable mediante el
consiste sólo del encabezamiento). Cualquier signo =:
clase que Variable = Constante | Expresión ;
contenga al menos un método abstracto (o cuya Ej: suma = suma + 1;
clase madre contenga al menos un método precio = 1.05 * precio;
abstracto que no laCuenta.cnt = 0;
esté implementado en la hija) es una clase El último caso es válido si cnt es una variable
abstracta. pública de la clase Contador. Personalmente no
Es final un método que no puede ser redefinido creo
por ningún descendiente de la clase. conveniente acceder directamente a variables de
http://www.cybercursos.net otro objeto, ya que futuras modificaciones del
Página 17 objeto llamado
Las clases native son aquellas que se o del que llama puede propender la difusión de
implementan en otro lenguaje (por ejemplo C o errores… Es mejor usar métodos como getCuenta
C++) propio de la o un
máquina. Sun aconseja utilizarlas bajo riesgo hipotético inicializarContador para ello. De hecho,
propio, ya que en realidad son ajenas al lenguaje. algunos sugieren que todas las variables de una
Pero la clase se
posibilidad de usar viejas bibliotecas que uno declaren como private .
armó y no tiene ganas de reescribir existe!. En el primer caso, o sea en general:
Las clases synchronized permiten sincronizar Variable = Variable Operador Expresión;
varios threads para el caso en que dos o más se puede escribir en forma más sencilla:
accedan Variable Operador= Expresión;
concurrentemente a los mismos datos. De nuevo, Por ejemplo, suma = suma + 9 - cantidad;
más detalles habrá en el futuro, cuando hablemos puede escribirse: suma += 9-cantidad;
de threads. y precio = precio * 0.97;
Finalmente, la cláusula throws sirve para indicar como: precio *= 0.97;
que la clase genera determinadas excepciones. Operaciones matemáticas
El cuerpo de los métodos Hay varios tipos de operadores:
Otra vez recordaremos nuestra vieja clase Unarios: + - ++ -- ~ ! (tipo) …..etc.
Contador: Se colocan antes (o en algunos casos después) de
// Implementación de un contador sencillo la constante o expresión.
public class Contador { Por ejemplo: -cnt; // cambia de signo; por ejemplo
……………….. si cnt es 12 el resultado es -12; cnt no cambia.
public int incCuenta() { ++cnt; // equivale a cnt += 1;
cnt++; cnt++; // equivale a cnt +=1; veremos la
return cnt; diferencia al hablar de estructuras de control
} --cnt; // equivale a cnt -= 1;
………………… cnt--; // equivale a cnt -= 1;
} Binarios: + - * / % …..etc.
Dentro de los métodos pueden incluirse:
Van entre dos constantes o expresiones o // constructor:
combinación de ambas. public Complejo(float rx, float iy) {
http://www.cybercursos.net x = rx;
Página 19 y = iy;
Por ejemplo: cnt + 2; // debuelve la suma de }
ambos. // métodos:
promedio + ( valor / 2); // como se ve, se pueden http://www.cybercursos.net
usar paréntesis. Página 20
horas / hombres; // división. public float Norma() {
acumulado % 3; // resto de la división entera return (float)Math.sqrt(x*x+y*y);
entre ambos. }
Nota: + sirve también para concatenar cadenas // obligatorios (son abstractos en Number):
de caracteres; hablaremos de String y public double doubleValue() {
StringBuffer pronto. return (double)Norma( );
Cuando se mezclan Strings y valores numéricos, }
éstos se convierten automáticamente a cadenas: public float floatValue() {
"La frase tiene " + cant + " letras" return Norma();
se convierte en: "La frase tiene 17 letras" // }
suponiendo que cant = 17 public int intValue() {
Precedencia de operadores en Java return (int)Norma();
La siguiente es la precedencia de los operadores }
en expresiones compuestas. De todos modos, public long longValue() {
como en todos return (long)Norma();
los lenguajes, se recomienda usar paréntesis en }
caso de duda. public String toString() {
Posfijos [] . (params) expr++ expr-- return "("+x+")+i("+y+")";
Operadores unarios ++expr --expr +expr -expr }
~! }
Creación y "cast" new (type) Pueden probar la clase (mínima) con el siguiente
Multiplicativos * / % ejemplo de aplicación; la línea en negrita es un
Aditivos + - ejemplo de
Desplazamiento << >> >>> un llamado a un método de un objeto de otra
Relacionales < > <= >= instanceof clase. Notar que es este caso, es necesario llamar
Igualdad == != al método
AND bit a bit & sobre un objeto (instancia) existente, por lo que
OR exclusivo bit a bit ^ se indica:
OR inclusivo bit a bit | Nombre_del_Objeto<punto>Nombre_del_Método(
AND lógico && parámetros)
OR lógico || // Archivo: Ejemplo4.java
Condicional ? : // Compilar con: javac Ejemplo4.java
Asignación = += -= *= /= %= ^= &= |= <<= // Ejecutar con: java Ejemplo4
>>= >>>= import java.io.*;
Algunos ejemplos: public class Ejemplo4 {
[] define arreglos: int lista[]; public static void main(String args[]) {
(params) es la lista de parámetros cuando se Complejo numComp = new Complejo(4,-3);
llama a un método: convertir(valor, base); System.out.println(numComp.toString());
new permite crear una instancia de un objeto: System.out.println(numComp.Norma());
new Contador(); }}
(type) cambia el tipo de una expresión a otro: En la clase Complejo tenemos también un
(float)(total % 10); ejemplo de un llamado a un método de clase, o
>> desplaza bit a bit un valor binario: base >> 3; sea static:
<= devuelve "true" si un valor es menor o igual return (float)Math.sqrt(x*x+y*y);
que otro: total <= maximo; Como el método es de clase, no hace falta
instanceof devuelve "true" si el objeto es una llamarlo para un objeto en particular. En ese caso,
instancia de la clase: papa instanceof Comida; en lugar del
|| devuelve "true" si cualquiera de las expresiones nombre de un objeto existente se puede utilizar
es verdad: (a<5) || (a>20) directamente el nombre de la clase:
Llamadas a métodos Nombre_de_la_Clase<punto>Nombre_del_Método
Se llama a un método de la misma clase (parámetros)
simplemente con el nombre del método y los http://www.cybercursos.net
parámetros entre Página 21
paréntesis, como se ve, entre otros, en el ejemplo Las estructuras de control
en negrita: Las estructuras de control en Java son
// Archivo: Complejo.java básicamente las misma que en C, con excepción
// Compilar con: javac Complejo.java del goto, que no existe
public final class Complejo extends Number { (al fin un lenguaje serio! )
// atributos: if…[else]
private float x;
private float y;
La más común de todas, permite ejecutar una System.out.println(linea);
instrucción (o secuencia de instrucciones) si se da }
una condición Do…while
dada (o, mediante la cláusula else, ejecutar otra Similar al anterior, sólo que la condición se evalúa
secuencia en caso contrario). al final del ciclo y no al principio:
if (expresión_booleana) instrucción_si_true; do {
[else instrucción_si_false;] instrucciones…
http://www.cybercursos.net } while (expresión_booleana);
Página 22 Por ejemplo:
o bien: do {
if (expresión_booleana) { linea = archivo.LeerLinea();
instrucciones_si_true; if (linea != null) System.out.println(linea);
} } while (linea != null);
else { For
instrucciones_si_false; También para ejecutar en forma repetida una
} serie de instrucciones; es un poco más complejo:
Por ejemplo: for ( instrucciones_iniciales; condición_booleana;
public final String toString() { instruccion_repetitiva_x ) {
if (y<0) instrucciones…
return x+"-i"+(-y); }
else Si bien las instrucciones pueden ser cualquiera (el
return +x+"+i"+y; bucle se repite mientras la condición sea
} verdadera), lo
Switch…case…brake…default usual es utilizarlo para "contar" la cantidad de
Permite ejecutar una serie de operaciones para el veces que se repiten las instrucciones; se podría
caso de que una variable tenga un valor entero indicar así:
dado. La for ( contador = valor_inicial; contador <
ejecución saltea todos los case hasta que valor_final; contador++ ) {
encuentra uno con el valor de la variable, y instrucciones…
ejecuta desde allí hasta el }
final del case o hasta que encuentre un break , en Por ejemplo:
cuyo caso salta al final del case. El default for ( i=0; i<10; i++ ) {
permite poner System.out.println( i );
una serie de instrucciones que se ejecutan en }
caso de que la igualdad no se de para ninguno de o, para contar hacia atrás:
los case. for ( i=10; I>0; I-- ) {
switch (expresión_entera) { System.out.println( i );
case (valor1): instrucciones_1; }
[break;] Break y continue
case (valor2): instrucciones_2; Estas instrucciones permiten saltar al final de una
[break;] ejecución repetitiva (break) o al principio de la
….. misma
case (valorN): instrucciones_N; (continue).
[break;] Por ejemplo, en:
default: instrucciones_por_defecto; import java.io.*;
} class Bucles {
Por ejemplo: http://www.cybercursos.net
switch (mes) { Página 24
case (2): if (bisiesto()) dias=29; public static void main (String argv[ ]) {
else dias=31; int i=0;
break; for (i=1; i<5; i++) {
case (4): System.out.println("antes "+i);
case (6): if (i==2) continue;
case (9): if (i==3) break;
case (11): dias = 30; System.out.println("después "+i);
break; }
default: dias = 31; }
} }
While La salida es:
Permite ejecutar un grupo de instrucciones antes 1
mientras se cumpla una condición dada: después 1
while (expresión_booleana) { antes 2
instrucciones… antes 3
http://www.cybercursos.net Por qué? "i" comienza en 1 (imprime "antes" y
Página 23 "después"); cuando pasa a 2, el continue salta al
} principio del
Por ejemplo: bucle (no imprime el "después"). Finalmente,
while ( linea != null) { cuando "i" vale 3, el break da por terminado el
linea = archivo.LeerLinea(); bucle for.
Otras... Página 26
Hay otras instrucciones que controlan el flujo del else
programa: return x+"+i"+y;
· synchronized (para ver junto con los threads) }
· catch, // Operaciones matemáticas
· throw, public static final Complejo Suma(Complejo c1,
· try, Complejo c2) {
· finally (para ver con las excepciones) return new Complejo(c1.x+c2.x,c1.y+c2.y);
Ahora sí, podemos usar todo nuestro }
conocimiento sobre Java para ir creando algunas public static final Complejo Resta(Complejo c1,
aplicaciones y de paso Complejo c2) {
ir viendo las bibliotecas estándar... return new Complejo(c1.x-c2.x,c1.y-c2.y);
Hagamos algo... }
Bueno, vamos a hacer una pequeña aplicación public static final Complejo Producto(Complejo c1,
para practicar un poco. Complejo c2) {
Para empezar, vamos a desarrollar un poquito una return new Complejo(c1.x*c2.x-
clase para trabajar con números complejos. c1.y*c2.y,c1.x*c2.y+c1.y*c2.x);
http://www.cybercursos.net }
Página 25 // Nos va a venir bien para aprender
La clase Complejo excepciones...
// grabar como Complejo.java // como división por cero!
// compilar con "javac Complejo.java" public static final Complejo DivEscalar(Complejo
public final class Complejo extends Number { c, float f) {
// atributos: return new Complejo(c.x/ f,c.y/f);
private float x; }
private float y; public static final Complejo Cociente(Complejo c1,
// constructores: Complejo c2) {
public Complejo() { float x = c1.x*c2.x+c1.y*c2.y;
x = 0; float y = -c1.x*c2.y+c1.y*c2.x;
y = 0; float n = c2.x*c2.x+c2.y*c2.y;
} Complejo r = new Complejo(x,y);
public Complejo(float rx, float iy) { return DivEscalar(r,n);
x = rx; }
y = iy; }
} Podemos hacer algunos comentarios...
// métodos: Primero: no hay include aquí, ya que la única
// Norma biblioteca que usamos es java.lang y se incluye
public final float Norma() { automáticamente.
return (float)Math.sqrt(x*x+y*y); Segundo: la clase es public final, lo que implica
} que cualquier clase en éste u otros paquetes
public final float Norma(Complejo c) { puede utilizarla,
return (float)Math.sqrt(c.x*c.x+c.y*c.y); pero ninguna clase puede heredarla (o sea que es
} una clase estéril...).
// Conjugado Hagamos un resumen de los atributos y métodos
public final Complejo Conjugado() { de la clase:
Complejo r = new Complejo(x,-y); // atributos:
return r; private float x;
} private float y;
public final Complejo Conjugado(Complejo c) { Siendo privados, no podemos acceder a ellos
Complejo r = new Complejo(c.x,-c.y); desde el exterior. Como además la clase es final,
return r; no hay forma
} de acceder a x e y. Además, al no ser static, cada
// obligatorios (son abstractos en Number): instancia de la clase tendrá su propio x e y.
public final double doubleValue() { // constructores:
return (double)Norma(); public Complejo()
} public Complejo(float rx, float iy)
public final float floatValue() { La clase tiene dos constructores, que se
return Norma(); diferencian por su "firma" (signature), o sea por la
} cantidad y tipo de
public final int intValue() { parámetros. El primero nos sirve para crear un
return (int)Norma(); objeto de tipo Complejo y valor indefinido
} (aunque en
public final long longValue() { realidad el método lo inicializa en cero); con el
return (long)Norma(); segundo, podemos definir el valor al crearlo.
} // métodos:
public final String toString() { public final float Norma()
if (y<0) public final float Norma(Complejo c)
return x+"-i"+(-y); public final Complejo Conjugado()
http://www.cybercursos.net public final Complejo Conjugado(Complejo c)
Estos métodos también son duales; cuando los System.out.println(c1+"\tNorma="+c1.Norma());
usamos sin parámetros devuelven la norma o el Complejo c2 = new Complejo(-2,5);
conjugado del System.out.println(c2+"\tNorma="+c2.Norma()+"
objeto individual (instancia): \n");
http://www.cybercursos.net System.out.println("("+c1+")/4
Página 27 :"+Complejo.DivEscalar(c1,4));
v = miComplejo.Norma(); // por ejemplo System.out.println("Suma :
otroComplejo = miComplejo.Conjugado(); "+Complejo.Suma(c1,c2));
Con parámetros, en cambio, devuelven la norma http://www.cybercursos.net
o el conjugado del parámetro: Página 28
v = unComplejo.Norma(miComplejo); System.out.println("Resta :
otroComplejo = "+Complejo.Resta(c1,c2).toString());
unComplejo.Conjugado(miComplejo); System.out.println("Multip:
Notar que lo siguiente es inválido: "+Complejo.Producto(c1,c2).toString());
otroComplejo = Complejo.Norma(miComplejo); // System.out.println("Divis :
NO SE PUEDE! "+Complejo.Cociente(c1,c2).toString());
...porque el método no es static, por lo tanto }
debe llamarse para una instancia en particular }
(en este caso, Hay varias cosas para notar: por ejemplo, que
unComplejo). podemos declarar las variables a la vez que las
// obligatorios (son abstractos en Number): creamos:
public final double doubleValue() Complejo c1 = new Complejo(4,-3);
public final float floatValue() c1 y c2 son dos objetos (instancias) de la clase
public final int intValue() Complejo.
public final long longValue() Notar también que no hace falta poner para
Estos métodos es obligatorio definirlos, ya que en imprimir:
la clase madre Number son métodos abstractos, o System.out.println(c1.toString().......);
sea que ya que println automáticamente usa el método
debemos implementarlos aquí. toString() de la clase para imprimir. Basta con
Como todos los métodos de esta clase son final, o poner c1, como
sea que no puede ser redefinido. No es en el programa, aunque c1.toString() también es
importante en válido.
realidad puesto que la clase no puede tener También se ve el uso de los métodos static,
descendientes... accediéndolos directamente por la clase, en:
public final String toString() System.out.println("Suma :
Este método nos sirve para representar el "+Complejo.Suma(c1,c2));
complejo como una cadena de caracteres, de la Y tampoco aquí usamos toString(), aunque no
forma x+iy. está mal si se usa
// Operaciones matemáticas Complejo.Suma(c1,c2).toString().
public static final Complejo Suma(Complejo c1, Algo sobre los métodos
Complejo c2) Analicemos un poco ahora cómo implementamos
public static final Complejo Resta(Complejo c1, los métodos de la clase Complejo.
Complejo c2) public final int intValue() {
public static final Complejo Producto(Complejo c1, return (int)Norma();
Complejo c2) }
public static final Complejo DivEscalar(Complejo Ya que no podemos convertir así nomás un
c, float f) complejo en un entero, para implementar estos
public static final Complejo Cociente(Complejo c1, métodos hemos
Complejo c2) elegido usar como valor de retorno la norma del
Aquí definimos varias operaciones matemáticas. complejo. En este caso, y dado que el método
Notar que se han definido como static, o sea que Norma()
los devuelve un float, usamos typecasting, es decir,
métodos son únicos independientemente de las lo convertimos en entero precediéndolo con (int).
instancias. Esto permite que los podamos ejecutar public final String toString() {
sobre una if (y<0)
instancia o directamente sobre la clase: return x+"-i"+(-y);
miComplejo = unComplejo.Suma(comp1,comp2); else
// vale return x+"+i"+y;
miComplejo = Complejo.Suma(comp1,comp2); // }
TAMBIEN VALE! Aquí representamos el complejo en forma de
Por ejemplo, la siguiente aplicación nos muestra cadena de caracteres. Hemos usado el if para
cómo podemos usar algunos de estos métodos: representar
// Archivo: Ejemplo5.java adecuadamente el signo de la parte imaginaria.
// Compilar con: javac Ejemplo5.java Noten también la asombrosa ayuda que nos
// Ejecutar con: java Ejemplo5 brinda Java, al
import java.io.*; convertir automáticamente las variables x e y a
public class Ejemplo5 { String para la concatenación (mediante el signo
public static void main(String args[]) { "+")!
Complejo c1 = new Complejo(4,-3);
public static final Complejo Cociente(Complejo c1, public static PrintStream err;
Complejo c2) { public static InputStream in;
float x = c1.x*c2.x+c1.y*c2.y; public static PrintStream out;
float y = -c1.x*c2.y+c1.y*c2.x; // Methods
float n = c2.x*c2.x+c2.y*c2.y; .............
Complejo r = new Complejo(x,y); }
return DivEscalar(r,n); Veremos otras bibliotecas (para entrada/salida,
} gráficos, etc) muy pronto.
Aquí tengan en cuenta que las variables x e y, http://www.cybercursos.net
definidas como float, no tienen nada que ver con Página 30
las variables Java a través de la ventana
(atributos) de la clase que están definidas al Para hacer algo un poco más divertido, vamos a
principio de la misma, sino que son variables empezar a trabajar con la biblioteca java.awt, que
locales al método. es la que
Podemos usar return DivEscalar(r,n), ya que contiene todo un grupo de objetos para trabajar
DivEscalar es un método propio de la clase; no con ventanas y sus contenidos: botones, listas,
hace falta etc.
poner Complejo.DivEscalar. Nuestra primera ventana
Qué pasa con r, el new Complejo(x,y) que En Java, la clase Window (descendiente de
creamos? Nada; cuando un objeto no se usa más, Container), en la biblioteca java.awt, permite
el "recogedor de implementar
basura" de Java lo elimina automáticamente ventanas "peladas", es decir, sin bordes ni menús.
(tarde o temprano) de la memoria. Son la base para cualquier tipo de ventanas
public final float Norma(Complejo c) { (normales, popup,
http://www.cybercursos.net diálogos, etc.). El otro descendiente de
Página 29 Container, Panel, es más sencillo aún y sirve
return (float)Math.sqrt(c.x*c.x+c.y*c.y); como espacio para
} que una aplicación incorpore dentro suyo otros
Aquí estamos usando otra clase, Math, que nos elementos (incluyendo otros paneles).
permite realizar varias operaciones matemáticas. La interface Java dirige tanto a uno como a otro
Esta clase todos los eventos de teclado, mouse y foco que
dispone de las constantes E y PI, y los métodos: los afecten
abs(x) valor absoluto (en seguida veremos cómo usar estos eventos).
acos(x) arco coseno De la clase Window descienden Dialog (para
asin(x) arco seno implementar diálogos) y Frame, que es una
atan(x) arco tangente ventana algo más
atan2(x,y) componente angular de la completa: ya tiene borde y menú, así como los
representación polar de x,y botones de cerrar, maximizar, etc.
ceil(x) menor entero mayor que x El siguiente ejemplo crea una ventana que no
cos(x) coseno hace nada pero contiene varios elementos; se
exp(x) ex puede usar
floor(x) mayor entero menor que x directamente (desde la ventana DOS o Unix con
IEEEremainder(x,y) resto de la división x/y según java Ejemplo7) o como applet dentro de una
el estándar IEEE 754 página HTML.
log(x) logaritmo natural Si bien los elementos no disparan ninguna acción,
max(x,y) el mayor de x e y se pueden utilizar con toda su funcionalidad (por
min(x,y) el menor de x e y ejemplo,
pow(x,y) xy editar el texto dentro de los cuadros de texto o
random() número aleatorio entre 0 y 1 presionar el botón).
rint(x) entero más cercano a x (devuelve un // grabar como "Ejemplo7.java"
doble) // compilar con "javac Ejemplo7.java"
round(x) entero más cercano a x (devuelve un import java.awt.*;
entero o un long) public class Ejemplo7 extends Frame {
sin(x) seno boolean inAnApplet = true;
sqrt(x) raíz cuadrada public static void main(String args[]) {
tan(x) tangente Ejemplo7 window = new Ejemplo7();
Algunos de estos métodos disparan excepciones, window.inAnApplet = false;
como sqrt o log de números negativos. Más window.setTitle("Ejemplo");
adelante window.pack();
veremos cómo se usan las excepciones. window.show();
Otra clase que hemos estado usando mucho es la }
PrintStream, a la que pertenece el método println. public Ejemplo7() {
En Panel panelAlto = new Panel();
System.out.println(...) panelAlto.add("West", new Label("Cartel",
out es un atributo de la clase System, del tipo Label.CENTER));
(clase) PrintStream: panelAlto.add("East", new TextArea("Area de
public final class System extends Object texto", 5, 20));
{ add("North", panelAlto);
// Fields Panel panelBajo = new Panel();
panelBajo.add(new TextField("Campo de Texto")); Para verlo más claro, se crea el panel (o espacio
panelBajo.add(new Button("Botón")); para contener objetos) con:
add("South",panelBajo); Panel panelAlto = new Panel();
} Se agregan componentes al panel con el método
public boolean handleEvent(Event ev) { add:
if (ev.id == Event.WINDOW_DESTROY) { http://www.cybercursos.net
if (inAnApplet) { Página 32
http://www.cybercursos.net panelAlto.add("West", new Label("Cartel",
Página 31 Label.CENTER));
dispose(); panelAlto.add("East", new TextArea("Area de
} else { texto", 5, 20));
System.exit(0); Se agregan el panel dentro de nuestro objeto con:
} add("North", panelAlto);
} que equivale a:
return super.handleEvent(ev); this.add("North", panelAlto);
} lo que se puede ver (aunque es inválido porque la
} clase no es static) como:
Un poco de detalle Ejemplo7.add("North", panelAlto);
La clase desciende de Frame (o sea que será una Como nuestra clase Ejemplo7 desciende de
ventana con borde, aunque no le vamos a poner Frame, ésta de Window, y ésta de Container, el
menú). método add lo
Vamos a usar el flag inAnApplet para saber si se está heredando de... su bisabuela! Por otra parte,
arrancó como applet o como aplicación Panel es hija de Container, y usa el mismo método
standalone (hay que para
cerrarla en manera diferente en cada caso) agregar sus componentes. Interesante, no?
public class Ejemplo7 extends Frame { Veamos la estructura:
boolean inAnApplet = true; Object --- Component --- Container --+-- Panel
Si se llama como aplicación standalone, lo |
primero que se ejecuta es main(...); en este caso +-- Window --- Frame --- Ejemplo7
la aplicación crea Noten que hemos usado dos métodos add con
una instancia de Ejemplo7 (ejecutando el diferente signature:
constructor Ejemplo7() a través de new), define panelAlto.add("West", new Label("Cartel",
que no es un Label.CENTER));
applet, y llama a tres métodos de la "abuela" ..........
window: panelBajo.add(new Button("Botón"));
· setTitle que define cuál va a ser el título que El método add(Component) agrega un
aparece en la ventana componente al final; el método
· pack que dimensiona los elementos que add(String,Component) lo agrega
componen la ventana a su tamaño preferido en un lugar especificado por una palabra que
· show que muestra la ventana depende del LayoutManager, el objeto que se
public static void main(String args[]) { encarga de
Ejemplo7 window = new Ejemplo7(); ordenar los componentes dentro del contenedor.
window.inAnApplet = false; LayoutManager es una interface, y como tal debe
window.setTitle("Ejemplo"); implementarse a través de objetos no abstractos
window.pack(); de los que
window.show(); hay varios predefinidos en la librería java.awt:
} BorderLayout, CardLayout, FlowLayout,
Ojo! No confundir el objeto (instancia) window con GridBagLayout
la clase Window! y GridLayout.
Si se carga como applet, entonces se ejecuta el El Layout por defecto es BorderLayout, que define
constructor Ejemplo7() como en el caso anterior: en el contenedor las áreas "North", "South",
public Ejemplo7() { "West",
Panel panelAlto = new Panel(); "East" y " Center" y es que usamos aquí.
panelAlto.add("West", new Label("Cartel", CardLayout permite "apilar" los componentes
Label.CENTER)); como cartas y ver
panelAlto.add("East", new TextArea("Area de uno por vez, FlowLayout los ordena de izquierda a
texto", 5, 20)); derecha como un texto, GridLayout los ordena en
add("North", panelAlto); una
Panel panelBajo = new Panel(); cuadrícula donde cada componente tiene un
panelBajo.add(new TextField("Campo de Texto")); tamaño fijo y GridBagLayout los pone en una
panelBajo.add(new Button("Botón")); cuadrícula pero
add("South",panelBajo); cada uno puede tener el tamaño deseado.
} Noten que no hace falta llamar, en el caso del
Este constructor define dos paneles que forman el applet, a Pack() y Show().
contenido de la ventana (panelAlto y panelBajo), Y los eventos...
los llena Ahora vamos a ver un método que viene de la
con un par de componentes y los pone dentro de clase tatarabuela! Hace falta decir que me gusta
la ventana (recordar que Ejemplo7 es una esto de los
ventana!). objetos?
Vamos a redefinir handleEvent(Event), que es el Es muy común, al redefinir un método, tener en
método que analiza los eventos dirigidos al cuenta llamar antes o después al método de la
componente y clase
toma las acciones adecuadas. antecesora para inicializar o terminar alguna
La clase Event define básicamente una serie de tarea.
métodos que permiten saber si hay alguna tecla http://www.cybercursos.net
de control Página 34
presionada y muchas constantes que indican si se Una ventana con vida
presionó o movió el mouse, si se presionó alguna Antes que nada, vamos a crear una página HTML
tecla en para cargar nuestra clase Ejemplo8, que será un
particular, si se cambió el tamaño de la ventana, applet
etc. En particular hay algunos atributos (aunque también la podremos ejecutar en forma
interesantes: standalone con "java Ejemplo8"), por ejemplo:
· id que indica el tipo de evento <!-- Archivo Ejemplo8.htm - HTML de ejemplo -->
· target que indica sobre qué componente se <HTML>
produjo el evento <HEAD>
· key qué tecla se presionó si fue un evento de <TITLE>Ejemplo 8 - Ventana de datos</TITLE>
teclado </HEAD>
etc. <BODY>
http://www.cybercursos.net Aqu&iacute; se tiene que abrir una ventana de
Página 33 entrada de datos
En los descendientes de Component, el método <applet code="Ejemplo8.class" width=170
handleEvent se llama automáticamente cada vez height=150>
que se </applet>
produce un evento sobre el componente. En este </BODY>
caso, simplemente vamos a mirar si el evento </HTML>
(sobre nuestro Nuestro applet será muy sencillo, ya que utilizará
objeto de clase Ejemplo7) fue "cerrar la ventana", clases que iremos definiendo en este capítulo; por
que se identifica mediante event.id = empezar
WINDOW_DESTROY (una constante estática de la sólo creará una ventana que definiremos en la
clase Event, y como tal la podemos usar con el clase Ventana8:
nombre // Archivo: Ejemplo8.java
de la clase como Event.WINDOW_DESTROY): // Compilar con "javac Ejemplo8.java"
public boolean handleEvent(Event ev) { import java.awt.*;
if (ev.id == Event.WINDOW_DESTROY) { import java.applet.*;
if (inAnApplet) { public class Ejemplo8 extends Applet {
dispose(); public static void main (String arg[]) { // para
} else { poder llamarla con "java Ejemplo8"
System.exit(0); new Ventana8("Ejemplo Standalone", true);
} }
} public void init() { // se ejecuta al abrirse un
return super.handleEvent(event); applet
} new Ventana8("Ejemplo Applet", false);
En ese caso, si nuestro ejemplo se disparó como }
aplicación llamamos al método System.exit(0), }
que cierra la Con los parámetros que le pasamos a la clase
aplicación; y si era un applet llamamos a Ventana8 le indicamos el título de la ventana y si
dispose(), implementación de un método de la se carga
interface como applet o no (ya que el método de cierre
ComponentPeer que se encarga de remover todos varía).
los componentes y la propia ventana. Viajando con Java
Noten que cualquier otro tipo de evento deja Ahora vamos a trabajar con nuestra clase
seguir hasta return super.handleEvent(event), que Ventana8, una ventana que nos permita
llama al seleccionar una fecha y dos
método handleEvent de la clase madre: así como ciudades (desde y hasta) que simula una ventana
el prefijo this. se refiere a un método de la propia de compra de pasajes de, por ejemplo, una
clase, el terminal de
prefijo super. llama al método de la clase madre ómnibus.
(aunque esté redefinido). En este caso, la llamada El ejemplo está basado en uno del libro
se "Programación Java" de Macary y Nicolas, aunque
remonta hasta Component.handleEvent, que algo mejorado y
determina el tipo de evento y llama a uno de los ampliado.
métodos En nuestra ventana podremos entrar una fecha a
action, gotFocus, lostFocus, keyDown, keyUp, mano o directamente mediante los botones Hoy y
mouseEnter, mouseExit, mouseMove, mouseDrag, Mañana,
mouseDown o mouseUp según sea apropiado (y elegiremos la ciudad de salida y la de llegada de
devuelve true). Si ningún método es aplicable, dos listas, y presionaremos luego un botón que
devuelve nos mostrará
false.
los servicios disponibles, nos permitirá comprar Esto sería como llamar a super.Frame(titulo), o
los pasajes, etc. bien Frame(titulo), ya que el método constructor
A medida que entramos los datos, en el botón se tiene el
irá mostrando el detalle de lo que se fue mismo nombre de la clase. Luego, con:
seleccionando. this.enApplet = enApplet; // guardamos esto
http://www.cybercursos.net asignamos a nuestra variable enApplet de la clase
Página 35 el valor del parámetro que se pasó al constructor,
Nuestra ventana quedará más o menos así: que se
Empecemos por armar la estructura de la clase llama igual. El prefijo this, que se refiere a la
Ventana8: instancia particular de la clase, permite
import java.awt.*; diferenciar uno de otro
class Ventana8 extends Frame { // hija de Frame (esto es válido tanto para variables como para
// aquí agregaremos luego métodos).
// algunas variables para guardar datos ok = new Button("Viaje: de ? a ? el ?/?/?");
// (ciudades de salida y llegada, fecha) add("South",ok);
button ok; // también el botón de compra de Aquí hemos creado un botón ubicado al pie de la
pasajes ventana (por ahora lo único que pusimos), y luego
boolean enApplet; // y otra para indicar si es un dimensionamos la ventana y la mostramos:
applet o no pack(); // dimensionamos la ventana
Ventana8 (String titulo, boolean enApplet) { // un show(); // y la mostramos!
constructor Preparando listas
super(titulo); // llama al de Frame Ahora vamos a empezar a crear otros objetos
this.enApplet = enApplet; // guardamos esto para ir completando nuestra aplicación.
// aquí crearemos los botones, listas, etc Comencemos con las
// con sus valores iniciales listas de ciudades.
// y los pondremos en la ventana. Para eso, vamos a crear un objeto descendiente
// por ejemplo: de Panel que simplemente contenga una lista de
ok = new Button("Viaje: de ? a ? el ?/?/?"); ciudades
add("South",ok); predefinidas y un título que diga "Seleccione
pack(); // dimensionamos la ventana ciudad de", y a continuación "salida" o "llegada".
show(); // y la mostramos! También agregaremos un método
} import java.awt.*;
public boolean handleEvent(Event e) { // para class SelecPueblo extends Panel {
manejar los eventos private List listaPueblos;
if (e.id == Event.WINDOW_DESTROY) { // cerrar la SelecPueblo (String salidaOllegada) {
ventana setLayout (new BorderLayout (20,20));
if (enApplet) dispose(); // armamos el título, que va a ser un Label:
else System.exit(0); StringBuffer titulo = new StringBuffer();
} titulo.append("Seleccione ciudad de ");
// aquí miraremos si se presionó un botón titulo.append(salidaOllegada);
// o se eligió algo de una lista titulo.append(": ");
// y actuaremos en consecuencia add("North", new Label(titulo.toString()));
return super.handleEvent(e); // los demás eventos // armamos la lista de ciudades, que va a ser un
los maneja Frame List:
} listaPueblos = new List (4, false);
void ActualizaBoton() { http://www.cybercursos.net
// aquí pondremos un método que servirá Página 37
// para actualizar el botón de compra de pasajes, listaPueblos.addItem("Buenos Aires");
// ya que el texto del mismo se actualiza cada listaPueblos.addItem("La Plata");
// vez que se selecciona una ciudad o se cambia la listaPueblos.addItem("Azul");
fecha listaPueblos.addItem("Rosario");
} listaPueblos.addItem("Cordoba");
void Activar() { listaPueblos.addItem("Bahía Blanca");
// y aquí un método para cuando se presione add("South", listaPueblos);
// dicho botón, que se supone que va a consultar }
// una base de datos y abrir una ventana public String getDescription() {
http://www.cybercursos.net return listaPueblos.getSelectedItem();
Página 36 }
// para vendernos el pasaje }
}} No hay mucho para analizar aquí, creo. La
Nuestro programa ya funciona! Aunque un variable listaPueblos es privada, pero puede
poquito incompleto, claro... consultarse cuál es la
Igual vamos a analizarlo un poco el constructor, ciudad seleccionada mediante getDescription
que es lo más interesante aquí. (que es public). Este método llama al método
Primero llamamos al constructor de la clase getSelectedItem de la lista, que devuelve el texto
madre, que se encarga de crear la ventana: seleccionado.
Ventana8 (String titulo, boolean enApplet) { // un En el constructor, armamos el texto del título
constructor como un StringBuffer. Los objetos StringBuffer
super(titulo); // llama al de Frame son similares a
los de clase String pero pueden ser modificados. add (diasiguiente);
En cambio los objetos String, una vez creados, no }
pueden private String GetHoy() {
ser modificados directamente: sus métodos Date d = new Date();
(concat, toLowerCase, etc.) simplemente crean un int dia = d.getDate();
nuevo String int mes = d.getMonth();
con el nuevo valor. int ano = d.getYear();
Esto lo hicimos para introducir esta nueva clase; return dia+"/"+mes+"/"+ano;
por supuesto hubiera sido más fácil poner, como }
pueden private String GetManana() {
comprobar, con el mismo resultado: Date d = new Date();
String tit = "Seleccione ciudad de int dia = d.getDate();
"+salidaOllegada+": "; int mes = d.getMonth();
add("North", new Label(tit)); int ano = d.getYear();
Por otra parte, creamos el objeto listaPueblos dia = dia++;
como new List(4, false), que indica que la lista va switch (mes) {
a tener 4 case (1):
renglones y sólo se puede seleccionar un ítem por case (3):
vez. Agregamos luego 6 ítems mediante addItem case (5):
y la case (7):
agregamos al panel. case (8):
Ahora ya podemos agregar las listas a nuestra case (10): if (dia>31) {
ventana y poner un par de variables para dia = 1;
guardarlas: mes++;
class Ventana8 extends Frame { // hija de Frame }
SelecPueblo cs; // ciudad de salida break;
SelecPueblo cl; // ciudad de llegada case (12): if (dia>31) {
button ok; // también el botón de compra de dia = 1;
pasajes mes = 1;
boolean enApplet; // y otra para indicar si es un ano++;
applet o no }
Ventana8 (String titulo, boolean enApplet) { // un http://www.cybercursos.net
constructor Página 39
super(titulo); // llama al de Frame break;
this.enApplet = enApplet; // guardamos esto case (4):
cs = new SelecPueblo("SALIDA"); // CIUDAD case (6):
DE SALIDA case (9):
add ("Center", cs); case (11): if (dia>30) {
cl = new SelecPueblo("LLEGADA"); // CIUDAD dia = 1;
DE LLEGADA mes++;
add ("East", cl); }
ok = new Button("Viaje: de ? a ? el ?/?/?"); break;
add("South",ok); default: if (dia>28) { // ojo, hay que corregir para
pack(); // dimensionamos la ventana bisiestos!
show(); // y la mostramos! dia = 1;
} mes++;
........................... }
Ya pueden ir probando cómo queda, aunque por }
ahora mucha funcionalidad no tenemos... return dia+"/"+mes+"/"+ano;
http://www.cybercursos.net }
Página 38 public String getDescription() {
Agregando fechas return elDia.getText();
Otro panel más nos servirá para seleccionar o }
entrar la fecha: public boolean handleEvent (Event e) {
import java.util.*; if (e.target == hoy)
import java.awt.*; elDia.setText(GetHoy());
class DiaPartida extends Panel { if (e.target == diasiguiente)
private TextField elDia; elDia.setText(GetManana());
private Button hoy; return super.handleEvent(e);
private Button diasiguiente; }
DiaPartida() { }
setLayout (new GridLayout (4,1)); Este es un poco más largo pero no más complejo.
elDia = new TextField(); Vamos por parte:
elDia.setText(GetHoy()); DiaPartida() {
hoy = new Button ("Hoy"); setLayout (new GridLayout (4,1));
diasiguiente = new Button ("Mañana"); elDia = new TextField();
add (new Label ("Día salida: ")); elDia.setText(GetHoy());
add (elDia); hoy = new Button ("Hoy");
add (hoy); diasiguiente = new Button ("Mañana");
add (new Label ("Día salida: ")); campo de texto por ejemplo.
add (elDia); Juntando todo hasta aquí
add (hoy); Bueno, ahora vamos a reunir las piezas que
add (diasiguiente); tenemos hasta ahora agregando estos métodos a
} nuestra clase
El constructor crea un panel con cuatro campos Ventana8 para ver cómo queda la ventana
en forma de grilla vertical, donde mostrará el completa:
texto "Día class Ventana8 extends Frame { // hija de Frame
salida: ", el campo de entrada de texto elDia y los SelecPueblo cs; // ciudad de salida
botones hoy y diasiguiente. SelecPueblo cl; // ciudad de llegada
El método privado getHoy usa los métodos DiaPartida dp; // día de salida
getDate, getMonth y getYear de la clase date para button ok; // botón de compra de pasajes
armar un boolean enApplet; // para indicar si es un applet o
String con la fecha actual. El método privado no
getManana hace lo mismo para leer la fecha Ventana8 (String titulo, boolean enApplet) { // un
actual, y le suma constructor
1 al día para tener el día siguiente. El switch super(titulo); // llama al de Frame
siguiente verifica que si pasó de fin de mes tome this.enApplet = enApplet; // guardamos esto
el primer día y dp = new DiaPartida(); // DIA DE SALIDA
el mes siguiente (o el primer día del año siguiente add ("West", dp);
si es en diciembre). Notar que no se consideraron cs = new SelecPueblo("SALIDA"); // CIUDAD DE
los años SALIDA
bisiestos en febrero para no complicar el método, add ("Center", cs);
pero no es difícil de corregir. cl = new SelecPueblo("LLEGADA"); // CIUDAD DE
http://www.cybercursos.net LLEGADA
Página 40 add ("East", cl);
Otra manera sería armar un array con los días de ok = new Button("Viaje: de ? a ? el ?/?/?");
cada mes, corregir los días de febrero para los add("South",ok);
años pack(); // dimensionamos la ventana
bisiestos, y comparar contra este array en lugar show(); // y la mostramos!
de usar un switch. La idea siempre es la misma: }
devolver un http://www.cybercursos.net
String con la fecha del día siguiente. Página 41
Notar algo interesante: como estas clases se Completando la ventana
cargan y ejecutan en la máquina cliente, la fecha Vamos a empezar por completar nuestro método
que aparece es ActualizaBoton, que modificará el texto del botón
la del cliente y no la del servidor (que puede ser ok a
diferente depende la hora y el lugar del mundo en medida que seleccionemos las ciudades y la
que estén fecha:
ambas máquinas). void ActualizaBoton() {
El método getDescription es público y se usa para StringBuffer b = new StringBuffer("Viaje: de ");
acceder a la fecha que se ha ingresado desde las if (cs.getDescription() != null)
demás b.append(cs.getDescription());
clases; simplemente devuelve el contenido del else b.append("?");
campo elDia, de clase TextField. b.append(" a ");
Aquí hemos desarrollado también el método if (cl.getDescription() != null)
handleEvent: b.append(cl.getDescription());
public boolean handleEvent (Event e) { else b.append("?");
if (e.target == hoy) b.append(" el ");
elDia.setText(GetHoy()); if (dp.getDescription() != null)
if (e.target == diasiguiente) b.append(dp.getDescription());
elDia.setText(GetManana()); else b.append("?/?/?");
return super.handleEvent(e); ok.setLabel(b.toString());
} }
En caso de alguna acción sobre uno de los Nuestro método comienza por crear un
botones, el método setText (de la clase TextField) StringBuffer con las palabras "Viaje: de ", y va
pone en el agregando el resto:
campo de texto elDia el valor del día actual o el · la ciudad de partida, llamando al método
siguiente. getDescription de cs (ciudad de salida)
Notar que sólo hemos considerado que haya · el texto constante " a "
algún evento y no un tipo de evento en particular; · la ciudad de llegada, llamando al método
en realidad el getDescription de cl (ciudad de llegada)
método va a actuar por ejemplo tanto al presionar · el texto constante " el "
el mouse sobre el botón como al soltarlo. Pero · la fecha seleccionada, llamando al método
esto no nos getDescription de dp (día de partida)
molesta. Si en cualquier caso recibe un string nulo, pone
super.handleEvent se encarga de otros eventos un signo de pregunta (o ?/?/? para la fecha).
dirigidos al panel, como la entrada de datos por El método setLabel, sobre el objeto ok de tipo
teclado al Label, modifica la "etiqueta" del botón.
Realmente nos devuelven null los métodos que Una forma de filtrar sólo los eventos que nos
llamamos si no hay selección hecha? interesan sería usar, por ejemplo:
Veamos: if ((e.target=cs.listaPueblos) &&
class SelecPueblo extends Panel { (e.id==Event.LIST_SELECT)) ActualizaBoton();
private List listaPueblos; que está dirigida a la lista y no al panel en
............................ general, y tiene en cuenta el tipo de evento.
public String getDescription() { Lamentablemente, listaPueblos es privada dentro
return listaPueblos.getSelectedItem(); de la clase SelecPueblo y por lo tanto dentro de
}} cs. Pero
El método getSelectedItem de la clase List es mejor así, porque declararla pública y leerla
devuelve null si no hay ítems seleccionados, así desde afuera sería bastante sucio (así como la
que acá leemos
andamos bien. En cuanto a la clase DiaPartida, de podríamos escribirla).
entrada inicializa el valor del texto en la fecha Hay varias formas de mejorar esto sin cometer la
actual, así torpeza de declarar pública a listaPueblos. Una
que aquí no se daría nunca este caso... Aunque al posibilidad
crear el objeto Ventana8 estamos poniendo un es verificar, usando cs.getDescription(), si el texto
texto fijo en cambió (y sólo en ese caso modificar el texto del
el botón, y no el que devuelve el objeto dp. botón).
Sería mejor, para ser más consistente, modificar Otra, es hacer que los objetos de la clase
el constructor de Ventana8 para que arme el texto SelecPueblo pasen a sus padres cualquier evento
mediante sobre ellos, o
el método ActualizaBotón: mejor solamente la selección de un elemento de
Ventana8 (String titulo, boolean enApplet) { la lista; para eso basta agregar a la clase
........................................ SelecPueblo:
ok = new Button("cualquiera"); public boolean handleEvent(Event e) {
ActualizaBoton(); if ((e.target==listaPueblos) &&
add("South",ok); (e.id==Event.LIST_SELECT)) {
pack(); e.target=this;
show(); }
} return super.handleEvent(e);
http://www.cybercursos.net }
Página 42 En resumen: si el evento en el panel es una
Esto ya se ve mejor! Y de paso probamos el selección de la lista (tanto con mouse como
método... moviendo la selección
Un poquito de actividad con las flechas), cambio el target del evento para
Ahora sí, pasemos a completar nuestro manejador que indique el panel (y no la lista); si no, lo paso a
de eventos: la clase
public boolean handleEvent(Event e) { antecesora.
if (e.id == Event.WINDOW_DESTROY) { Lo mismo podemos hacer con handleEvent para la
if (enApplet) dispose(); clase DiaPartida:
else System.exit(0); public boolean handleEvent (Event e) {
} if (e.target == hoy) {
if ( (e.target==dp)||(e.target==cs)|| elDia.setText(GetHoy());
(e.target==cl) ) http://www.cybercursos.net
ActualizaBoton(); Página 43
if (e.target==ok) e.target=this;
Activar(); }
} if (e.target == diasiguiente) {
return super.handleEvent(e); elDia.setText(GetManana());
} e.target=this;
Simplemente, si detectamos un evento sobre }
alguno de nuestros paneles actualizamos el texto if (e.target == elDia) {
del botón; y si e.target=this;
se presiona dicho botón llamamos al método }
Activar que se supone que va a tomar los datos return super.handleEvent(e);
de la base de }
datos, indicarnos servicios disponibles, etc. Esto no anda como esperaríamos! El campo de
Algo importante a notar es que el simple hecho de texto no se comporta muy bien...
mover el mouse sobre uno de los paneles ya Esto es porque el código dependiente de la
llama a plataforma procesa los eventos de mouse antes
ActualizaBoton (se nota porque titila el texto, de llamar a
sobre todo en una máquina lenta). Además, si handleEvent , pero procesa los de teclado
hacen click sobre después de llamar a handleEvent.
el botón Hoy o Mañana sin mover el mouse, el Lo que significa que, en el caso del campo de
texto del botón ok no se actualiza ya que el texto, handleEvent (y por lo tanto ActualizaBotón)
evento va dirigido se llama
al botón presionado y no al panel. antes de modificar el texto!
Para corregir esto, deberíamos procesar nosotros Y para terminar...
las teclas presionadas (lo que podríamos Bueno, sólo nos queda por definir el método
aprovechar para Activar(). Primero vamos a llamar a
verificar que se presiona una tecla válida). ActualizaBoton() por si
Cuidado! En futuras versiones de Java podría alguien lo último que hizo fue entrar un texto sin
implementarse el mismo comportamiento para el presionar Enter, y dejo para otro día más
mouse, y por tranquilo consultar
lo tanto tendríamos que repensar la estrategia. un archivo o base de datos con lo que vamos a
Para colmo, sólo los eventos que la plataforma mostrar al usuario de nuestro programa.
envía llegan a Java; por ejemplo, Motif no envía Por ahora simplemente vamos a mostrar una
eventos de ventana con la selección y un lindo botón de OK.
movimiento de mouse dentro de un campo de Primero vamos a hacer una muy pequeña
texto... lo que significa que nunca podríamos modificación a ActualizaBoton() para que nos
capturar ese tipo devuelva el valor
de eventos. Sólo el componente Canvas pasa del texto del botón (para no calcularlo de nuevo):
todos los eventos. String ActualizaBoton() {
Para simplificar, sólo actualizaremos el texto del StringBuffer b = new StringBuffer("Viaje: de ");
botón cuando se presiona Enter (Event.key=10): ..............................................
if ((e.target == ok.setLabel(b.toString());
elDia)&&(e.id==Event.KEY_PRESS)) { }
if (e.key==10) e.target=this; Y ahora vamos a definir nuestro método, teniendo
} en cuenta que nuestro botón sólo actuará si se
Ahora debemos modificar el método handleEvent han entrado
en nuestra clase Ventana8 para que soporte todos todos los datos:
estos void Activar() {
eventos: if ( (cs.getDescription() != null) &&
public boolean handleEvent(Event e) { (cl.getDescription() != null) )
if (e.id == Event.WINDOW_DESTROY) { // también podríamos verificar que la fecha sea
if (enApplet) dispose(); válida aquí
else System.exit(0); Result8 resultado = new
} Result8("Resultado",ActualizaBoton());
if else ok.setLabel("Especificación incompleta!");
( ((e.target==dp)&&((e.id==Event.ACTION_EVEN }
T)||(e.id==Event.KEY_PRESS))) Sólo nos falta definir una sencilla clase Result8
||((e.target==cs)&&(e.id==Event.LIST_SELECT)) para nuestra ventanita resultado:
||((e.target==cl)&&(e.id==Event.LIST_SELECT)) ) // archivo Result8.java, compilar con javac
ActualizaBoton(); Result8.java
if (e.target==ok) import java.awt.*;
Activar(); class Result8 extends Frame {
return super.handleEvent(e); Button r_ok;
} Result8 (String titulo, String texto) { // constructor
Obviamente, procesar todas las teclas nosotros super(titulo);
sería bastante más complicado... de todos modos, Label r_lbl = new Label(texto);
el método en r_ok = new Button("Ok");
DiaPartida sería más o menos así: add("Center", r_lbl);
if ((e.target == add("South", r_ok);
elDia)&&(e.id==Event.KEY_PRESS)) { pack();
// 1- leer el contenido del campo con: show();
elDia.getText() }
// 2- modificarlo de acuerdo a la tecla presionada: public boolean handleEvent(Event e) {
e.key if ((e.id == Event.WINDOW_DESTROY)||
// 3- poner el resultado en el campo con: (e.target==r_ok))
elDia.setText(texto) http://www.cybercursos.net
http://www.cybercursos.net Página 45
Página 44 dispose(); // cierra esta ventana pero no la
// 4- modificar el objeto del evento al panel con: aplicación
e.target=this; return super.handleEvent(e);
// 5- enviar el evento al objeto padre (no la clase }
padre), }
// en este caso Ventana8, mediante: Noten que usé dispose y no System.exit! Esto
getParent().deliverEvent(e) permite cerrar sólo la ventana de resultado, y
// 6- evitar proceso posterior del evento mediante: seguir usando la
result(true) aplicación hasta que se nos ocurra cerrarla
} mediante meta-F4, alt-F4, el menú de sistema de
Me ahorro explicar estos dos últimos pasos; se la ventana, la cruz
complica bastante todo porque hay que manejar de Windows 95 o lo que le resulte a su sistema
la posición del operativo.
cursor dentro del campo de texto, etcétera. Con lo Finale con tutto
que hicimos es bastante... creo!
Espero que se haya entendido! Esta aplicación at
costó bastante pero en el camino hemos tenido java.io.FileInputStream.<init>(FileInputStream.jav
oportunidad de a:51)
aprender unas cuantas cosas... Si logran juntar at Ejemplo9.main(Ejemplo9.java:9)
todo el código y generar las varias clases que (Caramba! ¿Dónde vi ese FileNotFoudException
definimos, todo antes?)
tiene que andar sobre rieles e Justamente, cuando el archivo al que quiero
independientemente de la plataforma. acceder no existe, Java "lanza" una excepción.
Si no... avísenme, y subo también los fuentes o Esto es, un aviso
las clases. de que algo falló y, si no se toma ninguna acción,
Por las dudas, pueden probar esta aplicación detiene el programa.
como applet cargando: La clase FileInputStream puede "lanzar" (throws)
http://www.amarillas.com/rock/java/Ejemplo8.htm la excepción FileNotFoundException.
http://www.cybercursos.net http://www.cybercursos.net
Página 46 Página 47
Un paréntesis de Entrada/Salida ¿Cómo capturar y tratar las excepciones? En
En Java hay muchas clases para leer y escribir seguida; primero terminemos con nuestro
archivos (u otros dispositivos de E/S). Están programa.
reunidos en la f = new DataInputStream(fptr);
biblioteca java.io. La clase DataInputStream nos permite leer, en
Vamos a empezar como siempre con un pequeño forma independiente del hardware, tipos de datos
ejemplo funcional y en seguida nos meteremos en de una
el "corriente" (stream) que, en este caso, es un
necesario camino de las excepciones... archivo. Es descendiente de FilterInputStream e
Primera Lectura implementa
// archivo: Ejemplo9.java - compilar con "javac DataInput, una interface.
Ejemplo9.java", etc. etc. Al crear un objeto de tipo DataInputStream lo
import java.io.*; referimos al archivo, que le pasamos como
public class Ejemplo9 { parámetro (fptr);
public static void main(String args[]) throws esta clase tiene toda una serie de métodos para
FileNotFoundException,IOException { leer datos en distintos formatos.
FileInputStream fptr; En nuestro programa usamos uno para leer líneas,
DataInputStream f; que devuelve null cuando se llega al final del
String linea = null; archivo o un
fptr = new FileInputStream("Ejemplo9.java"); String con el contenido de la línea:
f = new DataInputStream(fptr); do {
do { linea = f.readLine();
linea = f.readLine(); System.out.println(linea);
if (linea!=null) System.out.println(linea); } while (linea != null);
} while (linea != null); En seguida de leer la línea la imprimimos, y
fptr.close(); repetimos esto mientras no nos devuelva null.
}} Al final, cerramos el archivo:
(Caramba! ¿Qué hace ese throws ahí?) fptr.close();
El programa de ejemplo simplemente lee un Tanto readLine como close pueden lanzar la
archivo de texto y lo muestra en pantalla, algo así excepción IOException, en caso de error de
como el type lectura o cierre de
del DOS o el cat de Unix. archivo.
Dejemos por ahora el throws En realidad, podríamos no haber usado un
FileNotFoundException,IOException y vamos al DataInputStream y trabajar en forma más directa:
código. import java.io.*;
fptr = new FileInputStream("Ejemplo9.java"); public class Ejemplo10 {
La clase FileInputStream (descendiente de public static void main(String args[]) throws
InputStream) nos sirve para referirnos a archivos FileNotFoundException,IOException {
o conexiones FileInputStream fptr;
(sockets) de una máquina. Podemos accederlos int n;
pasando un String como aquí, un objeto de tipo fptr = new FileInputStream("Ejemplo9.java");
File o uno de do {
tipo FileDescriptor, pero en esencia es lo mismo. n = fptr.read();
Al crear un objeto de este tipo estamos "abriendo" if (n!=-1) System.out.print((char)n);
un } while (n!=-1);
archivo, clásicamente hablando. fptr.close();
Si el archivo no existe (por ejemplo reemplacen }
"Ejemplo9.java" por alguna otra cosa, como }
"noexiste.txt"), Ya que la clase FileInputStream también dispone
al ejecutarlo nos aparece un error: de métodos para leer el archivo. Sólo que son
C:\java\curso>java Ejemplo9 unos pocos
java.io.FileNotFoundException: noexiste.txt métodos que nos permiten leer un entero por vez
o un arreglo de bytes. DataInputStream tiene
métodos para
leer los datos de muchas formas distintas, y en ejecuta lo que ésta engloba. Si no encuentra un
general resulta más cómodo. catch para esa excepción, para el programa y
Capturando excepciones muestra el error
Ahora sí, vamos a ver cómo nos las arreglamos que se produjo.
con las excepciones para que no se nos pare el Por ejemplo, para evitar este último error bastaría
programa con con agregar:
un mensaje tan poco estético... catch (ArrayIndexOutOfBoundsException e) {
En lugar de lanzar las excepciones al intérprete, System.out.println("Debe ingresar un nombre de
vamos a procesarlas nosotros mediante la archivo!");
cláusula catch: System.out.println("Ej.: java Ejemplo11 pepe.txt");
// Archivo: Ejemplo11.java }
// Compilar con: javac Ejemplo11.java http://www.cybercursos.net
http://www.cybercursos.net Página 49
Página 48 Hay que notar que cuando se lanza una excepción
// Ejecutar con: java Ejemplo11 el programa igual se detiene, porque el código
<nombre_archivo> que sigue al
import java.io.*; lanzamiento de la excepción no se ejecuta.
public class Ejemplo11 { Veremos luego cómo se comporta esto en un
public static void main(String args[]) { objeto que fue creado
FileInputStream fptr; por otro, y cómo usar la instrucción finally para
DataInputStream f; poner una parte de código que se ejecute pase lo
String linea = null; que pase.
try { Los applets y los archivos
fptr = new FileInputStream(args[0]); Veamos cómo se comporta esta aplicación si la
f = new DataInputStream(fptr); modificamos para usarla como applet.
do { /*
linea = f.readLine(); // ----- Archivo: Ejemplo12.java
if (linea!=null) System.out.println(linea); */
} while (linea != null); import java.io.*;
fptr.close(); import java.awt.*;
} import java.applet.*;
catch (FileNotFoundException e) { public class Ejemplo12 extends Applet {
System.out.println("Hey, ese archivo no public void init() {
existe!\n"); new Ventana12();
} }
catch (IOException e) { }
System.out.println("Error de E/S!\n"); /*
} // -------- Esta clase es la que en realidad hace el
}} trabajo
También hicimos un cambio para elegir el archivo */
a imprimir desde la línea de comandos, en lugar class Ventana12 extends Frame {
de entrarlo TextArea contenido;
fijo, utilizando para eso el argumento del método Button cerrar;
main(arg[]), que consiste en una lista de Strings Ventana12() {
con los super("Ejemplo de E/S");
parámetros que se pasan en la línea a contenido = new TextArea();
continuación de java nombre_programa. Por cerrar = new Button("Cerrar");
ejemplo, si llamamos a CargarArchivo();
este programa con: add("North",contenido);
java Ejemplo11 archi.txt otro.xxx add("South",cerrar);
arg[0] contendrá "archi.txt", arg[1] contendrá pack();
"otro.xxx", y así sucesivamente. show();
Por supuesto, si llamamos a Ejemplo11 sin }
parámetros se lanzará otra excepción al intentar public boolean handleEvent(Event e) {
accederlo: if ((e.id==Event.WINDOW_DESTROY)||
C:\java\curso>java Ejemplo11 (e.target==cerrar))
java.lang.ArrayIndexOutOfBoundsException: 0 dispose();
at Ejemplo11.main(Ejemplo11.java:10) return super.handleEvent(e);
Pero también podríamos capturarla! }
Veamos un poquito cómo es esto de capturar void CargarArchivo() {
excepciones. FileInputStream fptr;
La cláusula try engloba una parte del programa DataInputStream f;
donde se pueden lanzar excepciones. Si una String linea = null;
excepción se try {
produce, Java busca una instrucción catch http://www.cybercursos.net
(nombre_de_la_excepción variable), y, si la Página 50
encuentra, fptr = new FileInputStream("Ejemplo12.java");
f = new DataInputStream(fptr);
do {
linea = f.readLine(); · Pueden establecer conexiones con el servidor
if (linea!=null) del que provienen
contenido.appendText(linea+"\n"); http://www.cybercursos.net
} while (linea != null); Página 51
fptr.close(); · Pueden llamar fácilmente páginas HTML desde el
} browser
catch (FileNotFoundException e) { · Pueden invocar métodos públicos de otros
contenido.appendText("Hey, ese archivo no applets de la misma página
existe!\n"); · Si se cargan desde la propia máquina
} (localmente) no tienen ninguna de las
catch (IOException e) { restricciones anteriores
contenido.appendText("Error de E/S!\n"); · Pueden seguir corriendo aunque se cambie de
} página en el browser
}} En realidad, la especificación de Java permite que
Lo cargamos desde la página Ejemplo12.html: los applets lean archivos en otras máquinas
<HTML> dando la URL
<HEAD> completa; sin embargo, los browsers no lo
<TITLE>Ejemplo 12 - Ejemplo con permiten. Veremos más adelante cómo
archivo</TITLE> intercambiar datos entre
</HEAD> máquinas para poder ver un archivo del server,
<BODY> por ejemplo.
<applet code="Ejemplo12.class" width=170 Nuestro modesto "Editor"
height=150> Para terminar este capítulo, el siguiente applet
</applet> nos permite cargar, editar y grabar archivos ascii
</BODY> a elección.
</HTML> Podemos usar inclusive las acciones "cut & paste"
Mientras corramos esto en la misma máquina, no del windows manager (Ctrl-C y Ctrl-V en
hay problema (anda muy bien!). Pero qué pasa si Windows)!
intentamos Cargarlo con "appletviewer Ejemplo13" luego de
cargarlo desde la red? Para los que no tengan haberlo compilado (o usar una página html desde
server html puse una copia en: un
http://www.amarillas.com/rock/java/Ejemplo12.ht browser):
m /*
El archivo no aparece! En su lugar se produce una // ----- Archivo: Ejemplo13.java
excepción; en la línea de estado del Microsoft */
Internet import java.io.*;
Explorer, por ejemplo, se lee: import java.awt.*;
exception: import java.applet.*;
com.ms.applet.AppletSecurityException: public class Ejemplo13 extends Applet {
security.file.read: Ejemplo12.java public void init() {
Esto es debido a una restricción de seguridad de new Ventana13();
Java: NO SE PUEDEN CARGAR ARCHIVOS QUE }
ESTEN EN UNA MAQUINA DISTINTA A AQUELLA }
DESDE LA CUAL SE CARGO EL APPLET. El /*
applet se corre en el cliente, e intenta acceder a // -------- Esta clase es la que en realidad hace el
un archivo local. Eso es lo que provoca la trabajo
excepción (que, por */
supuesto, puede detectarse con un catch y class Ventana13 extends Frame {
tratarse...) TextArea contenido;
Por cuestiones de seguridad, los applets son más Botones13pieVentana;
limitados que las aplicaciones Java locales. Las Ventana13() {
políticas de super("Ejemplo de E/S");
seguridad las manejan los browsers (no Java), y contenido = new TextArea();
generalmente los límites que se imponen a los pieVentana = new Botones13();
applets son: add("North",contenido);
· Un applet no puede cargar bibliotecas (libraries) add("South",pieVentana);
ni definir métodos nativos pack();
· No puede leer o escribir normalmente archivos show();
en el cliente que lo carga desde otro server }
· No puede establecer conexiones de red, salvo al public boolean handleEvent(Event e) {
servidor del que proviene if ((e.id==Event.WINDOW_DESTROY)||
· No puede arrancar programas en la máquina (e.id==2003))
donde se está ejecutando dispose();
· No puede leer ciertas propiedades del sistema if (e.id==2001)
· En las ventanas de los applets se indica que se CargarArchivo(pieVentana.toString());
trata de un applet if (e.id==2002)
Sin embargo, pueden: GrabarArchivo(pieVentana.toString());
· Reproducir sonidos http://www.cybercursos.net
Página 52
return super.handleEvent(e); if
} ((e.id==Event.ACTION_EVENT)&&(e.target==cerr
void CargarArchivo(String nombre) { ar))
FileInputStream fptr; e.id=2003;
DataInputStream f; return super.handleEvent(e);
String linea = null; }
contenido.setText(""); public String toString() {
try { return fname.getText();
fptr = new FileInputStream(nombre); }
f = new DataInputStream(fptr); }
do { /*
linea = f.readLine(); // ------- Para mostrar los errores...
if (linea!=null) contenido.appendText(linea+"\n"); */
} while (linea != null); class Error13 extends Frame {
fptr.close(); Error13(String error) {
} add("Center",new Label(error));
catch (FileNotFoundException e) { add("South", new Button("Ok"));
new Error13("El archivo no existe!"); pack();
} show();
catch (IOException e) { }
new Error13("Error leyendo archivo!"); public boolean handleEvent(Event e) {
} dispose();
} return super.handleEvent(e);
void GrabarArchivo(String nombre) { }
FileOutputStream fptr; }
DataOutputStream f; http://www.cybercursos.net
try { Página 54
fptr = new FileOutputStream(nombre); Volviendo al AWT
f = new DataOutputStream(fptr); Para aprender un poquito más sobre la biblioteca
f.writeBytes(contenido.getText()); gráfica (AWT), vamos a modificar nuestro último
fptr.close(); programa
} para usar menús.
catch (IOException e) { Vamos a volver a poner todo el código (que
new Error13("Error grabando archivo!"); ampliamos para usar como applet o aplicación
} local) marcando
} las diferencias más notables:
} /*
/* // ----- Archivo: Ejemplo14.java
// -------- Esta es para los botones y el nombre del */
archivo import java.io.*;
*/ import java.awt.*;
class Botones13 extends Panel { import java.applet.*;
TextField fname; public class Ejemplo14 extends Applet {
Button cargar; public void init() {
Button grabar; new Ventana14(true); // con "true" avisamos que
Button cerrar; es applet
Botones13() { }
setLayout(new GridLayout(1,4)); public static void main(String args[]) { //
fname = new TextField(); para usarlo como aplicación
http://www.cybercursos.net Ventana14 v14 = new Ventana14(false); //
Página 53 con "false" avisamos que no es applet
cargar = new Button("Cargar"); }
grabar = new Button("Grabar"); }
cerrar = new Button("Cerrar"); /*
add(new Label("Archivo:")); // -------- Esta clase es la que en realidad hace el
add(fname); trabajo
add(cargar); */
add(grabar); class Ventana14 extends Frame {
add(cerrar); TextArea contenido;
} boolean enApplet; // para indicar si lo llamamos
public boolean handleEvent(Event e) { como applet
if String nombreArchivo; // para guardar el
((e.id==Event.ACTION_EVENT)&&(e.target==carg nombre del archivo abierto
ar)) MenuItem mArchivoAbrir; // ACA ESTAN LOS
e.id=2001; ITEMS DE LOS MENUS
if MenuItem mArchivoGrabar; // .
((e.id==Event.ACTION_EVENT)&&(e.target==grab MenuItem mArchivoSalir; // .
ar)) MenuItem mEditCortar; // .
e.id=2002; MenuItem mEditCopiar; // .
MenuItem mEditPegar; // . }
MenuItem mEditTodo; // v if (e.target==mEditPegar) {
String clipboard; // buffer para cortar y pegar contenido.replaceText("",contenido.getSelec
boolean editado = false; // acá indicamos si tionStart(),contenido.getSelectionEnd());
modificamos el archivo http://www.cybercursos.net
Ventana14(boolean enApp) { Página 56
super("Ejemplo de E/S"); contenido.insertText(clipboard,contenido.ge
enApplet = enApp; // recordamos si es applet o tSelectionStart());
no editado=true;
Menu menuArchivo = new }
Menu("&Archivo"); // CREAMOS LOS MENUS!!! if (e.target==mEditTodo)
mArchivoAbrir = new MenuItem("&Abrir..."); contenido.selectAll();
mArchivoGrabar = new if
MenuItem("&Grabar..."); ((e.id==Event.KEY_PRESS)&&(e.target==co
http://www.cybercursos.net ntenido)) editado=true;
Página 55 mArchivoGrabar.enable(editado);
mArchivoSalir = new MenuItem("&Salir"); return super.handleEvent(e);
menuArchivo.add(mArchivoAbrir); }
menuArchivo.add(mArchivoGrabar); void CargarArchivo() {
menuArchivo.add(new MenuItem("-")); FileInputStream fptr;
menuArchivo.add(mArchivoSalir); DataInputStream f;
Menu menuEdit = new Menu("&Edit"); String linea = null;
mEditCortar = new MenuItem("Cor&tar"); if (editado) System.out.println("Pedir
mEditCopiar = new MenuItem("&Copiar"); confirmación!\n");
mEditPegar = new MenuItem("&Pegar"); FileDialog fd = new
mEditTodo = new MenuItem("&Seleccionar FileDialog(this,"Abrir...",FileDialog.LOAD); //
todo"); elijo archivo
menuEdit.add(mEditCortar); fd.show(); // usando el diálogo estándar del
menuEdit.add(mEditCopiar); sistema!
menuEdit.add(mEditPegar); nombreArchivo = fd.getFile();
menuEdit.add(new MenuItem("-")); try {
menuEdit.add(mEditTodo); fptr = new FileInputStream(nombreArchivo);
MenuBar barraMenu = new MenuBar(); f = new DataInputStream(fptr);
barraMenu.add(menuArchivo); contenido.setText(""); // vacío la ventana antes
barraMenu.add(menuEdit); de cargar nuevo archivo
setMenuBar(barraMenu); do {
contenido = new TextArea(); // solo pongo una linea = f.readLine();
ventana de texto if (linea!=null) contenido.appendText(linea+"\n");
add("Center",contenido); } while (linea != null);
pack(); fptr.close();
show(); editado=false; // archivo nuevo -> no editado
clipboard = new String(""); // clipboard vacío, }
mEditPegar.disable(); // nada para pegar, catch (FileNotFoundException e) {
mArchivoGrabar.disable(); // nada para grabar new Error14("El archivo no existe!");
} }
public boolean handleEvent(Event e) { catch (IOException e) {
if ((e.id==Event.WINDOW_DESTROY)|| new Error14("Error leyendo archivo!");
(e.target==mArchivoSalir)) { }
if (editado) System.out.println("Pedir catch (NullPointerException e) {
confirmación!\n"); // debería confirmar ;
// si se quiere }
ir sin grabar! }
if (enApplet) dispose(); void GrabarArchivo() {
else System.exit(0); FileOutputStream fptr;
} DataOutputStream f;
if (e.target==mArchivoAbrir) FileDialog fd = new
CargarArchivo(); // acá proceso selecciones FileDialog(this,"Grabar...",FileDialog.SAVE);
if (e.target==mArchivoGrabar) // grabo archivo
GrabarArchivo(); // de menú fd.setFile(nombreArchivo); // usando el diálogo
if (e.target==mEditCortar) { estándar del sistema!
clipboard = contenido.getSelectedText(); fd.show();
mEditPegar.enable(); nombreArchivo = fd.getFile();
contenido.replaceText("",contenido.getSelec try {
tionStart(),contenido.getSelectionEnd()); fptr = new FileOutputStream(nombreArchivo);
editado=true; f = new DataOutputStream(fptr);
} f.writeBytes(contenido.getText());
if (e.target==mEditCopiar) { fptr.close();
clipboard = contenido.getSelectedText(); editado=false; // recién grabado -> no editado
mEditPegar.enable(); }
http://www.cybercursos.net no agrega un ítem al menú sino una línea de
Página 57 separación, y no necesitamos crearlo como objeto
catch (IOException e) { permanente.
new Error14("Error grabando archivo!"); Si miramos la arquitectura de las clases, tanto
} MenuBar como MenuItem descienden de
catch (NullPointerException e) { MenuComponent.
; A su vez, Menu desciende de MenuItem, por lo
} que implementa los mismos métodos y vamos a
} lo que
} decíamos antes: un menú puede ser un ítem de
/* otro menú, y así sucesivamente tantos subniveles
// ------- Para mostrar los errores... de menús
*/ como queramos.
class Error14 extends Frame { Finalmente, en nuestro manejador de eventos
Error14(String error) { simplemente necesitamos verificar si se eligió un
add("Center",new Label(error)); ítem probando
add("South", new Button("Ok")); si el evento ocurrió sobre el ítem determinado:
pack(); if ((e.id==Event.WINDOW_DESTROY)||
show(); (e.target==mArchivoSalir)) {
} if (editado) System.out.println("Pedir
public boolean handleEvent(Event e) { confirmación!\n");
dispose(); if (enApplet) dispose();
return super.handleEvent(e); else System.exit(0);
} }
} if (e.target==mArchivoAbrir)
Menú a la Java CargarArchivo();
Bueno, lo primero que vamos a ver son los ................
menús. if (e.target==mEditTodo)
La barra de menú está compuesta por menúes, contenido.selectAll();
que a su vez están compuestos de ítems (que En resumen lo que hago es:
pueden también · Si eligió Archivo/Salir (o alt-F4 o lo que sea)
ser menúes). Por ejemplo la barra de menú la salgo del programa
declaramos con: · Si eligió Archivo/Abrir, llamo al método
MenuBar barraMenu = new MenuBar(); CargarArchivo
y le agregamos los menúes Archivo y Edit (que · Si eligió Archivo/Grabar, llamo al método
habremos creado previamente) con: GrabarArchivo
barraMenu.add(menuArchivo); · Si eligió Edit/Cortar copio el texto seleccionado a
barraMenu.add(menuEdit); mi clipboard y borro la selección
Finalmente la declaramos como EL menú de la · Si eligió Edit/Copiar sólo copio el texto
ventana (Frame): seleccionado a mi clipboard
setMenuBar(barraMenu); · Si eligió Edit/Pegar borro el texto seleccionado e
Cada uno de los menús los declaramos inserto el de mi clipboard
previamente: · Si eligió Edit/Seleccionar_todo marco todo el
Menu menuArchivo = new Menu("&Archivo"); texto
... En todos los casos, si se modifica el texto del
Menu menuEdit = new Menu("&Edit"); contenido lo indico poniendo editado en true; lo
Noten que el "&" no se visualiza, sino que la letra mismo si
que le sigue aparece subrayada: Archivo, Edit. presiono una tecla sobre el área de edición:
Esto permite if
que se pueda seleccionar el menú tanto con el ((e.id==Event.KEY_PRESS)&&(e.target==conteni
mouse como con la tecla alt- o meta-, seguida de do)) editado=true;
la tecla Un par de aclaraciones:
subrayada. · getSelectionStart() y getSelectionEnd() marcan
http://www.cybercursos.net los límites del texto seleccionado (si no lo hay,
Página 58 son iguales).
A su vez, el método add está presente también · getSelectedText() devuelve el texto seleccionado
en la clase Menú y nos permite agregar los ítems: en el TextArea.
mArchivoAbrir = new MenuItem("&Abrir..."); · replaceText() reemplaza una parte (o todo) del
mArchivoGrabar = new MenuItem("&Grabar..."); TextArea por un String.
mArchivoSalir = new MenuItem("&Salir"); · insertText() inserta un String en un lugar
menuArchivo.add(mArchivoAbrir); determinado del TextArea.
menuArchivo.add(mArchivoGrabar); · selectAll() selecciona todo el texto del TextArea.
menuArchivo.add(new MenuItem("-")); http://www.cybercursos.net
menuArchivo.add(mArchivoSalir); Página 59
A estos ítems los hemos declarado como globales · MenuItem.enable() habilita un ítem de menú. Lo
en la clase para usarlos luego en los eventos. utilizo para habilitar Edit/Pegar sólo luego de
Noten además cortar o copiar algo a mi clipboard.
que · En el caso del ítem Archivo/Grabar, lo habilito o
menuArchivo.add(new MenuItem("-")); no dependiendo de la variable editado, utilizando
la otra forma de enable: método hide(), que lo oculta de la vista pero no se
MenuItem.enable(boolean). pierde hasta no salir del método que lo creó,
Diálogos donde actuaría
En Java disponemos de la clase Dialog para crear el recogedor de basura de la memoria). Esto hace
diálogos, es decir, ventanitas temporarias para que aunque no lo veamos podamos llamar al
entradas de método
usuario, que dependen de otra (de hecho la clase getFile() sobre este objeto, que nos devuelve el
Dialog es heredera de la clase Window). nombre del archivo seleccionado (o null si se
Si bien podemos crear diálogos a medida usando presionó
la clase Frame, se supone que usar diálogos debe Cancel).
ser más http://www.cybercursos.net
fácil. La realidad es que por ahora no se puede Página 60
usar mucho más que los diálogos estándar (y el DibuJava
único que vale Además de los componentes estándar (botones,
la pena es FileDialog), ya que las listas, etc.), hay un componente para dibujo
implementaciones actuales de Java tienen un "libre" que nos
problema: en algunas permite implementar cualquier otro tipo de
plataformas el programa que abre el diálogo control: la clase Canvas. Típicamente se usa para
sigue, en lugar de esperar que se cierre el diálogo dibujar, y
y devuelva la corresponde a una zona rectangular dentro de
respuesta. una ventana.
Por eso hemos puesto solamente una indicación La clase en sí no hace prácticamente nada; el
adonde debería haber un diálogo de confirmación: programador debe definir una subclase de
if (editado) System.out.println("Pedir Canvas a la que el
confirmación!\n"); AWT le envía todos los eventos de mouse y
En ese lugar deberíamos llamar por ejemplo a un teclado. Redefiniendo los métodos gotFocus,
diálogo que nos permita decidir por sí o por no: lostFocus,
if (editado) { keyDown, keyUp, mouseEnter, mouseExit,
sino = new ConfirmarDlg(this,"Archivo mouseMove, mouseDrag, mouseDown y
modificado!"); mouseUp, el
if (sino.getResponse()==true) ....; programador puede hacer lo que se le ocurra
else ....; dentro de ese rectángulo. Vamos a hacer uso de
} un Canvas para
o algo así. Esto mismo lo podemos hacer de otras generar un applet donde habrá una zona
maneras, por ejemplo usando threads y rectangular dentro de la que, haciendo click con
comunicaciones el mouse y
entre procesos, pero se complica mucho para esta moviéndolo sin soltar el botón, dibujaremos un
altura del curso. Esperemos un poco más rectángulo dinámicamente.
adelante, aunque Esto nos permitirá ver cómo usar un Canvas para
Sun me prometió que en la versión 1.1 ya va a dibujar, capturar eventos, etc. El borde tiembla un
estar corregido (sale para fines del '96). poco al
Por lo pronto, veamos un caso simple con la clase redibujar, pero ya veremos cómo evitar eso.
FileDialog: Canvas en acción
FileDialog fd = new Primero vamos a poner, como ya se está haciendo
FileDialog(this,"Abrir...",FileDialog.LOAD); costumbre, el código del applet (Recordar que
fd.show(); debe
nombreArchivo = fd.getFile(); cargarse desde una página html para verlo! Aquí
Primero declaramos una variable de tipo no creamos ninguna ventana y no podremos verlo
FileDialog, y creamos la instancia con new. Como como
parámetros se aplicación standalone) y luego intentaremos
pasa el padre (this, o sea "esta ventana"), el título explicar cómo funciona.
de la ventanita de diálogo, y una constante LOAD import java.awt.*;
o SAVE import java.applet.Applet;
(son static, por lo que se denominan directamente public class Ejemplo15 extends Applet {
con el nombre de la clase y no necesariamente de public void init() {
una Label label = new Label("Pique y arrastre con el
instancia) que indica si el diálogo es para cargar o mouse!");
grabar un archivo (Obviamente la tarea en sí de miCanvas zonaDib = new miCanvas();
cargar o zonaDib.resize(new Dimension (200,200));
grabar el archivo la tenenmos que hacer nosotros, add("North", label);
el diálogo sólo espera que elijamos un nombre). add("Center", zonaDib);
El método show() muestra el diálogo y espera resize(300,250);
que seleccionemos y presionemos Ok o Cancel. }
Aquí es donde }
fallan los demás diálogos ya que es programa class miCanvas extends Canvas {
sigue sin esperar. Rectangle rectActual;
Finalmente, el diálogo se cierra pero no se elimina public boolean mouseDown(Event e, int x, int y) {
el objeto (posiblemente está implementado rectActual = new Rectangle(x, y, 0, 0);
usando el repaint();
return false; El método rezise, sobre la clase miCanvas, nos
} permite redimensionar el mismo al tamaño
public boolean mouseDrag(Event e, int x, int y) { deseado.
rectActual.resize(x-rectActual.x, y-rectActual.y); Igualmente, usamos resize sobre el applet para
repaint(); darle un tamaño adecuado. Si se modifica el
return false; tamaño de la
} ventana en el appletviewer se observará un
public boolean mouseUp(Event e, int x, int y) { comportamiento algo extraño en cuanto al
rectActual.resize(x-rectActual.x, y-rectActual.y); posicionamiento
repaint(); relativo del rectángulo y el cartel, pero para
return false; simplificar esto bastará.
http://www.cybercursos.net Nuestro Canvas a medida
Página 61 Como no vamos a tomar ninguna acción especial
} al crear el canvas, no hemos definido el
public void paint(Graphics g) { constructor (se
Dimension d = size(); utiliza el constructor por defecto de la clase
g.setColor(Color.red); Canvas).
g.drawRect(0, 0, d.width-1, d.height-1); Simplemente hemos redefinido algunos métodos
g.setColor(Color.blue); para actuar al presionar, arrastrar y soltar el
if (rectActual != null) { mouse, para
Rectangle box = cortarRect(rectActual, d); redibujar el área de dibujo (canvas) y para
g.drawRect(box.x, box.y, box.width-1, box.height- recortar el rectángulo dibujado si nos vamos con
1); el mouse fuera
} del espacio que ocupa el canvas.
} La variable global rectActual, de la clase
Rectangle cortarRect(Rectangle miRect, Rectangle, contendrá las coordenadas del
Dimension areaDib) { rectángulo que estamos
int x = miRect.x; dibujando. El método Paint se llama
int y = miRect.y; automáticamente cada vez que es necesario
int ancho = miRect.width; redibujar el componente, o si
int alto = miRect.height; llamamos explícitamente al método repaint():
if (ancho < 0) { public void paint(Graphics g) {
ancho = -ancho; Dimension d = size();
x = x - ancho + 1; g.setColor(Color.red);
if (x < 0) { g.drawRect(0, 0, d.width-1, d.height-1);
ancho += x; g.setColor(Color.blue);
x = 0; if (rectActual != null) {
} Rectangle box = cortarRect(rectActual, d);
} g.drawRect(box.x, box.y, box.width-1, box.height-
if (alto < 0) { 1);
alto = -alto; }
y = y - alto + 1; }
if (y < 0) { En primer lugar le asignamos a una variable d el
alto += y; tamaño del canvas usando el método size(), luego
y = 0; elegimos
} un color (rojo) para dibujar un borde y dibujamos
} un rectángulo del tamaño del componente:
if ((x + ancho) > areaDib.width) { Dimension d = size();
ancho = areaDib.width - x; g.setColor(Color.red);
} g.drawRect(0, 0, d.width-1, d.height-1);
if ((y + alto) > areaDib.height) { Dos atributos de la clase Dimension, width y
alto = areaDib.height - y; height, se han cargado con el tamaño del canvas
} y son los que
return new Rectangle(x, y, ancho, alto); usamos para dar el tamaño del rectángulo.
} Luego, si se está dibujando un rectángulo
} (rectActual != null) simplemente lo recortamos
El applet-container (en caso de que
En primer lugar hemos tenido en cuenta que un hayamos arrastrado el mouse fuera del canvas) y
Applet es un Panel, y por lo tanto también un lo dibujamos.
Container, así El método que lo recorta a los límites del canvas,
que en lugar de crear una ventana aparte cortarRect, asigna a cuatro variables las
simplemente le agregamos dos componentes: un coordenadas del
Label y un Canvas. rectángulo (que se le pasaron como parámetro
zonaDib.resize(new Dimension (200,200)); miRect al llamarlo):
http://www.cybercursos.net int x = miRect.x;
Página 62 int y = miRect.y;
add("North", label); int ancho = miRect.width;
add("Center", zonaDib); int alto = miRect.height;
resize(300,250);
Si el ancho (o el alto) es negativo, simplemente lo Vamos a retocar un poquito nuestro ejemplo15
cambia de signo y toma como coordenada x (y) para que no se borren los rectángulos cuando
de origen queremos
el otro vértice del rectángulo, que corresponderá dibujar uno nuevo. Aprenderemos algo sobre la
al x que se pasó menos el ancho y más uno clase Vector, perteneciente al paquete java.util.
(recordar que el Vectores en acción
origen de coordenadas empieza en cero y no en Los vectores nos permiten hacer arreglos de
uno). Si este vértice está fuera del canvas (x<0), cualquier tipo de objeto, y referirnos
lo pone en individualmente a cualquier
cero y le resta al ancho la parte recortada (notar elemento del vector, aunque para utilizarlos
que ancho+=x, como x es negativo, es en (debido a que para java el vector contiene objetos
realidad una resta). genéricos)
http://www.cybercursos.net tendremos que decirle qué clase de objeto es
Página 63 mediante un "cast". Vamos a ver cómo quedan
if (ancho < 0) { nuestras clases
ancho = -ancho; Ejemplo16 (ex Ejemplo15) y miCanvas:
x = x - ancho + 1; import java.awt.*;
if (x < 0) { import java.util.*;
ancho += x; import java.applet.Applet;
x = 0; public class Ejemplo16 extends Applet {
} public void init() {
} ................ (esta parte no cambia)................
Si nos vamos del área de dibujo por la derecha (o }
por abajo), simplemente le recortamos al ancho }
(alto) el class miCanvas extends Canvas {
exceso de modo que llegue hasta el borde del Vector v = new Vector(); // inicializamos con
área de dibujo (que también hemos pasado al tamaño indeterminado
método como // Java se encarga de manejar la me moria
parámetro): necesaria!
if ((x + ancho) > areaDib.width) { public boolean mouseDown(Event e, int x, int y) {
ancho = areaDib.width - x; v.addElement( new Rectangle(x, y, 0, 0) ); //
} nuevo elemento!
Sólo nos quedan por ver los métodos que repaint();
responden al mouse. return false;
Cuando presionamos el mouse dentro del canvas, }
comenzamos la creación de un nuevo rectángulo public boolean mouseDrag(Event e, int x, int y) {
de ancho y Rectangle r = (Rectangle)v.lastElement(); //
alto cero que comienza en el punto en que hemos cast: v son rectángulos
presionado el mouse, y redibujamos el canvas: r.resize( x - r.x, y - r.y ); // (creé r sólo por
public boolean mouseDown(Event e, int x, int y) { claridad)
rectActual = new Rectangle(x, y, 0, 0); repaint();
repaint(); return false;
return false; }
} public boolean mouseUp(Event e, int x, int y) {
Al mover el mouse, redimensionamos el Rectangle r = (Rectangle)v.lastElement(); //
rectángulo con ancho x menos el origen de dibujo cast: v son rectángulos
(y alto y menos el r.resize( x - r.x, y - r.y ); // (creé r sólo por
origen de dibujo), y repintamos: claridad)
public boolean mouseDrag(Event e, int x, int y) { repaint();
rectActual.resize(x-rectActual.x, y-rectActual.y); return false;
repaint(); }
return false; public void paint(Graphics g) {
} int i; // contador de rectángulos
Finalmente, al soltar el mouse, redimensionamos Dimension d = size();
como antes y redibujamos: g.setColor(Color.red);
public boolean mouseUp(Event e, int x, int y) { g.drawRect(0, 0, d.width-1, d.height-1);
rectActual.resize(x-rectActual.x, y-rectActual.y); g.setColor(Color.blue);
repaint(); if (v.size() > 0)
return false; http://www.cybercursos.net
} Página 65
Como no se toma ninguna medida para guardar el for (i=0; i<v.size(); i++) {
rectángulo dibujado, al crear uno nuevo Rectangle box = cortarRect(
(reasignando (Rectangle)v.elementAt( i ), d);
rectActual a un nuevo rectángulo), el anterior se g.drawRect(box.x, box.y, box.width-1, box.height-
pierde. 1);
http://www.cybercursos.net }
Página 64 }
DibuJava II ........................ (el resto no cambia)
........................
} Aquí no hemos creado variables intermedias ya
Les sugiero utilizar un HTML que reserve espacio que igualmente es claro (eso creo...).
suficiente para ver todo el applet, como: Flicker molesto!
<HTML> Bueno, el problema que nos queda es el molesto
<HEAD> "flicker", o sea la manera en que titila el dibujo
<TITLE>Ejemplo 16 - Ejemplo con cuando
canvas</TITLE> movemos el mouse. Esto es porque cada vez que
</HEAD> se llama a paint(), el fondo se borra y se redibuja
<BODY> todo el
<applet code="Ejemplo16.class" width=300 canvas.
height=250> Básicamente, la manera de evitarlo es
</applet> reescribiendo el método update(), que es el que
</BODY> borra el fondo antes de
</HTML> llamar a paint() para que no lo borre; otro método
Veamos los pasos ahora. En primer lugar creamos (que es el que vamos a usar) es dibujar no sobre
una variable (global a la clase) llamada v, de la pantalla
clase Vector, sino sobre un "buffer" gráfico, y luego copiar ese
y sin asignarle un tamaño definido: buffer sobre la pantalla (lo que es mucho más
Vector v = new Vector(); eficiente que
Al crear un nuevo rectángulo agregamos un dibujar sobre la misma).
elemento (objeto) al vector mediante el método Para eso vamos a crear un par de objetos:
add: class miCanvas extends Canvas {
v.addElement( new Rectangle(x, y, 0, 0) ); Vector v = new Vector();
Para acceder a un atributo de un objeto del vector Image imgBuff;
no basta utilizar directamente el vector, como: Graphics grafBuff;
v.lastElement().x .............................
(lastElement() nos permite acceder al último Image es una clase abstracta, madre de todas las
elemento agregado al vector). Es necesario clases que representan imágenes gráficas.
aclarar Graphics es
explícitamente que el elemento en cuestión es un también abstracta y nos permite obtener un
rectángulo, ya que el vector puede contener contexto en el cual dibujar.
objetos de Lo que vamos a hacer es modificar nuestro
cualquier tipo. Para eso usamos el casting: método paint() para que simplemente llame a
(Rectangle)v.lastElement().x update(), y
En nuestro código original reemplazaríamos por: redefinir el método update():
(Rectangle)v.lastElement().resize( x - public void paint(Graphics g) {
(Rectangle)v.lastElement().x, ...... update(g);
Pero es más claro si usamos una variable local de }
clase Rectangle, le asignamos el mismo objeto El método update() es el que hará todo el trabajo
que y básicamente es como nuestro viejo paint() con
acabamos de agregar al vector, y lo usamos en su algunos
lugar: agregados:
Rectangle r = (Rectangle)v.lastElement(); public void update(Graphics g) {
r.resize( x - r.x, y - r.y ); int i;
Finalmente, en el método paint() no podemos Dimension d = size();
asignar el elemento hasta no saber que existe if (grafBuff == null) {
(originalmente el imgBuff = createImage(d.width, d.height);
vector estaba vacío!). Así que un if nos permite grafBuff = imgBuff.getGraphics();
verificar que el tamaño del vector es mayor que }
cero (tiene grafBuff.setColor(getBackground());
elementos), y un for nos permite dibujarlos uno grafBuff.fillRect(0, 0, d.width, d.height);
por uno. grafBuff.setColor(Color.red);
http://www.cybercursos.net grafBuff.drawRect(0, 0, d.width-1, d.height-1);
Página 66 grafBuff.setColor(Color.blue);
Se puede acceder a todos los elementos, uno por http://www.cybercursos.net
uno, mediante el método elementAt(x), que nos Página 67
da el xésimo if (v.size() > 0) for (i=0; i<v.size(); i++) {
elemento del vector. El método size() nos da la Rectangle box =
cantidad de elementos (el primero es el número cortarRect((Rectangle)v.elementAt(i), d);
0, y grafBuff.drawRect(box.x, box.y, box.width-1,
así): box.height-1);
if (v.size() > 0) }
for (i=0; i<v.size(); i++) { g.drawImage(imgBuff, 0, 0, this);
Rectangle box = cortarRect( }
(Rectangle)v.elementAt( i ), d); En negrita hemos indicado los agregados.
g.drawRect(box.x, box.y, box.width-1, box.height- Si no está creado todavía (grafBuff==null),
1); creamos nuestro buffer de dibujo. Para crear dicho
} buffer gráfico
(de clase Graphics), primero creamos una imagen resize(new Dimension (200,200));
que en este caso tiene las mismas dimensiones }
que el public void start() {
canvas (d.width x d.height ), y luego asignamos a if (animador == null) animador = new
grafBuff el contexto de dicha imagen mediante el Thread(this);
método animador.start();
getGraphics(). Imagínense que con }
createImage(...) crean una "pantalla virtual", y http://www.cybercursos.net
getGraphics() nos da Página 68
una forma de acceder a esa pantalla como si public void run() {
fuera real. while (Thread.currentThread() == animador) {
Utilizando dicho contexto, elegimos como color el repaint();
mismo color de fondo del applet try {
(getBackground()) y Thread.sleep(100);
dibujamos un rectángulo lleno (fillRect(...)), }
borrando así cualquier cosa que hubiera estado catch (InterruptedException e) {
dibujada. break;
En itálica hemos indicado las modificaciones a }
nuestro método anterior. Simplemente, en lugar }
de usar el }
contexto de la pantalla (el parámetro g del public void update(Graphics g) {
método), dibujamos sobre nuestro contexto- int i;
pantalla virtual. int dx, dy;
Finalmente, y para poder visualizar nuestro Dimension d = size();
dibujo, usamos el método drawImage sobre el if (grafBuff == null) {
contexto de la imgBuff = createImage(d.width, d.height);
pantalla real (g), que copia nuestro contexto grafBuff = imgBuff.getGraphics();
imgBuff en las coordenadas (0,0) sobre la }
pantalla. Se hace grafBuff.setColor(getBackground());
también referencia al canvas (...this): el cuarto grafBuff.fillRect(0, 0, d.width, d.height);
parámetro de drawImage es un objeto de clase grafBuff.setColor(Color.red);
ImageObserver, una interface que sirve para que grafBuff.drawRect(0, 0, d.width-1, d.height-1);
el objeto dentro del cual se dibuja reciba grafBuff.setColor(Color.blue);
mensajes dx = (int)(50 * Math.abs(Math.cos(ang)));
asincrónicos que le indican cómo está siendo dy = (int)(50 * Math.abs(Math.sin(ang)));
construida la imagen, y cuándo está lista. ang = ang + 0.1;
Animate! if (ang>2*Math.PI) ang = 0.0;
Si bien puede ser un poco más complejo de grafBuff.drawRect(100-dx, 100-dy, 2*dx, 2*dy);
entender que un dibujo directo sobre la pantalla, g.drawImage(imgBuff, 0, 0, this);
notarán que la }
implementación es directa y no trae ningún }
problema. Esta misma aproximación puede http://www.cybercursos.net
utilizarse para crear Página 69
animaciones. Java en hebras
En este ejemplo, para manejar la ejecución La clase anterior usamos, en el último ejemplo, un
cuadro a cuadro de la animación, usamos concepto al que vamos a dedicar ahora nuestra
Threads. No se atención:
preocupen por eso, lo veremos pronto. los threads.
Únicamente tengan en cuenta que nuestro applet La traducción literal de thread es hilo o hebra, y
debe implementar la se utiliza también para referirse al hilo de un
clase runnable, y el thread se encarga de ejecutar discurso. El
el método run() que simplemente llama a concepto de threads en los ambientes y sistemas
repaint() y espera operativos es un poco complejo de explicar pero
100 milisegundos entre cuadro y cuadro. sencillo de
El trabajo de cálculo y dibujo lo hace update(). Se entender: independientemente del sistema
los dejo para que lo estudien; no es nada elegido, puede pensarse que un thread es algo así
complicado y como el lugar de
también usa doble buffering (como el ejemplo ejecución de un programa.
anterior). En la mayoría de los programas que hemos visto,
import java.awt.*; hemos usado un solo thread; es decir que un
import java.util.*; programa
import java.applet.Applet; comienza y su ejecución sigue un camino único:
public class Ejemplo18 extends Applet como un monólogo.
implements Runnable { Java es multithreading. Esto significa algo así
Thread animador; como que tiene capacidad de diálogo, y más aún:
Image imgBuff; puede
Graphics grafBuff; ejecutar muchos threads en paralelo, como si
double ang = 0.0; tratáramos de una conversación múltiple y
public void init() { simultánea.
No confundir aquí multithreading con la capacidad throws InterruptedException {
de ejecutar varios programas a la vez. Esta es una Thread Juan = new Thread (new Amigo("Juan"));
posibilidad, pero también un mismo programa Thread Luis = new Thread (new Amigo("Luis"));
puede utilizar varios threads ("caminos de Thread Nora = new Thread (new Amigo("Nora"));
ejecución"?) Juan.start();
simultáneamente. Luis.start();
Esto, por supuesto, depende fundamentalmente Nora.start();
de la capacidad del sistema operativo para Juan.join();
soportar Luis.join();
multithreading, y por esto Java no puede Nora.join();
ejecutarse (al menos en forma completa) en }
sistemas que no lo }
soporten. class Amigo implements Runnable {
El uso de threads nos permite, por ejemplo, String mensaje;
ejecutar simultáneamente varios programas que public Amigo(String nombre) {
interactúen entre mensaje = "Hola, soy "+nombre+" y este es mi
ellos; o, también, que un programa, mientras por mensaje ";
ejemplo actualiza la pantalla, simultáneamente }
realice una public void run() {
serie de cálculos sin tener que hacer esperar el for (int i=1; i<6; i++) {
usuario. String msg = mensaje+i;
Una forma sencilla de verlo es imaginar que System.out.println(msg);
tenemos un grupo de microprocesadores que }
pueden ejecutar, }
cada uno, un solo thread; y nosotros asignamos }
programas (o partes de programas) a cada uno de Como siempre, compilarlo con javac
ellos. Ejemplo19.java y ejecutarlo con java Ejemplo19.
Además, podemos imaginar que esos En un sistema operativo preemptivo, la salida
microprocesadores comparten una memoria será más o menos así:
común y recursos comunes, Hola, soy Juan y este es mi mensaje 1
de lo que surgirá una serie de problemas Hola, soy Juan y este es mi mensaje 2
importantes a tener en cuenta cuando se usan Hola, soy Luis y este es mi mensaje 1
threads. Hola, soy Luis y este es mi mensaje 2
Los pasos básicos Hola, soy Nora y este es mi mensaje 1
Hay tres cosas a tener en cuenta para usar Hola, soy Nora y este es mi mensaje 2
threads en un programa: Hola, soy Nora y este es mi mensaje 3
· La clase que queremos asignar a un thread debe Hola, soy Juan y este es mi mensaje 3
implementar la interface Runnable. ...........etc.
· Debemos crear una variable (instancia) del tipo Qué significa que un sistema operativo es
Thread, que nos permitirán acceder y manejar el preemptivo? Casos típicos son Unix o Windows 95:
thread. En los applets, en el método start() cada tarea
simplemente crearemos el thread (y, utiliza una parte del tiempo del procesador, y
posiblemente, lo luego lo libera para que puedan ejecutarse otras
pondremos a ejecutar) tareas (otros
· Y por último tenemos que crear un método run() threads). Por eso se mezclan los mensajes de
que es el que ejecuta el código del programa salida. Si el sistema operativo es no preemptivo,
propiamente dicho. el procesador
La interface Runnable, simplemente definida no se libera hasta que no termina con el thread
como: actual, y por lo tanto la salida sería así:
public interface java.lang.Runnable Hola, soy Juan y este es mi mensaje 1
{ Hola, soy Juan y este es mi mensaje 2
// Methods Hola, soy Juan y este es mi mensaje 3
public abstract void run(); Hola, soy Juan y este es mi mensaje 4
} Hola, soy Juan y este es mi mensaje 5
le asegura al compilador que nuestra clase (la http://www.cybercursos.net
que utilizará el thread para ejecutarse) dispone de Página 71
método run(). Hola, soy Luis y este es mi mensaje 1
Vamos a ver un par de ejemplos, primero una Hola, soy Luis y este es mi mensaje 2
aplicación standalone y luego un applet. ...........etc.
Reunión de amigos Si ustedes están utilizando un sistema operativo
El siguiente ejemplo (Ejemplo19.java) usa threads no preemptivo, deben explícitamente indicarle al
para activar simultáneamente tres objetos de la procesador
misma cúando puede ejecutar (dar paso) a otra tarea;
clase, que comparten los recursos del procesador para eso simplemente modifiquen el método
peleándose para escribir a la pantalla. run():
class Ejemplo19 { public void run() {
public static void main(String argv[]) for (int i=1; i<6; i++) {
http://www.cybercursos.net String msg = mensaje+i;
Página 70 System.out.println(msg);
Thread.yield(); "muerto" (finalizada su ejecución). Este método
} puede disparar la excepción InterruptedException,
} por lo que
En este ejemplo, tanto en sistemas preemptivos lo hemos tenido en cuenta en el encabezamiento
como no preemptivos la salida será: de la clase.
Hola, soy Juan y este es mi mensaje 1 En nuestro ejemplo, simplemente a cada instancia
Hola, soy Luis y este es mi mensaje 1 de Amigo(...) que creamos la hemos ligado a un
Hola, soy Nora y este es mi mensaje 1 thread y
Hola, soy Juan y este es mi mensaje 2 puesto a andar. Corren todas en paralelo hasta
Hola, soy Luis y este es mi mensaje 2 que mueren de muerte natural, y también el
Hola, soy Nora y este es mi mensaje 2 programa principal
Hola, soy Juan y este es mi mensaje 3 acaba.
Hola, soy Luis y este es mi mensaje 3 Cuando usamos Thread.yield() (que en rigor
...........etc. debería ser Thread.currentThread().yield(), pero
Esto es porque en seguida de imprimir estamos siendo algo de
liberando al procesador para que pase a otro uso muy común los desarrolladores de Java lo han
thread (si hay simplificado), simplemente el thread actual le
alguno esperando). Noten la diferencia con el permite al
primer caso, sin usar yield(), para sistemas procesador dedicarse a otro (si es que hay alguno
preemptivos: el deseando utilizar sus servicios).
procesador reparte su trabajo en forma La clase Amigo() es muy simple y con lo que
(aparentemente) impredecible, por eso el orden hemos visto hasta ahora no creo que tengamos
de los mensajes no será que explicar
el mismo en cualquier máquina o sistema nada más.
operativo. Y los applets...?
Ya lo vimos funcionar, pero sería bueno que lo También podemos usar estos conceptos en los
entendamos! Por eso, vamos paso a paso. applets. Veamos un ejemplo para terminar la
Creando Threads clase de hoy, muy
Thread es una clase básica en Java, que similar al anterior, donde tres contadores cuentan
implementa la interface Runnable y dispone de (en un sistema preemptivo) en forma simultánea.
unos cuantos métodos Recuerden
por defecto. Lo importante a tener en cuenta que, crear una página HTML con el tag
para usar Threads, debemos crearlas como <applet code="Ejemplo20.class" width=300
instancias y height=100></applet>
ponerlas a "andar": para poder verlo en acción con el appletviewer o
Thread Juan = new Thread (new Amigo("Juan")); su browser favorito (que desde ya supongo que
.............. soporta Java!
Juan.start(); ;-)
.............. El programa es extremandamente sencillo, y
Juan.join(); pueden verlo en acción si lo desean cargando via
Un thread tiene cuatro estados posibles: Internet la
creado: ha sido creado mediante new(), pero no página:
se ha puesto en marcha todavía. http://www.amarillas.com/rock/java/Ejemplo20.ht
activo: está en ejecución, ya sea porque arrancó m
con start() o fue "despertado" con resume(). // Ejemplo de applet que usa multithreading
dormido: ha sido suspendida su ejecución import java.awt.*;
momentáneamente mediante wait(), sleep() o import java.applet.*;
suspend(). public class Ejemplo20 extends Applet {
muerto: se ha detenido definitivamente, ya sea TextField tfa,tfb,tfc;
porque se terminó el programa o mediante el public void init() {
llamado a stop(). setLayout(new GridLayout(3,2));
En este ejemplo hemos creado un thread tfa = new TextField("0");
asignándole simultáneamente un objeto que lo tfb = new TextField("0");
utiliza (new tfc = new TextField("0");
Amigo("Juan")), y seguidamente lo hemos add(new Label("Contador A"));
activado, llamando al método start(). Este método add(tfa);
se encarga de add(new Label("Contador B"));
inicializar el thread y, finalmente, llamar al add(tfb);
método run() que hemos implementado. add(new Label("Contador B"));
http://www.cybercursos.net add(tfc);
Página 72 }
De este modo, todo ocurre como si los métodos public void start() {
run() de cada objeto se ejecutaran en paralelo, Thread A = new Thread (new Counter(tfa));
concurrentemente. La forma de manejar esto Thread B = new Thread (new Counter(tfb));
depende del sistema operativo. Thread C = new Thread (new Counter(tfc));
El método join() que llamamos al final hace que el A.start();
programa principal espere hasta que este thread B.start();
esté http://www.cybercursos.net
Página 73
C.start(); Como se ve, a pesar de haber arrancado antes la
} tortuga, casi todo el tiempo de CPU lo usa primero
} el
class Counter implements Runnable { Guepardo, luego la Liebre (aunque algo queda
TextField texto; para la pobre tortuga, como se ve en la T
String s; marcada), y
public Counter(TextField txtf) { finalmente para la Tortuga. No todas las corridas
texto = txtf; ni todos los sistemas dan igual salida, ya que ésta
} depende
public void run() { de la carga del procesador y de la
for (int i=0; i<1000; i++) { implementación de Java particular.
texto.setText(s.valueOf(i)); Este programa simplemente crea tres animales
} (clase Animal), asigna un thread a cada uno y los
} ejecuta. Este
} ejemplo está hecho en base a uno del libro
http://www.cybercursos.net "Programación Java" de Macary y Nicolas.
Página 74 Sincronicemos los relojes
La liebre y la tortuga (y el guepardo) Un problema básico del multithreading es cuando
Java dispone de un mecanismo de prioridades varios programas (o, para el caso, varios threads)
para los threads, de modo de poder asignar más acceden a
tiempo de CPU los mismos datos: ¿cómo sabemos si uno de ellos
a un thread que a otro. Típicamente se asigna una no los modifica mientras los está usando otro?.
prioridad de 1 a 10 (10 es la mayor prioridad) Veamos un ejemplo, donde suponemos que varios
mediante threads usan la variable valorImportante:
setPriority, como en el ejemplo que sigue: if (valorImportante > 0 ) {
public class Ejemplo21 { ..... algo se procesa acá ........
static Animal tortuga; valorImportante = valorImportante - 1;
static Animal liebre; ..... sigue.....................
static Animal guepardo; }
public static void main(String argv[]) ¿Cómo nos aseguramos que valorImportante no
throws InterruptedException { cambió entre el if y la línea resaltada? Otros
tortuga = new Animal(2, "T"); threads pueden
liebre = new Animal(3, "L"); haberlo modificado mientras tanto. Asimismo,
guepardo = new Animal(4, "G"); puede suceder que dos threads estén ejecutando
tortuga.start(); la misma
liebre.start(); porción de código, y se pierda uno de los
guepardo.start(); decrementos. Imaginen algo así:
tortuga.join(); (antes) valorImportante = 10
liebre.join(); (thread 1) lee valorImportante = 10
guepardo.join(); (thread 2) lee valorImportante = 10
} (thread 1) 10 -1 = 9
} (thread 2) 10 -1 = 9
class Animal extends Thread { (thread 2) asigna 9 a valorImportante
String nombre; (thread 1) asigna 9 a valorImportante
public Animal(int prioridad, String nombre) { (después) valorImportante = 9
this.nombre = nombre; Como vemos, a pesar de haber restado dos veces,
setPriority(prioridad); hemos perdido una de las restas. Aunque usemos
} -= en vez
public void run() { de la resta es lo mismo, porque el código
for (int x = 0; x < 30; x++) { igualmente se resuelve en varios pasos (varias
System.out.print( nombre ); operaciones
yield(); atómicas).
} Para evitar esto, Java nos brinda la palabra clave
System.out.println("\nLlega "+nombre ); Synchronized, que bloquea el acceso a una
} variable a todos
} los threads menos el que lo está usando.
La salida de este programa, ejecutado con java Vamos a ver un caso específico; se trata de dos
Ejemplo21, es por ejemplo: contadores que usan el mismo sumador para
C:\java\curso>java Ejemplo21 sumar de a uno
GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG una cantidad a. Supuestamente entre los dos
Llega G deben llevar el sumador (a) hasta 20000.
LTLLLLLLLLLLLLLLLLLLLLLLLLLLLLL // Archivo Ejemplo22.java, compilar con javac
Llega L Ejemplo22.java, ejecutar con java Ejemplo22
TTTTTTTTTTTTTTTTTTTTTTTTTTTTT public class Ejemplo22 {
Llega T public static void main(String argv[]) {
http://www.cybercursos.net Sumador A = new Sumador(); // un único sumador
Página 75 Contador C1 = new Contador(A); // dos threads
que lo usan...
Contador C2 = new Contador(A); // ...para sumar
C1.start(); C:\java\curso>java Ejemplo22
C2.start(); 0<
try { 5000 < primer thread
C1.join(); 10000 <
C2.join(); 10000 (
} 15000 ( segundo thread
catch (Exception e) { 20000 (
http://www.cybercursos.net Lo mismo logramos (y en forma más correcta)
Página 76 declarando como synchronized al método
System.out.println(e); sumar():
} public synchronized void sumar() { .............
} Esto es mejor porque la clase que llama a sumar()
} no necesita saber que tiene que sincronizar el
class Contador extends Thread { objeto antes
Sumador s; de llamar al método, y si otros objetos (en otros
Contador (Sumador sumador) { threads) lo llaman, no necesitamos preocuparnos.
s = sumador; // le asigno un sumador a usar Más sincronización
} Otra manera de sincronizar el acceso de los
public void run() { threads a los métodos, es lograr que éstos se
s.sumar(); // ejecuto la suma pongan de acuerdo
} entre sí, esperando uno hasta que otro realizó
} alguna tarea dada. Para esto se usan los métodos
class Sumador { wait() y
int a = 0; notify(). Cuando un thread llama a wait() en un
public void sumar() { método de un objeto dado, queda detenido hasta
for (int i=0; i<10000; i++ ) { que otro
if ( (i % 5000) == 0 ) { // "%" da el resto de la thread llame a notify() en algún método del
división: mismo objeto.
System.out.println(a); // imprimo cada 5000 Por ejemplo, vamos a suponer cuatro empleados
} que se encuentran con su jefe y lo saludan, pero
a += 1; sólo luego
} de que éste los salude primero.
System.out.println(a); // imprimo el final public class Ejemplo23 {
} public static void main(String argv[]) {
} Saludo hola = new Saludo();
Ejecutando esto nos da más o menos así (cada Personal pablo = new Personal(hola, "Pablo",
corrida es diferente, dependiendo de cómo se false);
"chocan" los Personal luis = new Personal(hola, "Luis", false);
threads y la carga de la CPU): Personal andrea = new Personal(hola, "Andrea",
C:\java\curso>java Ejemplo22 false);
0 Personal pedro = new Personal(hola, "Pedro",
87 false);
8926 Personal jefe = new Personal(hola, "JEFE", true);
10434 pablo.start();
14159 luis.start();
17855 andrea.start();
Esto se debe justamente a lo que explicábamos al pedro.start();
principio: a veces los dos threads intentan jefe.start();
ejecutar a += 1 try {
simultáneamente, con lo que algunos incrementos pablo.join();
se pierden. luis.join();
Podemos solucionar esto modificando el método andrea.join();
run(): pedro.join();
public void run() { jefe.join();
synchronized (s) { }
s.sumar(); catch (Exception e) {
} System.out.println(e);
} }
http://www.cybercursos.net }
Página 77 }
Con esto, sólo a uno de los dos threads se les http://www.cybercursos.net
permite ejecutar s.sumar() por vez, y se evita el Página 78
problema. Por class Saludo {
supuesto, el otro thread queda esperando, por lo synchronized void esperarJefe(String empleado) {
que más vale no utilizar esto con métodos muy try {
largos ya que wait();
el programa se puede poner lento o aún System.out.println(empleado+"> Buenos dias
bloquearse. jefe!");
La salida ahora será: }
catch (InterruptedException e) { if (esJefe) {
System.out.println(e.toString()); while (llegaron < 4) {
} System.out.println("(Esperando...)");
} }
synchronized void saludoJefe() { saludo.saludoJefe();
System.out.println("JEFE> Buenos dias!"); }
notifyAll(); else {
} synchronized(this) {
} llegaron++;
class Personal extends Thread { }
String nombre; saludo.esperarJefe(nombre);
Saludo saludo; }
boolean esJefe; }
Personal (Saludo s, String n, boolean j) { }
nombre = n; Preparamos una variable static (de clase) para
saludo = s; contar todos los empleados que pasaron por aquí;
esJefe = j; la
} incrementamos justo antes de ejecutar
public void run() { saludo.esperarJefe (sincronizando el thread en el
System.out.println("("+nombre+" llega)"); incremento para
if (esJefe) que no pasen los problemas que vimos en el
saludo.saludoJefe(); capítulo).
else En el caso del jefe, simplemente espera que el
saludo.esperarJefe(nombre); contador llegue a 4. Podríamos modificar esto un
} poco,
} pasando la cantidad de empleados como
Usé notifyAll() en lugar de notify(), porque en el parámetro para que sea más flexible.
segundo caso sólo se notificaría al primer thread Inclusive, podemos usar dos constructores
(el primer distintos (uno para los empleados y otro para el
empleado en llegar) y no a los demás, que se jefe, y en este
quedarían en el wait(). último caso pasamos la cantidad de empleados a
Como se ve en la salida, a pesar de que los esperar). Les dejo el ejemplo para que lo
empleados están en condiciones de saludar, no lo estudien.
hacen hasta que public class Ejemplo23 {
no llega el jefe: public static void main(String argv[]) {
C:\java\curso>java Ejemplo23 Saludo hola = new Saludo();
(Pablo llega) Personal jefe = new Personal(hola, "JEFE", 3);
(Luis llega) Personal pablo = new Personal(hola, "Pablo");
(Andrea llega) Personal luis = new Personal(hola, "Luis");
(Pedro llega) Personal andrea = new Personal(hola, "Andrea");
(JEFE llega) jefe.start();
JEFE> Buenos dias! pablo.start();
Luis> Buenos dias jefe! luis.start();
http://www.cybercursos.net andrea.start();
Página 79 try {
Pedro> Buenos dias jefe! pablo.join();
Andrea> Buenos dias jefe! luis.join();
Pablo> Buenos dias jefe! http://www.cybercursos.net
Aquí hice trampa: a veces, el jefe llega y saluda Página 81
antes que alguno de los empleados, por lo que andrea.join();
ese empleado jefe.join();
se queda esperando indefinidamente. Prueben de }
modificar las clases para que el jefe no salude catch (Exception e) {
hasta que no System.out.println(e);
estén todos los empleados presentes... }
http://www.cybercursos.net }
Página 80 }
Capítulo XV - Solución al problema class Saludo {
propuesto synchronized void esperarJefe(String empleado) {
En forma muy sencilla, modificando sólo la clase try {
Personal, podemos solucionar el problema de que wait();
el jefe System.out.println(empleado+"> Buenos dias
llegue antes que un empleado: jefe!");
class Personal extends Thread { }
................ catch (InterruptedException e) {
static int llegaron = 0; System.out.println(e.toString());
................ }
public void run() { }
System.out.println("("+nombre+" llega)"); synchronized void saludoJefe() {
System.out.println("JEFE> Buenos dias!"); Por otra parte, una foto puede cargarse mediante
notifyAll(); un objeto de clase Image mediante el método
} getImage(URL, archivo). Luego la mostramos en
} un objeto Graphics correspondiente al applet (o al
class Personal extends Thread { área de
String nombre; dibujo) mediante drawImage(imagen, x, y,
Saludo saludo; observador). Observador es un objeto que
boolean esJefe; implementa la
static int llegaron = 0; interface ImageObserver; los applets, por
int numEmp; descender de Component (que implementa dicha
Personal (Saludo s, String n) { interface) también
esJefe = false; la implementan. Típicamente, la imagen se
nombre = n; visualiza en el método paint(...) del applet:
saludo = s; .............
} algunMetodo(...) {
Personal (Saludo s, String n, int x) { ..........
esJefe = true; Image imagen = getImage(getDocumentBase(),
nombre = n; "imagen.gif");
saludo = s; ..........
numEmp = x; }
} ...........
public void run() { public void paint(Graphics g) {
System.out.println("("+nombre+" llega)"); g.drawImage(imagen, xOffset, yOffset, this); //
if (esJefe) { "this" representa al applet
while (llegaron < numEmp) }
{ System.out.println("(Esperando...)"); } ...............
saludo.saludoJefe(); El problema con las imágenes es asegurarse que
http://www.cybercursos.net fue cargada antes de mostrarla. Para eso se
Página 82 utiliza un
} MediaTracker (también debería servir para los
else { archivos de audio, pero en esta versión aún no
synchronized(this) { llegaron++; } está
saludo.esperarJefe(nombre); implementado).
} Mediante addImage( imagen, grupo) se agrega
} una imagen a la lista del MediaTracker, y hay
} métodos
http://www.cybercursos.net para esperar que sea cargada (como waitForAll()
Página 83 o waitForID(grupo)), para verificar que se haya
Multimedia! cargado
Java permite cargar y visualizar archivos GIF o correctamente (como checkAll(), checkID(grupo),
JPEG de imagen y AU de audio (solamente en isErrorAny()...), etcétera.
mono, 8 bits, El siguiente applet utiliza estos conceptos para
8000Hz de muestreo). cargar una imagen y un archivo de audio y
Para el caso del sonido, un archivo de audio se mostrarlos:
carga mediante un objeto de la clase AudioClip, // Ejemplo24.java
mediante el import java.awt.*;
método getAudioClip(URL, archivo), se ejecuta import java.applet.*;
con los métodos play() o loop() y se detiene con public class Ejemplo24 extends Applet {
stop(). MediaTracker supervisor;
Noten esto! Si bien dijimos que un applet no String archImagen, archAudio;
puede acceder al disco de la máquina cliente, SI Image imagen;
puede leer http://www.cybercursos.net
archivos del server desde donde se cargó. Por lo Página 84
tanto, pasándole el URL de la máquina desde AudioClip audio;
donde se cargó Label titulo;
el applet, podemos leer cualquier tipo de archivo Panel cuadro;
a través de la red. public void init() {
La forma más segura de indicar dicho URL es supervisor = new MediaTracker(this);
mediante el método getDocumentBase(), que nos archImagen = "javacero.gif";
da el URL archAudio = "tada.au";
adecuado. // carga imagen
Por ejemplo, puedo cargar y reproducir audio con imagen = getImage(getDocumentBase(),
sólo dos líneas: archImagen);
........ supervisor.addImage(imagen,0);
AudioClip sonido = getAudioClip( try {
getDocumentBase(), "sonido.au" ); supervisor.waitForID(0); // espero que
sonido.play(); se cargue
......... }
catch (InterruptedException e) {
System.out.println("Error cargando imagen!"); De esta manera podemos pasar cualquier valor
} como parámetro para un applet, haciéndolo más
showStatus("Imagen cargada"); flexible.
// carga sonido Y esto es todo por hoy!
audio = getAudioClip(getDocumentBase(), Con esto hemos visto una gran parte de lo que es
archAudio); Java. No hemos profundizado demasiado en cada
// arma layout punto,
setLayout(new BorderLayout()); pero hemos hecho ejemplos que funcionan para
titulo = new Label(archImagen); ilustrar cada cosa.
setFont(new Font("helvetica", Font.BOLD, 18)); Sin embargo, hemos dejado un punto importante
add("South", titulo); y muy fuerte de Java, que es el de las
} comunicaciones entre
public void start() { aplicaciones y, especialmente, el uso de sockets y
repaint(); la programación de aplicaciones cliente-servidor.
audio.play(); http://www.cybercursos.net
} Página 86
public void paint(Graphics g) { Paseando por la Red
if (supervisor.isErrorAny()) { Es muy sencillo acceder a archivos en la red
g.setColor(Color.black); utilizando Java. El paquete java.net dispone de
g.fillRect(0, 0, size().width, size().height); varias clases e
return; interfases a tal efecto.
} En primer lugar, la clase URL nos permite definir
g.drawImage(imagen, 0, 0, this); un recurso en la red de varias maneras, por
} ejemplo:
} URL url1 = new URL
Para visualizarlo, como siempre, creamos un ("http://www.rockar.com.ar/index.html");
HTML: URL url2 = new URL ("http", "www.rockar.com.ar",
<HTML> "sbits.htm");
<HEAD> Por otra parte, podemos establecer una conexión
<TITLE>Ejemplo 24 - Ejemplo a un URL dado mediante openConnection:
Multimedia</TITLE> URLConnection conexion = url.openConnection();
</HEAD> Una vez lograda la conexión, podemos leer y
<BODY> escribir datos utilizando streams (corrientes de
http://www.cybercursos.net datos), como en
Página 85 el caso de manejo de archivos comunes (ver
<applet code="Ejemplo24.class" width=150 capítulo X). Un DataInputStream nos permite leer
height=200> datos que
</applet> llegan a través de la red, y un DataOutputStream
</BODY> nos permite enviar datos al host.
</HTML> Por ejemplo:
Parametrizando un applet DataInputStream datos = new DataInputStream(
Vamos a aprovechar este ejemplo, modificándolo corrienteEntrada );
un poco para indicarle desde el HTML qué En nuestro caso, la corriente de entrada de datos
archivos debe proviene de la conexión al URL. El método
cargar, mediante parámetros. Nuestro HTML getInputStream() del objeto URLConnection nos
modificado será: provee tal corriente:
<HTML> DataInputStream datos = new
<HEAD> DataInputStream(conex.getInputStream())
<TITLE>Ejemplo 24 - Multimedia</TITLE> De este modo podemos escribir un pequeño
</HEAD> programa para, por ejemplo, leer una página
<BODY> HTML desde una
<applet code="Ejemplo24.class" width=150 dirección arbitraria de internet. El programa,
height=200> luego de compilarse mediante javac
<param name="imagen" value="javacero.gif"> Ejemplo25.java, se ejecuta
<param name="sonido" value="tada.au"> con java Ejemplo25 <url>; por ejemplo: java
</applet> Ejemplo25 http://www.rockar.com.ar/index.html.
</BODY> import java.io.*;
</HTML> import java.net.*;
Para leer estos parámetros desde el applet, public class Ejemplo25 {
usamos el método public static void main(String argv[]) {
getParameter(nombreParámetro), así que String s;
podemos modificar nuestro applet simplemente try {
modificando un par de líneas: URL url = new URL (argv[0]);
archImagen = getParameter("imagen"); URLConnection conex = url.openConnection();
archAudio = getParameter("sonido"); System.out.println("Cargando "+argv[0]);
Voilá! Pueden probar de cargar este applet en DataInputStream datos = new
http://www.amarillas.com/rock/java/Ejemplo24.ht DataInputStream(conex.getInputStream());
m. do {
s = datos.readLine();
if (s != null) System.out.println(s); disponibles para establecer conexiones
} while (s != null); especiales.
} Justamente, una de las formas de crear un objeto
catch (ArrayIndexOutOfBoundsException e) { de la clase URL permite especificar también el
System.out.println("Sintaxis: java Ejemplo25 puerto:
<url>"); URL url3 = new URL ("http", "www.rockar.com.ar",
} 80,"sbits.htm");
catch (UnknownHostException e) { Para establecer una conexión a través de un
http://www.cybercursos.net socket, tenemos que programar por un lado el
Página 87 servidor y por otro
System.out.println("El host no existe o no los clientes.
responde"); En el servidor, creamos un objeto de la clase
} ServerSocket y luego esperamos algún cliente (de
catch (Exception e) { clase
e.printStackTrace(); Socket) mediante el método accept():
} ServerSocket conexion = new
} ServerSocket(5000); // 5000 es el puerto en este
} caso
Este programa muestra el HTML como texto en la Socket cliente = conexion.accept(); // espero al
pantalla, pero podríamos grabarlo a un archivo cliente
para Desde el punto de vista del cliente, necesitamos
guardarlo. Inclusive, podríamos procesarlo a un Socket al que le indiquemos la dirección del
medida que lo recibimos, identificar los tags <A servidor y el
HREF=url>, número de puerto a usar:
guardarlos en un vector, y seguidamente Socket conexion = new Socket ( direccion, 5000 );
conectarnos y bajar los links que figuran en la http://www.cybercursos.net
página original hasta Página 88
bajar un site completo. Una vez establecida la conexión, podemos
Noten que esto no sólo sirve para establecer intercambiar datos usando streams como en el
conexiones a páginas HTML. En realidad, un URL ejemplo anterior.
puede Como la clase URLConnection, la clase Socket
referirse también a otros protocolos, como dispone de métodos getInputStream y
gopher, ftp, etcétera; si bien según la getOutputStream
implementación de Java que nos dan respectivamente un InputStream y
puede haber problemas para conectarse a un OutputStream a través de los cuales transferir
algunos tipos de URL. los datos.
Para ver los tipos de URL posibles les recomiendo Un servidor atento
leer la página: Vamos a crear un servidor Ejemplo26a.java (que
http://www.ncsa.uiuc.edu/demoweb/url- podemos correr en una ventana) que atenderá a
primer.html un cliente
Los Sockets de la misma máquina (lo vamos a correr en otra
Los sockets (zócalos, referido a los enchufes de ventana). Para hacerlo simple, el servidor sólo le
conexión de cables) son mecanismos de enviará un
comunicación entre mensaje al cliente y éste terminará la conexión. El
programas a través de una red TCP/IP. De hecho, servidor quedará entonces disponible para otro
al establecer una conexión via Internet estamos cliente.
utilizando Es importante notar que, para que el socket
sockets: los sockets realizan la interfase entre la funcione, los servicios TCP/IP deben estar activos
aplicación y el protocolo TCP/IP. (aunque ambos
Dichos mecanismos pueden tener lugar dentro de programas corran en la misma máquina). Los
la misma máquina o a través de una red. Se usan usuarios de Windows asegúrense que haya una
en forma conexión
cliente-servidor: cuando un cliente y un servidor TCP/IP activa, ya sea a una red local o a Internet.
establecen una conexión, lo hacen a través de un El servidor correrá "para siempre", así que para
socket. Java detenerlo presionen control-C.
proporciona para esto las clases ServerSocket y // servidor
Socket. import java.io.*;
Los sockets tienen asociado un port (puerto). En import java.net.*;
general, las conexiones via internet pueden public class Ejemplo26a {
establecer un public static void main(String argv[]) {
puerto particular (por ejemplo, en ServerSocket servidor;
http://www.rockar.com.ar:80/index.html el puerto Socket cliente;
es el 80). Esto casi nunca int numCliente = 0;
se especifica porque ya hay definidos puertos por try {
defecto para distintos protocolos: 20 para ftp- servidor = new ServerSocket(5000);
data, 21 para do {
ftp, 79 para finger, etc. Algunos servers pueden numCliente++;
definir otros puertos, e inclusive pueden utilizarse cliente = servidor.accept();
puertos
System.out.println("Llega el cliente C:\java\curso>java Ejemplo26b
"+numCliente); Usted es mi cliente 2
PrintStream ps = new C:\java\curso>java Ejemplo26b
PrintStream(cliente.getOutputStream()); Usted es mi cliente 3
ps.println("Usted es mi cliente "+numCliente); (----- aquí cerramos el servidor -----)
cliente.close(); C:\java\curso>java Ejemplo26b
} while (true); java.net.SocketException: connect
} at
catch (Exception e) { java.net.PlainSocketImpl.doConnect(PlainSocketI
e.printStackTrace(); mpl.java:223)
} at
} java.net.PlainSocketImpl.connectToAddress(PlainS
} ocketImpl.java:128)
Utilizamos un PrintStream para enviar los datos al at
cliente, ya que es sencillo de utilizar para mandar java.net.PlainSocketImpl.connect(PlainSocketImpl.
Strings. java:115)
El método PrintStream.println maneja los datos at java.net.Socket.<init>(Socket.java:125)
como System.out.println, simplemente hay que at java.net.Socket.<init>(Socket.java:101)
indicarle el at Ejemplo26b.main(Ejemplo26b.java:12)
stream a través del cual mandarlos al crearlo (en
este caso, el OutputStream del cliente, que
obtenemos con
cliente.getOutputStream()).
El cliente satisfecho
Ahora vamos a crear la clase cliente,
Ejemplo26b.java. El cliente simplemente
establece la conexión, lee a
través de un DataInputStream (mediante el
método readLine()) lo que el servidor le manda, lo
muestra y
corta.
// cliente:
import java.io.*;
import java.net.*;
http://www.cybercursos.net
Página 89
public class Ejemplo26b {
public static void main(String argv[]) {
InetAddress direccion;
Socket servidor;
int numCliente = 0;
try {
direccion = InetAddress.getLocalHost(); //
direccion local
servidor = new Socket(direccion, 5000);
DataInputStream datos =
new DataInputStream(servidor.getInputStream());
System.out.println( datos.readLine() );
servidor.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Para probar esto, asegúrense que los servicios
TCP/IP estén activos, corran java Ejemplo26a en
una ventana
y corran varias veces java Ejemplo26b en otra.
Las salidas serán más o menos así:
Ventana servidor:
C:\java\curso>java Ejemplo26a
Llega el cliente 1
Llega el cliente 2
Llega el cliente 3
(----- cortar con control-C -----)
Ventana cliente:
C:\java\curso>java Ejemplo26b
Usted es mi cliente 1

You might also like