5.- Tutoriales mejorados con Java.
Los tutoriales elaborados con las herramientas descritas hasta el capítulo
anterior tienen limitaciones sobre todo en lo referente a la interacción
humano-máquina. Además, el usuario dispone en todo momento
del código fuente de los guiones escritos en HTML y en JavaScript.
Si el usuario de los tutoriales necesita una mayor interacción con
la máquina y el programador decide mantener la privacidad de su
código fuente, el lenguaje de programación Java es una buena
alternativa. En este capítulo se revisarán las características
más relevantes del lenguaje Java, enfatizando en aquellas que nos
ayuden a elaborar tutoriales que permitan una mayor interacción
con el usuario.
5.1.- Características del lenguaje Java.
Java es un lenguaje de programación orientado a objetos que,
entre otras, tiene las características de ser :
Simple
Comparado con C++, Java es más simple[VANHA96] , puesto que no
tiene :
Orientado a Objetos
Con la excepción de los tipos simples tales como números
y booleanos, la mayoría de las cosas en Java son objetos. El código
de Java está organizado en clases y solo maneja herencia sencilla.
En la raíz de la jerarquía de clases siempre está
la clase Object [VANHA96].
Distribuido
Java está diseñado para soportar aplicaciones en redes, por lo que se dice que es un lenguaje distribuido. Soporta varios niveles de conectividad por medio de las clases contenidas en el paquete java.net.
Compilado e interpretado
Aunque puede parecer contradictorio, Java puede clasificarse como un
lenguaje compilado y también como uno interpretado. Esto se debe
a que, en una primera fase, el código fuente se compila para producir
código de bytes ( bytecode , un código muy cercano
al código de máquina), el cual puede transportarse sin ningún
problema a computadoras de diversas arquitecturas. Una vez que se encuentra
en la máquina cliente, el código de bytes es traducido y
ejecutado por un programa intérprete denominado Máquina
Virtual de Java .
Robusto
Java elimina ciertos tipos de errores de programación, por lo que facilita la escritura de programas confiables. También es un lenguaje muy estricto con los tipos, lo que permite revisiones extensas en tiempo de compilación para encontrar problemas potenciales de mezcla de tipos. Por otra parte, al no soportar apuntadores elimina la posibilidad de sobre escribir la memoria y corromper los datos ; la asignación dinámica de memoria la realiza por medio de los métodos constructores de objetos, y los bloques de memoria desocupados son recuperados por su recolector automático de basura.
El manejo de excepciones es otra característica de Java
que permite la creación de programas más robustos. Una excepción
es una señal para alguna clase de condición excepcional,
tal como un error [FLAND96].
Seguro
Los visualizadores utilizan la verificación del código
de bytes para asegurar que los programas no contienen virus.
Independiente de la arquitectura
Puesto que Java está diseñado para crear aplicaciones
basadas en Internet, el código de bytes de éstas debe poder
ejecutarse en cualquiera de las computadoras que conforman la red. No importa
la clase de máquina en que se compiló el código y
no importa la clase de máquina donde va a ejecutarse. La única
condición es que la máquina en que va a ejecutarse el código
cuente con una Máquina Virtual de Java [FLAND96].
Multi-hilos
Java provee soporte para múltiples hilos de ejecución
que pueden manejar diferentes tareas ; de manera tal que una aplicación
puede estar reproduciendo un archivo de sonido y desplegando una página,
mientras que en el fondo se carga un archivo de imagen [FLAND96].
Dinámicamente extensible
Es posible construir interfaces de Java para bibliotecas que fueron escritas en otros lenguajes. Esto es muy fácil puesto que las estructuras de datos escritas en Java son muy similares a los tipos de datos y estructuras de C. El gran problema es que la mayoría de las bibliotecas existentes no son multi-hilos.
Un programa Java puede declarar ciertos métodos para que sean
native ( nativo ). Después, estos métodos nativos
se mapean a funciones definidas en bibliotecas de programas que se enlazan
dinámicamente a la máquina virtual. Por razones de seguridad,
usualmente esta característica se desactiva para programas que se
cargan a través de la red [VANHA96].
5.2.- Estructura del lenguaje Java.
En esta sección se describirá la forma en que Java maneja
los tipos de datos, los operadores, la sintaxis para las expresiones, y
la sintaxis para cada una de las estructuras de control.
5.2.1.- Tipos de datos.
Java cuenta con ocho tipos de datos primitivos para números enteros,
números de punto flotante, caracteres, y valores booleanos.
5.2.1.1.- Números enteros.
En Java se manejan cuatro tipos de datos enteros. Todos ellos pueden
ser positivos o negativos, como se observa en la Tabla 5.1.
|
Tipo |
Tamaño en bits |
Rango de valores permitidos |
| byte |
8 |
-128 a 127 |
| short |
16 |
-32,768 a 32,767 |
| int |
32 |
-2,147,483,648 a 2,147483,647 |
| long |
64 |
-9,223,372,036,854,775,808 a 9,223,372,036,854,775,807 |
Tabla 5.1.- Tipos de datos enteros.
5.2.1.2.- Números de punto
flotante.
Los números de punto flotante se manejan con un punto decimal
y cumplen con el estándar IEEE754. En Java existen dos tipos de
números de punto flotante :
5.2.1.3.- Caracteres.
Para caracteres individuales, Java utiliza el tipo char, con 16 bits de precisión ( sin signo ) ; suficientes para manejar el conjunto de caracteres Unicode.
Unicode es un estándar que permite dar soporte a caracteres de
diferentes idiomas. Este nuevo estándar se creó para cubrir
las deficiencias del código ASCII en el manejo de caracteres pertenecientes
a idiomas distintos del Inglés.
5.2.1.4.- Booleanos.
Los únicos valores permitidos para el tipo boolean son :
true ( verdadero ) y
false ( falso ).
Además de los ocho tipos primitivos ( simples) descritos anteriormente,
en Java se pueden manejar nuevos tipos (simples o compuestos ) por medio
de la creación de nuevas clases ( tal como las clases predefinidas
String, Font y OvalShape ).
5.2.1.5.- Tipos de datos de referencia.
Los tipos de datos no primitivos en Java son los objetos y los arreglos. Frecuentemente, estos tipos no primitivos son denominados "tipos de referencia" porque se manejan por referencia y no por valor como se hace con los tipos primitivos. En el manejo por referencia, las variables almacenan direcciones de memoria y no valores literales.
En los casos de las asignaciones y del paso de argumentos en los métodos,
los tipos primitivos son pasados por valor ; los objetos y los arreglos
son pasados por referencia.
Ejemplo 1 :
int i = 10 ; // La variable i almacena el valor 10
.
int j = i ; // Se asigna a la variable j el valor
almacenado actualmente en i ( 10 ).
i = 20 ; // Ahora i almacena el valor 20 ,
y j almacena el valor 10 .
Ejemplo 2 :
Button x,y ; // Crea las referencias x , y
a objetos de la clase Button.
x = new Button() ; // Crea un nuevo objeto con el constructor de la clase Button ;
// la dirección del objeto se asigna a la variable x.
y = x ; // A la variable y se le asigna la misma dirección que tiene
// almacenada la variable x ( La variable y se referirá al mismo
// objeto que x ) .
x.setLabel("Correcto !") ; // Se modifica
el objeto a través de x.
String z = y.getLabel( ) ; // La variable z tendrá
el valor : Correcto !.
En Java no es necesario destruir los objetos que ya no se utilizan, puesto que el lenguaje usa una técnica denominada recolección de basura que detecta automáticamente los objetos que ya no están utilizándose. Un objeto ya no se utiliza cuando dejan de existir referencias a él.
5.2.1.6.- Arreglos.
Un arreglo está formado por un conjunto de elementos del mismo
tipo que utilizan el mismo nombre pero que se distinguen entre ellos por
medio de un índice.
A diferencia de otros lenguajes, Java maneja los arreglos como objetos.
Por esta razón, los siguientes conceptos aplicados a los tipos de
referencia y a los objetos también los aplica a los arreglos.
5.2.1.6.1.- Creación de arreglos.
Existen dos formas para crear arreglos en Java.
La primera utiliza la palabra clave new, y especifica el número
de elementos que van a existir en el arreglo.
Ejemplo :
int arregloDeCincoEnteros[ ] = new int[5] ;
Los elementos de los arreglos creados de esta manera se inicializan
al valor predeterminado para su tipo. Por ejemplo, los elementos de tipo
int se inicializan a 0 y los objetos se inicializan a
null.
La segunda forma de crear un arreglo es inicializando estáticamente
los valores de los elementos.
Ejemplo :
int arregloDeCincoEnteros[ ] = { 10, 15, 20, 25, 30 } ;
En este caso se crea dinámicamente un arreglo y se inicializan
sus elementos a los valores especificados.
5.2.1.6.2.- Acceso a los elementos de un arreglo.
Para acceder a los elementos de un arreglo, deben especificarse el nombre
del arreglo y el índice que identifica al elemento.
Los índices son números enteros que representan la posición
de cada elemento en el arreglo. Al índice del primer elemento en
el arreglo le corresponde el valor cero.
Así, del ejemplo anterior tenemos que :
arregloDeCincoEnteros[0] tiene un valor igual a 10,
arregloDeCincoEnteros[1] tiene un valor igual a 15,
arregloDeCincoEnteros[2] tiene un valor igual a 20,
arregloDeCincoEnteros[3] tiene un valor igual a 25 y
arregloDeCincoEnteros[4] tiene un valor igual a 30.
Observe que el valor del índice del último elemento es
igual al tamaño del arreglo menos uno ( esto se debe a que la numeración
empieza en cero ).
5.2.1.6.3.- Arreglos multidimensionales.
Si definimos un arreglo multidimensional como aquel que permite el uso
de varios índices ( donde cada índice representa una dimensión)
, entonces podemos afirmar que Java soporta arreglos multidimensionales
y que los implementa con arreglos de arreglos, de manera similar a como
lo hacen otros lenguajes.
Ejemplo :
byte arregloBidimensional[][] = new byte [20][30] ;
Esto crea un arreglo de arreglos de bytes, al reservar dinámicamente
( en tiempo de ejecución) 600 bytes de memoria ( en el montículo
- heap - de la memoria RAM ). La dirección de ese bloque de memoria
se asigna a la variable arregloBidimensional.
No es necesario especificar los valores de cada una de los índices
de un arreglo, por lo que podemos escribir el ejemplo anterior como :
byte arregloBidimensional[][] = new byte [600][] ;
Lo importante es que se especifique el total de elementos y señalar las dimensiones necesarias.
La regla para este caso es que las primeras n dimensiones ( n > = 1 ) deben tener el número de elementos especificados, y que esas dimensiones deben estar seguidas por m dimensiones adicionales, las cuales no tendrán valores específicos.
De acuerdo a esta regla, es correcto escribir :
String quinceCadenas[][][][] = new String[5][3][][] ;
pero no :
String quinceCadenas[][][][] = new String[5][][][3] ;
5.2.2.- Comentarios.
Java maneja tres tipos de comentarios :
1.- El que puede abarcar más de una línea, y que empieza con /* y termina con */ , como se muestra en el siguiente ejemplo :
/* Este es un comentario que
abarca varias líneas y sirve
para documentar de manera extensa
un programa */
2.- El que abarca parte de o toda una línea. Empieza con dos diagonales // y finaliza con un carácter de retorno de carro ( enter ).
Ejemplos :
// Este es un comentario de una línea completa.
x = 10 ; // Comentario que abarca parte de una línea.
3.- El comentario que empieza con /** y finaliza con */ es considerado por el compilador exactamente igual que el primer tipo. La diferencia consiste en que puede ser utilizado por javadoc para documentar aplicaciones.
5.2.3.- Literales.
Las literales son valores que se toman tal cual, que utilizan su propio
espacio de memoria y que no están asociados a un identificador.
Generalmente se usan para asignarlas a variables de tipos específicos
o para utilizarlas como parámetros al invocar ciertos métodos.
Podemos clasificar a las literales en los siguientes grupos :
5.2.3.1.- Literales numéricas.
Las literales numéricas consisten de números enteros o
de punto flotante, por lo que las podemos agrupar como :
5.2.3.1.1.- Literales enteras.
Consisten de valores sin punto decimal, los cuales pueden presentarse
en los siguientes formatos :
Decimal ( base 10 ).
Las literales se presentan como grupos de dígitos del 0 al 9, que no empiecen con 0.
Ejemplos : 33, 100 , 1997
Octal ( base 8 ).
Las literales se presentan como grupos de dígitos del 0 al 7, empezando con cero.
Por ejemplo, los números en decimal : 33, 100 y 1997 en
octal se representan como : 041, 0144 y 03715 , respectivamente.
Hexadecimal ( base 16 ).
Las literales se presentan como grupos formados por los dígitos del 0 al 9 y por las letras de la A a la F ( o sus correspondientes minúsculas ), y deben empezar con 0x o con 0X.
Por ejemplo, los números en decimal : 33, 100 y 1997
en hexadecimal se representan como : 0x21, 0x64 y 7CD, respectivamente.
Las literales enteras ocupan, de manera predeterminada, un espacio de
32 bits (que corresponde al tipo int). Puede indicarse un espacio de 64
bits agregando una letra L ( o su correspondiente minúscula).
Por ejemplo :
La literal 100 se almacena en 32 bits, pero la literal 100L se almacena en 64 bits.
5.2.3.1.2.- Literales de punto flotante.
Pueden escribirse en dos formatos : con punto decimal explícito
o en notación exponencial.
Por ejemplo, la literal con punto decimal explícito : 3.1416
puede representarse, en formato exponencial, como : 31416E-4,
lo cual significa " 31416 multiplicado por :10 elevado a la menos 4" ,
esto es : 31416 x 10 -4 (recordando que 10 -4 =
0.0001)
Las literales de punto flotante ocupan un espacio de 64 bits ( correspondiente al tipo
double ), pero pueden forzarse a ocupar un espacio de 32 bits ( tipo float )
agregándoles la letra F ( o su correspondiente minúscula
).
Por ejemplo : -25.6 se almacena en 64 bits, pero -25.6F se almacena en 32 bits.
5.2.3.2.- Literales booleanas.
Pueden consistir solamente de las palabras clave true o false.
5.2.3.3.- Literales de caracteres.
Equivalen a un solo carácter y se representan entre comillas sencillas, como por
ejemplo 'a', '$', '\n'. Se almacenan como caracteres Unicode ( en espacios de 16 bits).
Pueden representar explícitamente un carácter o su correspondiente valor en código
en formato octal, hexadecima o Unicode. Los caracteres no imprimibles o los valores
de los caracteres en Unicode se representan por medio de secuencias de escape, como
se muestra en la tabla 5.2.
|
Secuencia |
Significado |
| \n | Nueva línea |
| \t | Tabulador horizontal |
| \b | Retroceso |
| \r | Retorno de carro |
| \f | Alimentación de forma |
| \\ | Diagonal inversa |
| \' | Comilla sencilla |
| \" | Comillas dobles |
| \ddd | Valor en Octal |
| \xdd | Valor en Hexadecimal |
| \udddd | Unicode |
Tabla 5.2.- Secuencias de escape.
Las letras d en las secuencias de la Tabla 5.2 representan dígitos,
de acuerdo al formato indicado.
5.2.3.4.- Literales de cadena.
Aunque una cadena es una secuencia de caracteres, en Java no se manejan
como simples arreglos de caracteres sino como instancias de la clase
String. Puesto que las cadenas son objetos, puede determinarse su longitud
y tener acceso a cualquiera de los caracteres individuales que las forman.
Las literales de cadena consisten de series de caracteres ( inclusive
secuencias de escape ) encerradas entre comillas dobles.
Ejemplos :
"Esta es una literal de cadena"
" " // <= Ahí está una cadena vacía
"Literal de cadena con \n una nueva línea incluida "
5.2.3.4.- Literal nula.
Existe una literal para indicar la inexistencia de valor y se representa
por medio de la palabra reservada null. Esta literal no debe confundirse
con el valor cero que pertenece a los tipos numéricos ; tampoco
debe confundirse con la cadena vacía " ".
5.2.4.- Expresiones.
Las expresiones son mezclas de operandos y operadores. Cuando se evalúa una expresión, se obtiene un resultado.
Un operando es un dato que puede estar almacenado en una variable, en
una constante, o en una literal.
5.2.5.- Operadores.
Los operadores en Java son los mismos que se utilizan en JavaScript
y se describieron en la sección 4.2.1.3.
5.2.6.- Variables.
Una variable es un espacio de memoria que tiene asociados un nombre
y un tipo de dato. Además, almacena un valor que puede
cambiar durante el tiempo de ejecución.
5.2.6.1.- Declaración de variables.
Toda variable deberá declararse antes de utilizarla en cualquier instrucción. En la declaración, se establece el nombre de la variable, su tipo y, de manera opcional, su valor inicial.
La sintaxis básica para la declaración de variables es :
tipo nombre ;
Donde :
tipo es cualquiera de los descritos anteriormente,
nombre es un identificador de variable.
Ejemplo :
float impuesto ;
De manera opcional, se puede declarar más de una variable en
una instrucción, separando los nombres por medio de comas.
Ejemplo :
float impuesto, sueldo, servicios ;
De igual manera, es posible asignar valores iniciales a una o a todas
las variables en la declaración .
Ejemplo :
float impuesto = 10 , sueldo = 20, servicios ;
El tipo de una variable puede ser :
Más adelante se tratará lo referente a interfaces y arreglos.
Los nombres ( llamados también identificadores ), deben
sujetarse a las siguientes restricciones :
Existe un conjunto de palabras reservadas ( mostrado en la tabla
5.7 ) que no pueden utilizarse como nombres de variables, ya que tienen
un uso predeterminado.
| abstract | boolean | break | byte | byvalue | case | cast |
| catch | char | class | const | continue | default | do |
| double | else | extends | false | final | finally | float |
| for | future | generic | goto | if | implements | import |
| inner | instanceof | int | interface | long | native | new |
| null | operator | outer | package | private | protected | public |
| rest | return | short | static | super | switch | synchronized |
| this | throw | throws | transient | true | try | var |
| void | volatile | while |
Tabla 5.7.- Palabras reservadas en Java.
Existe un grupo de nombres de métodos de la clase Object
que, aunque no pertenecen a la categoría de palabras reservadas,
conviene no utilizarlos como nombres de variables. La Tabla 5.8 presenta
esos nombres.
| clone | getClass | notifyAll |
| equals | hashCode | toString |
| finalize | notify | wait |
Tabla 5.8.- Nombres de métodos definidos en la clase Object.
5.2.7.- Constantes.
Una constante representa un espacio de memoria que tiene asociados :
un nombre o identificador, y un valor que no puede
modificarse en tiempo de ejecución.
Aunque Java reserva la palabra const, hasta la fecha no la utiliza para la definición de constantes. Para definir constantes, deben utilizarse las palabras static y final precediendo al nombre de la constante.
Las palabras static y final se conocen como modificadores
y se describen más adelante en las tablas 5.9 y 5.10 . El orden
en que se escriban no es relevante, pudiéndose escribir :
final static tipo nombreConstante = literal ;
o
static final tipo nombreConstante = literal ;
Ejemplo :
static final int IVA = 10 ;
Aunque no es una restricción, se acostumbra escribir los nombres
de las constantes solo con letras mayúsculas. Esto permite distinguir
fácilmente entre los nombres de las variables y los de las constantes.
5.2.8.- Estructuras de control.
Las estructuras de control sirven para controlar el orden de ejecución
de las instrucciones que forman un programa . De acuerdo a su funcionamiento,
pueden agruparse en estructuras de secuencia, selección
e iteración.
5.2.8.1.- Estructuras de secuencia.
Las estructuras de secuencia consisten de instrucciones que se ejecutan
una a continuación de otra, sin bifurcaciones ni iteraciones.
5.2.8.2.- Estructuras de selección.
Las estructuras de selección contienen bifurcaciones que se controlan
por medio de la evaluación de una condición. Java cuenta
con dos estructuras de selección : if-else y switch.
5.2.8.2.1.- La estructura if-else.
La estructura de selección if-else se rige por la sintaxis mostrada
a continuación :
if ( condición) bloque 1
else bloque 2
donde :
condición es una expresión booleana,
bloque 1 y bloque 2 son bloques de una o más instrucciones.
La parte else es opcional.
Ejemplo :
if( calificacion >= 70 )
{
estatus = "Acreditada";
}
else
{
estatus = "No acreditada" ;
}
5.2.8.2.2.-La estructura switch.
La estructura switch es útil cuando se tiene que elegir entre
más de dos opciones. En lugar de evaluar una condición que
solo produce valores booleanos, esta estructura evalúa una expresión
que produce valores enteros.
La sintaxis para la estructura switch es :
switch(expresión_entera)
{
case(valor1) : bloque1 ; break ;
case(valor2) : bloque2 ; break ;
... ... ...
case(valorN) : bloqueN ; break ;
default : bloqueX ;
}
En caso de que el resultado de evaluar expresión_entera
coincida con valor1, se ejecuta bloque1 y se interrumpe (
por medio de la orden break ; ) la ejecución de la estructura
switch ; en caso de coincidir con el valor2, se ejecuta el
bloque2 y se interrumpe ; y así sucesivamente. La etiqueta
default : es opcional, pero en caso de existir, y si el valor
de expresión_entera no coincidió con ninguno de los
valores de las opciones, se ejecuta bloqueX.
La instrucción break sirve para romper la ejecución
de la estructura y pasar al siguiente nivel exterior de anidamiento. En
caso de omitirse, se ejecutan todos los bloques de instrucciones que siguen
a la opción coincidente, hasta encontrar una instrucción
break o el fin de la estructura switch.
El tipo de valor1, valor2, ... y valorN
puede ser byte, char, short, int o long.
Ejemplo :
char opera ;
...
switch(opera)
{
case '+' : suma(operando1, operando2) ; break ;
case '-' : resta(operando1, operando2) ; break ;
case '*' : multiplica(operando1, operando2) ; break ;
case '/' : divide(operando1, operando2) ; break ;
default : despliegaError("Operación no permitida...") ;
}
5.2.8.3.- Estructuras de Iteración.
Las estructuras de iteración sirven para ejecutar repetidamente un bloque de instrucciones.
El lenguaje Java ofrece las siguientes estructuras de iteración :
5.2.8.3.1.- La estructura while.
En esta estructura se ejecuta un bloque de instrucciones cero o más
veces, dependiendo del resultado de evaluar una expresión booleana.
Se rige por la siguiente sintaxis :
while(condición)
bloque ;
Primero se evalúa la expresión booleana condición. En caso de que el resultado de dicha evaluación sea true, se ejecuta el bloque y vuelve a evaluarse condición. Cuando el resultado de evaluar condición sea false, se interrumpe la ejecución de bloque y se continúa con la ejecución de las instrucciones que siguen a la estructura.
Debe tenerse la precaución de incluir en bloque una instrucción
para cambiar el valor de al menos una de las variables que intervienen
en condición, de manera tal que el resultado llegue a ser
false y no se caiga en un ciclo infinito.
Ejemplo :
double valor = 1.00, precio = 1000.00 ;
while(valor *precio < precio*3.0 )
{
valor = precio * 1.3 ;
precio *= 1.1 ;
}
5.2.8.3.2.- La estructura do-while.
La estructura do-while ejecuta un bloque de instrucciones y después
evalúa una condición, lo que garantiza que el bloque
de instrucciones se ejecutará al menos una vez, aunque el resultado
de evaluar condición sea false. Se rige por la sintaxis :
do {
bloque ;
}while(condición) ;
Ejemplo :
double valor, precio = 1000.00 ;
do{
valor = precio * 1.3 ;
precio *= 1.1 ;
} while(valor*precio < precio*3.0 ) ;
Observe que en, este ejemplo, no es necesario inicializar la variable valor .
5.2.8.3.3.- La estructura for.
Esta es la estructura de iteración más versatil, puesto
que permite simular una estructura while y también que se declaren
variables en la sección de inicio.
La sintaxis para esta estructura es :
for( inicio ; condición ; actualiza) bloque
donde :
inicio es la parte donde se asignan los valores iniciales a los elementos que sirven
como controladores de ciclos,
condición es una expresión booleana ,
actualiza es la parte donde se incrementan o decrementan los controladores, y
bloque es un conjunto formado por una o más instrucciones a ejecutar en cada
ciclo.
La tarea original para la que fue creada esta estructura es para ejecutar
un bloque durante un número predeterminado de veces como en :
for( int i=0 ; i<10 ; i++)
{
x = 2*i ;
y = x - i ;
}
donde la tarea se ejecuta 10 veces ( con valores para i desde 0 hasta
9 ). Sin embargo, puede comprobarse que el siguiente código arroja
los mismos resultados que el ejemplo de la estructura while mostrado
anteriormente. Se han cambiado los nombres de las variables por sus iniciales
para obligar a que el código aparezca en una sola línea.
A propósito : ¿Cuántas veces se ejecuta el código ?
for( double v = 1.00, p = 1000.00 ; v *p < p*3.0 ; v = p * 1.3 , p *= 1.1 ) ;
5.2.8.3.4.- Ruptura de estructuras de iteración.
Las estructuras de iteración finalizan cuando, al evaluar la
expresión que representa la condición, se obtiene un resultado
igual a false . Sin embargo, es posible romper la ejecución
de estas estructuras antes de que finalicen normalmente. Esto se logra
utilizando la palabra clave break, de la misma manera que en el
caso de la estructura switch.
Ejemplo :
int valorDado, valorBuscado , int valorMinimo = 100 ;
do{
valorBuscado = buscaValor( ) ;
valorDado = daValor( ) ;
if(valorBuscado == valorDado ) break ;
}while(valorBuscado >= valorMinimo) ;
Cuando se tienen estructuras de iteración anidadas, siempre que
se finalice o se rompa la ejecución de una de las más internas,
se continúa la ejecución de la estructura envolvente más
próxima. Para modificar esto, y obligar a que el salto se dé
hacia el exterior de la envolvente más próxima, se utiliza
break con una etiqueta. Una etiqueta es un identificador
al cual se le agrega un símbolo de dos puntos ( : ),
como se muestra en el siguiente ejemplo :
salto:
for(int i = 1 ; i <= 10 ; i++)
for(int j = 1 ; j <= 5 ; j++)
for(int k = 1 ; k <= 3 ; k++)
{
System.out.println( "i = " + i + " ,
j = " + j + " ,
k = " + k ) ;
if( (i + j + k) >= 7 )
break salto ;
}
System.out.println("Fin de las iteraciones....") ;
En este caso, cuando la suma de i+j+k de como resultado 7
se suspenderá la ejecución del las estructuras y se dará
un salto fuera de ellas, hasta el nivel de la etiqueta salto: ,
continuando la ejecución en la última línea para desplegar :
Fin de las iteraciones....
De manera similar a break, la instrucción continue obliga a que una estructura inicie la siguiente iteración desde el principio (omitiendo en esa iteración las instrucciones que siguen a la línea que contiene la palabra continue ). También se pueden utilizar etiquetas con la instrucción continue.
5.3.- Objetos en Java.
Excepto los tipos primitivos, todo en Java se maneja como objeto. Esto
compromete a quien lo utilice a tener una idea precisa acerca del modelo
de objetos y acerca de la nomenclatura particular empleada por el lenguaje
para adoptar dicho modelo.
A continuación describiremos algunos términos nuevos correspondientes
al lenguaje de programación Java, tomando como referencia los conceptos
básicos estudiados en la sección 4.1.
5.3.1.- Definición de Clases.
La definición de clases consiste en crear nuevas clases en las cuales se declaran las variables y métodos de la clase. Además, toda clase deberá pertenecer a una jerarquía basada en la herencia sencilla.
La jerarquía de clases en Java tiene, predeterminadamente, como raíz la clase Object que se encuentra incluida en el paquete java.lang . Todas las clases y jerarquías de clases, incluyendo las creadas por el programador, dependerán ( directa o indirectamente) de la clase Object.
La notación de punto se utiliza para indicar la pertenencia de
una clase a un paquete de clases dado. Por ejemplo :
java.lang.Object
indica que la clase Object pertenece a un paquete denominado
lang, el cual a su vez pertenece al paquete java .
La definición de una clase involucra la palabra clave class
, de acuerdo a la sintaxis :
[modificador] class nombreClase [extends superClase] [implements interfaz]
{
/* Lista de variables y métodos */
}
Ejemplo:
class primeraClase
{
...
}
En este caso, primeraClase extiende a la clase Object.
Es como si, en la primera línea, hubiéramos escrito :
class primeraClase extends java.lang.Object
Cuando una clase es subclase de alguna clase diferente de Object,
deberá expresarse de manera explícita por medio de la palabra
clave extends.
Ejemplo :
class primeraClase extends claseExistente
{
...
}
Cada clase deberá almacenarse en un archivo cuya extensión
sea .java , por lo que a primeraClase le correspondería
el archivo primeraClase.java .
5.3.2.- Definición de métodos.
Los métodos establecen el comportamiento de los objetos, por lo que su definición es esencial. En Java, los métodos son lo que en otros lenguajes se conoce como funciones, procedimientos, o subrutinas por lo que su definición es muy similar.
La sintaxis para definir un método es :
[modificador][tipo] nombreDeMétodo ( [tipo arg1,tipo arg2, ..., tipo argN] )
{
/* Cuerpo del método */
}
donde :
tipo es el tipo o clase del valor que regresa el método. Puede ser
cualquiera de los tipos básicos ( primitivos ) o la palabra reservada
void que indica que el método no regresa valor alguno.
La lista de argumentos que aparece entre paréntesis y entre corchetes es opcional y representan los valores que se pasan al método cuando es invocado. Si el argumento es una variable de alguno de los tipos primitivos, el paso se hace por valor, lo que significa que al método se le envía una copia del valor almacenado en la variable. En caso de que el argumento sea un objeto, el paso se hace por referencia, lo que significa que se pasa la dirección de memoria donde se almacena el objeto y que cualquier cambio realizado en el método ( con el nombre de la variable donde se recibe el argumento ) afectará al objeto original referenciado.
Ejemplo :
class PropiedadEnVenta
{
boolean credito = true;
void informa(String direccion, String tipo, float precio )
{
if(credito==true)
System.out.println("Esta propiedad tiene
atractivos planes de crédito.\n\n");
else
System.out.println("Lo sentimos. Esta propiedad
solo se vende al contado.");
System.out.println("Dirección : "
+direccion) ;
System.out.println("Tipo : "+tipo) ;
System.out.println("Precio : $ "precio) ;
}
}
En este ejemplo se ha definido la clase propiedadEnVenta , que contiene
un método denominado informa . Este método no retorna
ningún valor, puesto que se utiliza la palabra void como
tipo de retorno, y recibe tres argumentos que almacena en las variables
direccion, tipo y precio.
dirección almacena una referencia a un objeto de la clase String.
tipo almacena una referencia a un objeto de la clase String.
precio almacena un valor de tipo float.
El código aquí mostrado no es una aplicación completa,
pues requiere de una invocación al método informa( )
perteneciente a un objeto de la clase propiedadEnVenta, por ejemplo :
objeto.informa( "Balandra # 48", "Colonial",
250000.00F) ;
Con esto, se desplegaría :
Esta propiedad tiene atractivos planes de crédito.
Dirección : Balandra #48
Tipo : Colonial
Precio : $ 250000.00
5.3.3.- Clases y métodos abstractos.
Una clase abstracta es aquella que no puede ser instanciada,
( no pueden crearse objetos de esa clase ) y que solo sirve como una plantilla
para sus subclases.
En su definición, una clase abstracta se distingue porque su nombre está prefijado por la palabra clave abstract y/o porque tiene al menos un método abstracto.
Los métodos abstractos son aquellos cuya declaración está
prefijada por la palabra clave abstract . Además, los métodos
abstractos no cuentan con un cuerpo o definición en la clase donde
fueron declarados.
Ejemplo :
class abstract Figuras
{
abstract double area( ) ;
abstract double perimetro( ) ;
}
En la clase abstracta Figuras se han declarado dos métodos
abstractos : area y perimetro. Ambos regresan valores
de tipo double a sus invocadores.
Las clases abstractas no pueden utilizarse para crear nuevos objetos ya que sus métodos no son capaces de establecer comportamientos. Esto se debe a que solo existen los nombres de los métodos, pero no sus cuerpos (definiciones).
Para utilizar los métodos de una clase abstracta, es necesario crear una subclase de ella, donde se definan los métodos abstractos.
Por ejemplo, con base a la clase Figuras , se pueden definir
nuevas clases para diferentes figuras geométricas. Esto se logra
utilizando la palabra extends , como se muestra en el siguiente
código :
1 : class Circulo extends Figuras
2 : {
3 : double radio ;
4 : protected static final double PI = 3.14159265 ;
// PI es una constante
5 : Circulo( ) { radio = 1.0 ;}
6 : Circulo( double r) {radio = r ;}
7 : double area( ) { return PI * radio * radio ; }
8 : double perimetro( ) { return PI * 2 * radio ; }
9 : }
Analicemos cada una de las líneas del código anterior :
En la línea 1 se establece que la clase Circulo extiende a la clase Figuras.
En la línea 3 se define la variable radio como del tipo double.
En la línea 4 se define la variable PI como del tipo double y se le asigna un valor
inicial.
En la línea 5 se define un método constructor para la clase Circulo ( Obsérvese que el nombre del constructor es el mismo que el de la clase ). En este caso, el método no tiene argumentos y el cuerpo solo consta de la instrucción : radio = 1.0 ; , encerrada entre llaves.
En la línea 6 se define otro constructor para la clase,
solo que este método recibe un valor de tipo double en la
variable r.
En este punto, conviene observar algunas características de los
métodos constructores.
Cuando existen varias definiciones de un método, ¿ Como saber cuál código
ejecutar cuando se invoca al método ?. El mecanismo consiste en la elaboración
de una "firma" para cada una de las definiciones. Dependiendo de la cantidad y
los tipos de los argumentos utilizados en la invocación, se podrá determinar cual
código ejecutar, a pesar de que los nombres de métodos utilizados en las
definiciones sean iguales.
Por ejemplo, al crear el objeto miCirculo ,de la clase Circulo,
con la instrucción :
Circulo miCirculo = new Circulo( ) ;
se utilizará la definición de la línea 5 y el radio
será igual a 1.0 .
En cambio, si el objeto se crea con la instrucción :
Circulo miCirculo = new Circulo( 1.5 ) ;
será utilizada la definición de la línea 6 y a
radio se le asignará el valor 1.5 .
Obsérvese que la creación de un objeto se rige por la
sintaxis :
nombreClase nombreObjeto = new nombreConstructor( [argumentos]) ;
La sobrecarga de métodos no solamente se aplica a los constructores, sino a
todos los métodos ( salvo las excepciones establecidas por los modificadores
que se describen más adelante ).
Las líneas 7 y 8 definen a los métodos area(
) y perimetro( ) declarados como abstractos en la clase Figuras.
Las reglas correspondientes a la abstracción de clases y métodos
se pueden resumir de la siguiente manera :
5.3.4.- Interfaces.
Una interfaz es una clase que contiene declaraciones de métodos pero no las definiciones de ellos.
La declaración de una interfaz se distingue de la de una clase
abstracta por el hecho de no contener la palabra class y
porque su nombre está precedido por la palabra clave interface.
Además, la clase abstracta puede tener definidos algunos
métodos y la interfaz no debe tener métodos definidos.
Por ejemplo, si requerimos desplegar figuras geométricas en pantalla,
podemos definir la siguiente interfaz :
interface Desplegable
{
void estableceColor( Color c ) ;
void establecePosicion( double x, double y ) ;
void dibuja( DrawWindow d ) ;
}
Todos los métodos declarados en una interfaz son implícitamente abstractos, de manera que se puede omitir la palabra abstract.
Las interfaces se utilizan para suplir la falta de herencia múltiple
en Java, ya que una clase puede tener solo una superclase y , al
mismo tiempo, puede implementar varias interfaces utilizando la
palabra implements.
Por ejemplo, se puede definir una clase denominada DibujaCirculo
que extienda la clase Circulo y que implemente las interfaces Desplegable
y Borrable.
class DibujaCirculo extends Circulo
implements Desplegable, Borrable
{
...
...
}
Al igual que las clases, una interfaz puede extender una o más
interfaces.
Ejemplo :
interface Contador extends Impuestos, Declaraciones, Clientes
{
...
...
}
Una restricción adicional de las interfaces es que no puede definir
variables , solamente constantes ( utilizando los modificadores static
y final ).
Ejemplo :
// Una clase con una constante
class unaClase { static final int ALFA = 1 ; }
// Una interfaz con una constante.
interface unaInterfaz { static final BETA = 2 ; }
class otraClase implements unaInterfaz
{
void funcion( )
{
// Debe usarse el nombre unaClase porque
// otraClase no la extiende .
int a = unaClase.ALFA ;
// No se requiere el nombre de la interfaz, porque
// otraClase la implementa.
int b = BETA ;
}
}
5.3.5.- Modificadores de visibilidad.
Los modificadores de visibilidad especifican la posibilidad de acceder
a las clases, métodos y variables de otros paquetes, clases ,y métodos ;
como se describe en la Tabla 5.9.
|
Modificador |
Significado |
public |
Una clase o interfaz public es visible en cualquier lugar. Un método o una variable public son visibles en cualquier lugar donde su clase es visible. |
protected |
Las clases no pueden ser protected.
Un método o una variable protected son visibles a través del paquete de su clase, y en cualquier subclase de su clase. Una subclase en un paquete diferente al de la superclase puede acceder a los campos protected heredados por sus instancias, pero no puede acceder a esos campos en instancias de la superclase. |
| private | Las clases no pueden ser private.
Un método o una variable private solamente es visible dentro de su propia clase. |
private protected |
Una clase no puede ser private protected.
Una subclase puede acceder a los campos private protected heredados por sus instancias, pero no puede acceder a esos campos en instancias de la superclase. Un método o variable private protected es visible solamente dentro de su propia clase y dentro de cualquier subclase. |
| predeterminación | Al no especificarse algún modificador, una clase, interfaz, método, o variable es visible solamente dentro de su paquete. |
Tabla 5.9.- Modificadores de visibilidad.
5.3.6.- Otros modificadores.
Además de los modificadores de visibilidad, existen otros modificadores que se utilizan para especificar varios atributos de clases, métodos y variables. Estos modificadores se describen en la tabla 5.10.
|
Modificador |
Utilizado en : |
Significado |
| clase | La clase contiene métodos no implementados y no
puede
instanciarse (utilizarse para crear objetos). |
|
| abstract | interfaz | Todas las interfaces son abstractas. El modificador es
opcional en las declaraciones de interfaces. |
| método | No se provee el cuerpo ( definición, implementación)
del método. |
|
| clase | La clase no puede extenderse. | |
| final | método | El método no puede sobreponerse. |
| variable | El valor de la "variable" no puede cambiar. | |
static |
método | El método es un "método de clase", es implícitamente final, y puede ser invocado por medio del nombre de la clase. |
| variable | La variable es una "variable de clase". Existe solo una instancia de la variable. Puede accederse por medio del nombre de la clase. | |
| synchronized | método | El método hace modificaciones no atómicas a la clase o instancia. Debe asegurarse que dos hilos no puedan modificar a la clase o instancia, al mismo tiempo. |
| transient | variable | La variable no es parte del estado persistente del objeto.
( Este modificador no está implementado ) |
| volatile | variable | La variable cambia asincrónicamente. El compilador no tratará de salvar su valor en los registros. |
Tabla 5.10.- Otros modificadores.
5.3.7.- Paquetes.
Los paquetes son grupos de clases e interfaces que guardan alguna relación
entre sí. Las bibliotecas de clase en el Java Developer's Kit, están
contenidas dentro del paquete java , que a su vez contiene estos
otros paquetes :
java.applet
Este pequeño paquete contiene la clase Applet que es la superclase de todos los applets. Un applet es un pequeño programa en Java que se empotra en un guión HTML.
Además, este paquete contiene tres interfaces : AppletContext,
AppletStub y AudioClip.
java.awt
Este paquete toma su nombre de las iniciales de Abstract Windowing Toolkit
y contiene todas las clases para el manejo de ventanas y gráficos.
Las clases de este paquete pueden agruparse en :
Gráficas Estas clases definen colores, fuentes,
imágenes, polígonos, y demás.
Componentes Estas clases son componentes de la interfaz gráfica del usuario(GUI ), tales como botones, menús, listas, y cuadros de diálogo.
Manejadores de distribución Estas clases controlan
la distribución de componentes dentro de sus objetos contenedores.
Además, este paquete contiene los paquetes : java.awt.image
y java.awt.peer .
java.io
Este paquete contiene clases que permiten la entrada/salida basada en
flujos, y proveen acceso al sistema de archivos.
java.lang
Este paquete provee todas las clases relacionadas con el lenguaje Java,
tales como Object, String, Number, Exception, Error, etc.
java.net
Este paquete provee todas las clases que proveen acceso a TCP/IP, enchufes(sockets), direcciones de Internet, y URLs.
java.util
Este paquete provee clases de utilería tales como Hashtable,
Vector, Enumeration, Properties, etc.
Para una descripción detallada de estos seis paquetes y las clases
contenidas en ellos, se recomienda revisar la referencia [FLAND96] .
5.3.7.1.- Acceso a los paquetes.
Las clases en Java tienen acceso predeterminado a las clases que se encuentran en el paquete java.lang . Esto es, las clases de este paquete están a disposición de las nuevas clases creadas por el programador.
Por el contrario, si el programador necesita utilizar alguna de las
clases de los otros paquetes, deberá hacer referencias explícitas
a dichas clases o deberá escribir una instrucción que le
permita importarlas.
Ejemplo :
// Referencia explícita con notación de punto
public class MiClase extends java.applet.Applet
{
...
}
puede escribirse como :
import java.applet.Applet ;
// Referencia implícita después de la instrucción import.
public class MiClase extends Applet
{
...
}
La primera parte del ejemplo hace referencia explícita a la clase Applet, por medio de la notación de punto ( nombre cualificado ).
La segunda parte, primero se "importa" la clase Applet y luego se hace referencia a ella de manera directa ( nombre abreviado ).
Si además de la clase Applet se va a hacer referencia
a alguna otra clase del paquete lang, se puede escribir :
import java.lang.* ;
con lo que se podrán utilizar los nombres abreviados de todas las clases del paquete.
5.3.8.- Aplicaciones en Java.
A una aplicación que se ejecuta dentro de un guión HTML se le conoce como applet.
Este nombre sirve para distinguir a estas aplicaciones de otras llamadas
aplicaciones autónomas, que se ejecutan desde la línea
de órdenes del sistema operativo.
A fin de que puedan ejecutarse los programas para ambos tipos de aplicación,
la Maquina Virtual de Java (el intérprete de Java) requiere que
se compilen los programas fuente y se obtenga un programa en un código
especial llamado bytecode ( código de byte ), que no tiene instrucciones
relacionadas con una plataforma específica ( razón por la
cual debe interpretarse para su ejecución).
El código fuente para los dos tipos de aplicaciones debe almacenarse
en archivos con extensión .java , aunque su forma interna
y su ejecución son distintos.
5.3.8.1.- Aplicaciones autónomas.
Aunque para la elaboración de los tutoriales que nos ocupan no
se requiere de las aplicaciones independientes, en esta sección
se describirá brevemente su funcionamiento.
Una aplicación autónoma se ejecuta al invocar a la Máquina Virtual de Java, por ejemplo :
C :\ > java HolaMundo
donde :
java es el nombre de la Máquina Virtual de Java y,
HolaMundo es el nombre de la clase principal que se encuentra en el programa
en código de bytes HolaMundo.class, obtenido por medio de la
compilación del programa fuente HolaMundo.java .
Las aplicaciones autónomas deben incluir un método denominado
main( ) , como se muestra en el siguiente ejemplo:
class HolaMundo
{
public static void main ( String args[ ] )
{
System.out.println("Hola, Mundo ! !" ;
}
}
Este código debe almacenarse en el archivo de texto HolaMundo.java,
5.3.8.2.- Applets.
Un applet es una pequeña aplicación que se ejecuta en
un visualizador, por lo que el guión HTML deberá incluir
una etiqueta que inicie la ejecución del applet.
Los applets de Java están diseñados para ser independientes
de la plataforma, al igual que los guiones HTML. Por esta razón,
el visualizador deberá contar con una Máquina Virtual
de Java que traduzca de código de byte a código de máquina.
Para ejecutar una aplicación dentro de un guión HTML se
requiere que éste contenga la etiqueta <APPLET>, como en el
siguiente ejemplo :
<HTML>
<HEAD>
<TITLE>Mi Primer Applet</TITLE>
</HEAD>
<BODY>
<H3>Este es mi primer applet :</H3>
<P>
<APPLET CODE="HolaMun2.class"
WIDTH=100 HEIGHT=50></APPLET>
</BODY>
</HTML>
El archivo HolaMun2.class es el resultado de compilar el archivo
HolaMun2.java como se muestra a continuación:
C :\> javac HolaMun2.java
A continuación se presenta el contenido del archivo HolaMun2.java :
1 : import java.awt.Graphics ;
2 : public class HolaMun2 extends java.applet.Applet
3 : {
4 : public void paint (Graphics g)
5 : {
6 : g.drawString("Hola, Mundos !", 1,1) ;
7 : }
8 : }
En la línea 1 se importa la clase Graphics, la cual se
encuentra en el paquete awt, que a su vez se encuentra en el paquete
java. Esta línea se incluye para poder crear objetos de la
clase Graphics, como se observa en la línea 4.
En la línea 2 se deriva, de la clase Applet y por medio
de la palabra extends, la clase HolaMun2. Esta derivación
debe realizarse para todos los applets.
Observe que el nombre de esta clase coincide con el nombre del archivo
(Holamun2.java) que la contiene.
En la línea 4 se reescribe el método paint que
recibe como parámetro una dirección a un objeto de tipo Graphics,
la cual se almacena en la variable g.
Finalmente, en la línea 6 se invoca al método drawString
del objeto referenciado por g para dibujar la cadena Hola, Mundo !
, en las coordenadas 1 , 1 y con el tipo de letra actual.
La etiqueta <APPLET> tiene los siguientes atributos :
CODE = "archivo.class" indica el nombre del archivo de clase donde se almacena el applet compilado.
CODEBASE = ruta especifica la ruta del directorio donde se encuentran
almacenados los archivos de clase, cuando estos no se localizan en el directorio
actual.
WIDTH = valor indica el ancho (en pixeles) del cuadro donde se
desplegará el applet.
HEIGHT = valor indica la altura ( en pixeles) del cuadro donde
se desplegará elapplet.
ALIGN = valor define la alineación que tendrá el applet dentro de la página, donde valor puede ser:
TEXTOP alinea la parte superior del applet con la parte superior del texto en la línea.
TOP alinea el applet con el elemento más alto en la línea (applet, imagen o la parte superior del texto).
ABSMIDDLE alinea el centro del applet con el centro del elemento más grande en la línea.
MIDDLE alinea el centro del applet con el centro de la línea base del texto.
BASELINE alinea la parte inferior del applet con la línea base del texto. Es equivalente a BOTTOM.
ABSBOTTOM alinea la parte inferior del applet con el elemento más bajo en la línea ( línea base del texto, applet o imagen )
HSAPCE = valor controla el espacio horizontal en pixeles entre
el applet y su espacio circundante a la izquierda y a la derecha.
VSPACE = valor controla el espacio vertical en pixeles entre
el applet y su espacio circundante por arriba y por abajo.
Existe otra etiqueta asociada con los applets que se denomina <PARAM > y sirve para pasar parámetros del guión HTML al applet. Los parámetros son constantes contenidas en el guión HTML cuyos valores se pasan a variables del applet.
La etiqueta <PARAM> tiene dos atributos :
NAME = cadena que se refiere al nombre dado a la constante en el guión HTML.
Este nombre deberá coincidir exactamente con el nombre utilizado
en el applet.
VALUE = cadena que determina el valor del parámetro.
Los parámetros pueden servir para que los usuarios interactúen
con un applet, puesto que pueden modificar el guión HTML pero no
pueden modificar el archivo de clase.
Por ejemplo : si tenemos el siguiente applet que despliega un nombre
import java,awt.Graphics ;
import java,awt.Font ;
import java,awt.Color ;
public class saludo extends java.applet.Applet
{
Font fuente = new Font("TimesRoman",
Font.BOLD, 36 ) ;
String nombre ;
public void init( )
{
this.nombre = getParameter ( "nombre" ) ;
if( nombre == null ) nombre = "desconocido" ;
nombre = " Hola " + nombre +
" ! ";
}
public void paint( Graphics g )
{
g.setFont(fuente) ;
g.setColor(Color.blue) ;
g.drawString(nombre, 5, 75) ;
}
}
y al usuario solo le proporcionamos el archivo saludo.class ;
entonces, él puede escribir el siguiente guión HTML y lo
denomina saludo.html ,
<HTML> <HEAD> <TITLE> Ejemplo de paso de parámetros</TITLE> </HEAD> <BODY> <P> <APPLET CODE = "saludo.class" WIDTH = 500 HEIGHT = 75> <PARAM NAME = nombre VALUE = "Juan Mares Ruiz"> </APPLET> </BODY> </HTML>
en el visualizador aparecerá un cuadro de 500 pixeles de
ancho por 75 de alto y en él se desplegará:

Si en el archivo saludo.html se elimina la línea que contiene la etiqueta <PARAM>, entonces se desplegará :

5.3.8.2.1.- Métodos predeterminados para los
applets.
Un applet es un bloque de código Java con una tarea bien definida
y se implementa por medio de la creación de una subclase de la clase
Applet. Esta clase define varios métodos que pueden reescribirse
y que controlan el funcionamiento de los applets. A continuación
se describen brevemente esos métodos.
init ( )
Realiza la inicialización de un applet. La inicialización
se lleva a cabo cada vez que se carga un applet y en ella se incluye la
creación de los objetos necesarios, la configuración del
estado inicial, la carga de imágenes y/o fuentes de caracteres,
y el establecimiento de parámetros.
start ( )
Este método hace que el applet empiece a ejecutar su trabajo.
El arranque de un applet ocurre varias veces durante su vida útil,
a diferencia de la inicialización que ocurre sólo una vez.
stop ( )
Detiene la ejecución del trabajo del applet. La detención
y el arranque se complementan. La detención ocurre cuando se abandona
la página que contiene un applet en ejecución
destroy ( )
Libera los recursos ocupados por el applet. Este método permite
eliminar cualquier hilo en ejecución o liberar cualquier objeto
con referencias activas.
Las implementaciones predeterminadas de estos métodos están
vacías, por lo que no realizan tarea alguna. Cuando se requiera
que alguno de ellos realice algún trabajo, es necesario reescribirlo.
5.3.8.3.- Gráficos.
Los gráficos son elementos esenciales para describir de manera
clara y concisa ciertos conceptos y procesos. En Java, los objetos gráficos
se crean por medio de la clase Graphics, de la cual describiremos
sus principales características. También trataremos con las
clases relacionadas Color y Font.
La clase Graphics proporciona un conjunto de métodos para
crear algunas figuras geométricas como : líneas rectas,
rectángulos, polígonos, óvalos y arcos.
Para dibujar, se utilizan coordenadas x,y en el cuarto cuadrante,
por lo que los puntos A(2,3) y B(5,7) se verían así :
...............................................................
X

Y
También es necesario reescribir el método paint ( )
, como puede observarse en el siguiente applet de ejemplo :
/* Figuras.java : Muestra figuras de ejemplo,
dibujadas con métodos de la clase Graphics
*/
import java.awt.* ;
public class Figuras extends java.applet.Applet
{
public void paint(Graphics g)
{
// Dibuja una línea recta desde el
// punto 10,20 al punto 50,60.
g.drawLine(10,20,50,60);
// Dibuja un rectángulo, dadas las coordenadas
// del vértice superior izquierdo ( 50,60 ),
// y las dimensiones a= 30 , b=20.
g.drawRect(50,60,30,20);
// Dibuja un rectángulo, dadas las coordenadas
// del vértice superior izquierdo, sus dimensiones
// a,b y las dimensiones delcuadro de la
// esquina a redondear.
g.drawRoundRect(30,90,50,60,20,20) ;
// Dibuja un rectángulo relleno.
g.fillRect(130,20,30,40) ;
// Dibuja un círculo de diámetro = 20 .
g.drawOval(130,70,20,20);
// Dibuja un óvalo diam_x = 50, diam_y = 20.
g.drawOval(130,100,50,20);
// Dibuja un arco diam_x = 50, diam_y = 20,
// ang_ini = 90, ang_fin= 180.
g.drawArc(130,150,50,20,90,180);
}
}
Al compilarse el archivo Figuras.java, se produce el archivo
Figuras.class , que se utiliza en la etiqueta <APPLET>
del guión Figuras.html mostrado a continuación :
< ! Figuras.html >
<HTML>
<HEAD>
<TITLE>Dibuja figuras geométricas en Java</TITLE>
</HEAD>
<BODY>
<P>
<APPLET CODE="Figuras.class"
WIDTH=400 HEIGHT=200></APPLET>
</BODY>
</HTML>
El resultado se muestra en la Figura 5.1

Figura 5.1.- Resultado del guión Figuras.html.
5.3.8.4.- Texto.
Los objetos de la clase Graphics también poseen métodos para dibujar caracteres en la pantalla. Antes de dibujar las cadenas de caracteres, deberán crearse objetos de la clase Font para indicar la fuente, el estilo y el tamaño de los caracteres a utilizar.
La clase Font se encuentra en el paquete java.awt, y puede importarse
como se muestra en el siguiente ejemplo :
import java.awt.Font;
import java.awt.Graphics;
public class Fuentes extends java.applet.Applet
{
public void paint(Graphics g)
{
Font p = new Font("TimesRoman",
Font.PLAIN,20);
Font b = new Font("TimesRoman",
Font.BOLD,20);
Font i = new Font("TimesRoman",
Font.ITALIC,20);
Font ib = new Font("TimesRoman",
Font.ITALIC+Font.BOLD,20);
g.setFont(p);
g.drawString("Esta es una fuente normal", 10, 25);
g.setFont(b);
g.drawString("Esta es una fuente en negritas", 10, 50);
g.setFont(i);
g.drawString("Esta es una fuente italica", 10, 75);
g.setFont(ib);
g.drawString("Esta es una fuente
italica negrita",10,100);
}
}
El constructor Font( ) requiere tres argumentos. El primero es el nombre de la fuente, y puede tomar los valores : "TimesRoman" , "Courier" y "Helvetica".
El segundo es el estilo de fuente representado por el nombre de una de las constantes predefinidas en la clase Font . Los nombres de esas constantes son : PLAIN , BOLD e ITALIC.
El tercer argumento representa el tamaño ( en pixeles ) con relación
a la altura de los caracteres.
El método drawString( ) , de la clase Graphics , dibuja una cadena de caracteres con la fuente establecida por la invocación más reciente al método setFont( ).
Los argumentos requeridos por drawString( ) son : la cadena
de caracteres a dibujar, y las coordenadas x , y que especifican
la posición del primer carácter de la cadena.
A continuación se presenta el guión Fuentes.html
.
< ! Fuentes.html >
<HTML>
<HEAD>
>Ejemplos de fuentes</TITLE>
</HEAD>
<BODY>
<P>
<APPLET CODE="Fuentes.class" WIDTH=600
HEIGHT=200></APPLET>
</BODY>
</HTML>
El resultado se muestra en la Figura 5.2.

Figura 5.2.- Resultado del guión Fuentes.html.
Existen métodos para buscar información acerca de la fuente utilizada en una cadena, de los cuales se describen algunos en la Tabla 5.11.
|
Método |
Clase |
Descripción |
getFont( ) |
Graphics |
Regresa el objeto fuente actual . |
getName( ) |
Font |
Regresa el nombre de la fuente |
getSize( ) |
Font |
Regresa el tamaño de la fuente actual en formato de número entero |
getStyle( ) |
Font |
Regresa el estilo actual en formato de número entero ( 0= normal, 1= negrita, 2= itálica , 3= negrita itálica ) |
isPlain( ) |
Font |
Regresa true si el estilo de la fuente es normal |
isBold( ) |
Font |
Regresa true si el estilo de la fuente es negrita |
isItalic( ) |
Font |
Regresa true si el estilo de la fuente es itálica |
Tabla 5.11.- Métodos para el manejo de información acerca de fuentes.
5.3.8.5.- Colores.
En Java, los colores se manejan por medio de los métodos definidos en la clase Color.
El constructor Color( ) describe un color como componentes de los colores rojo, verde y azul ( RGB ; Red, Green, Blue ) por medio de valores enteros entre 0 y 255, o valores de punto flotante entre 0.0 y 1.0. De esta manera, el color negro se puede representar como 0,0,0 o como 0.0,0.0,0.0 y el color blanco como 255,255,255 o como 1.0,1.0,1.0
Entre estos valores existen poco mas de 16 millones de combinaciones que posibilitan el manejo de un número igual de colores. Sin embargo, el número de colores que se pueden manejar es de menor cuantía, dependiendo de la plataforma y del visualizador utilizados.
Aunque los colores se pueden representar como ternas de valores, existe
un conjunto de nombres de colores predefinidos, como se muestra en la Tabla
5.12.
|
Nombre : |
|
black |
|
blue |
|
cyan |
|
darkgray |
|
gray |
|
green |
|
lightgray |
|
magenta |
|
orange |
|
pink |
|
red |
|
white |
|
yellow |
Tabla 5.12.- Nombres de colores predefinidos en la clase
Color.
En el siguiente código se presenta un ejemplo para el uso de
nombres de colores predefinidos.
/* HolaColores.java : Ejemplo para el manejo de
colores predefinidos */
import java.awt.* ;
public class HolaColores extends java.applet.Applet
{
static final String mensaje = "Hola Colores !" ;
private Font fuente ;
// Inicialización por única vez del applet
public void init( )
{
fuente = new Font( "Helvetica",
Font.BOLD, 48) ;
}
// Dibuja el applet
public void paint( Graphics g)
{
// Dibuja un óvalo rosa.
g.setColor(Color.pink) ;
g.fillOval(10,10,330,100) ;
//Dibuja el contorno rojo, por medio de
//cuatro óvalos.
g.setColor(Color.red) ;
g.drawOval(10,10,330,100) ;
g.drawOval(9,9,332,102) ;
g.drawOval(8,8,334,104) ;
g.drawOval(7,7,336,106) ;
// Dibuja el texto.
g.setColor(Color.black) ;
g.setFont(fuente) ;
g.drawString(mensaje, 40,75) ;
}
}
El correspondiente guión HTML sería :
<! HolaColores.html >
<HTML><HEAD>
<TITLE>Ejemplo para el manejo de colores
predefinidos></TITLE>
</HEAD>
<BODY>
<P>
<APPLET CODE="HolaColores.class"
WIDTH=400 HEIGHT=200>
</APPLET>
</BODY>
</HTML>
La Figura 5.3 muestra el resultado del guión HolaColores.html

Figura 5.3.- Resultado del guión HolaColores.html
5.3.8.7.- Animación con Java.
La animación en Java consiste de la presentación repetida en pantalla de una secuencia de imágenes, construidas en tiempo de ejecución o previamente elaboradas.
Para esto, es necesario la utilización de hilos (threads) que permitan la ejecución en paralelo de varios procesos. En realidad, un hilo es un espacio de memoria que sirve para controlar un proceso. Con el uso de hilos se garantiza que los recursos del sistema no sean monopolizados y que se puedan tener varios applets en ejecución dentro de la misma página.
Cuando se utilizan hilos es preciso incluir las palabras implements Runnable en la identificación de la clase a crear. La interfaz Runnable está declarada en el paquete java.lang.
Como se muestra en el siguiente ejemplo, deberán reescribirse
los métodos start( ) , stop( ) y run( ).
/* Reloj digital */
import java.awt.Graphics;
import java.awt.Font;
import java.util.Date;//Para tomar la fecha del sistema.
public class Rdig extends java.applet.Applet
implements Runnable
{
Font fuente = new Font("TimesRoman",Font.BOLD,24);
Date fecha; // La fecha actual
Thread ejecutor; //El hilo para la ejecución
//del applet.
// Inicia la ejecución del applet.
public void start()
{
if (ejecutor == null);//No está en ejecución
//el applet.
{
ejecutor = new Thread(this);// Crea un hilo.
ejecutor.start();// e inicia la ejecución.
}
}
// Detiene la ejecución del applet.
public void stop()
{
if (ejecutor != null) //Si está en ejecución
//elapplet
{
ejecutor.stop();//Lo detiene.
ejecutor = null;//Desactiva el hilo (el recolector
//de basura lo recogerá).
}
}
//Se ejecuta automáticamente cuando se invoca a start( )
public void run()
{
while (true)// Genera un ciclo infinito.
{
fecha = new Date();// Toma la fecha actual.
repaint();// Redibuja el cuadro del applet.
try { Thread.sleep(1000); }//Espera 1 segundo para
//redibujar el cuadro
catch (InterruptedException e) { }//con un manejo de
//excepción
}
}
// Dibuja el applet.
public void paint(Graphics g)
{
g.setFont(fuente);
// Convierte la fecha a cadena y la dibuja.
g.drawString(fecha.toString(),10,50);
}
} // Fin de la clase Rdig
Este applet se ejecuta con el guión Rdig.html mostrado
a continuación :
<HTML>
<HEAD>
<TITLE>Reloj digital </TITLE>
</HEAD>
<BODY>
<P>
<APPLET CODE="Rdig.class"
WIDTH=600 HEIGHT=100>
</APPLET>
</BODY>
</HTML>
El resultado de visualizar el dibujo del applet en un momento dado,
por medio del guión Rdig1.htm , se muestra en la Figura 5.4.

Figura 5.4.- Resultado del guión Rdig.html
El parpadeo que se nota al ejecutar el guión Rdig.html se debe a la manera en que funcionan los métodos encargados de dibujar y redibujar los cuadros.
Para generar la animación, es necesario dibujar una imagen, borrar esa imagen, dibujar la siguiente imagen, borrarla, y así sucesivamente.
Para realizar este trabajo se escribe una invocación al método repaint( ), el cual primero invoca a update( ), que a su vez limpia el cuadro del applet ( lo rellena con el color del fondo actual ) y enseguida invoca a paint( ), quien dibuja el nuevo contenido del cuadro.
El parpadeo se genera por la invocación a update( ) ,
quien deja al cuadro "en blanco" por un breve lapso de tiempo.
El parpadeo puede reducirse :
1.- Dibujando sin limpiar el cuadro.
2.- Dibujando solamente las partes del cuadro que sufren algún
cambio.
3.- Utilizando doble buffer.
La aplicación de la opción 1 se limita a los casos en
que el dibujo no modifica su forma, sino que solamente cambia su color.
La opción 2 implica que el dibujo deberá hacerse por partes,
para poder seleccionar aquellas que deberán actualizarse.
En la opción 3 se asigna una área de memoria RAM para utilizarse como almacenamiento "de paso" para las figuras a dibujar. Esta opción es muy adecuada para el caso en que las imágenes que constituyen la animación se encuentran almacenadas en archivos de disco.
5.3.8.7.1.- Animación con imágenes .
Los ejemplos de animación mostrados en la sección anterior han utilizado métodos para el manejo de líneas, fuentes y colores. Esto implica que las figuras utilizadas en las animaciones se tienen que construir cada vez que se dibuja cada cuadro de animación.
Sin embargo, en las animaciones reales generalmente se requieren figuras constituidas por algo mas que líneas, cuadros y óvalos ; se requieren figuras de formas complejas que solo pueden lograrse por medio de paquetes para dibujo o por medio de fotografías. Las imágenes obtenidas por cualquiera de estos dos métodos se almacenan en archivos de disco.
La animación por medio de imágenes almacenadas implica la creación de un guión donde se manifieste la finalidad de la animación, así como la secuencia en que se desplegará cada imagen y la pausa entre cada una de ellas.
Una vez que se tiene el guión, es necesario elaborar cada imagen y almacenarla en un archivo ( con extensión .gif o .jpg ).
Finalmente, se debe escribir el applet que contenga el código
necesario para desplegar la animación.
Aquí nos concentraremos en la descripción de esta última
fase y mas adelante veremos un ejemplo completo.
El paquete java.awt contiene la clase Image, y puede accederse
a ella escribiendo la línea :
import java.awt.Image ;
Puesto que las imágenes a utilizar están almacenadas en
disco, debe hacerse la copia de cada imagen de disco a memoria. Para esto,
se utiliza el método getImage( ) definido en la clase Image.
La copia de la imagen en memoria puede desplegarse por medio del método
drawImage( ) definido en la clase Graphics.
A manera de ejemplo, supongamos que tenemos una imagen almacenada en
el archivo unaimag.gif , y deseamos desplegarla. El código
sería :
import java.awt.Graphics ;
import java.awt.Image ;
public class Imagen extends java.applet.Applet
{
Image unaimagen ;
public void init( )
{
unaimagen=getImage(getCodeBase( ),"unaimag.gif") ;
}
public void paint (Graphics g )
{
g.drawImage ( unaimagen, 20,20, this ) ;
}
}
Este código solo despliega una imagen, pero sirve de base para
la generación de una animación, como se muestra en el siguiente
ejemplo :
/* ApunAnim.java : Genera una animación */
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Color;
public class ApunAnim extends java.applet.Applet
implements Runnable
{
//Arreglo de apuntadores a imágenes.
Image apunimag[] = new Image[7];
Image imagactual; // Imagen actual.
Thread anima;//Hilo para manejar la animación.
int xpos = 1;//Coordenada en x ,
int ypos = 1;//Coordenada en y ,
// para la posición de la esquina superior
// izquierda de las imágenes.
public void init()
{
// Arreglo con los nombres de los archivos
// de las imágenes.
String archimag[] = { "apunap1.gif",
"apunap2.gif",
"apunap3.gif",
"apunap4.gif",
"apunap5.gif",
"apunap6.gif",
"apunap7.gif"};
// Crea el arreglo de imágenes.
for (int i=0; i < 7; i++)
{
apunimag[i] = getImage(getCodeBase(),archimag[i]);
}
}
public void start()
{
if (anima == null)
{
anima = new Thread(this);
anima.start();
}
}
public void stop()
{
if (anima != null)
{
anima.stop();
anima = null;
}
}
public void run()
{
// Inicia la animación.
setBackground(Color.white);
ejecutaAnimacion();
}
void ejecutaAnimacion()
{
for (int i = 0; i < 7; i++)
{
imagactual = apunimag[i];
repaint();
espera(5000); // Hace una pausa de 5 segundos.
}
}
void espera(int miliseg)
{
try { Thread.sleep(miliseg); }
catch (InterruptedException e) { }
}
public void paint(Graphics g)
{
g.drawImage(imagactual, xpos, ypos, this);
}
}
El guión HTML correspondiente es :
<HTML>
<HEAD>
<TITLE>ApunAnim(Animación con Java)
</TITLE>
</HEAD>
<BODY>
<P>
<APPLET CODE="ApunAnim.class"
WIDTH=301 HEIGHT=151></APPLET>
</BODY>
</HTML>
La imagen final mostrada por el visualizador es la que se observa en
la Figura 5.5.

Figura 5.5.- Imagen almacenada en el archivo apunap7.gif
Cuando se ejecuta el applet ApunAnim, se observa que el despliegue de las imágenes es irregular. Esto se debe a que el applet empieza a desplegar las imágenes antes de que se hayan cargado completamente desde el disco.