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 :

  1. float de 32 bits, precisión sencilla, y
  2. double de 64 bits, precisión doble.



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 ;

y = x ; // A la variable y se le asigna la misma dirección que tiene

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&aacute;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í :


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&eacutetricas 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&oacuten 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.