1 - PRESENTACIÓN

En este tutorial presentaremos una visión general del lenguaje de programación Java, incluyendo la programación de applets y aplicaciones. También trataremos algunas características avanzadas y el API del lenguaje Java.

No obstante, y para que todos tengamos una idea general de lo que es Java, haremos una serie de aclaraciones o indicaciones, para que alguien que no esté muy decidido a aprender Java, pueda tener elementos de juicio y de comparación para tomar esa decisión. Por ello, haremos una introducción más extensa de lo que sería habitual en un tutorial, lo que también me permitirá verter ciertas opiniones que en otro sitio no tendrían cabida.
Conocimientos previos...

En el planteamiento inicial se supone que tú, que estás leyendo esto, tienes experiencia en algún lenguaje de programación, como puede ser C o un lenguaje similar , también sabes utilizar un navegador de WWW como Netscape o Internet Explorer, también eres capaz de desarrollar páginas WWW con HTML y comprendes la programación orientada a objetos. Intentaremos que estos conocimientos sean suficientes para poder seguir el tutorial.

El entorno de desarrollo habitual entre la gente que trabajamos con el JDK (los pobres sin poder para comprarse herramientas comerciales) consiste en tener abiertas sobre el escritorio de la pantalla, ya sea en Solaris, Windows '95/NT o Linux, varias ventanas, que usamos para lo siguiente:

  • En una de ellas tenemos abierto HotJava, apuntando al fichero packages.html de la documentación del API de Java, para poder tener al momento la información sobre los parámetros de llamada, valores de retorno y la información de cada una de las funciones que estemos utilizando.
  • Los que estén desarrollando sobre Windows '95 o NT, podrán disponer de la documentación del API tambi‰n desde el sistema de ayuda de Windows si han descargado esta documentación en formato Winhelp desde Dippy.
  • En otra tendremos abierto un navegador con soporte Java, normalmente será Netscape, en una versión superior a la 2.0b4, para que tenga soporte audio en los applets que desarrollemos, o Microsoft Explorer, en versión 3.0 o superior.
  • Tendremos abierta otra ventana con un editor, donde iremos escribiendo el código de nuestro applet o aplicación Java. En Windows '95 son muy utilizados el editor Jpad y Diva. Aunque en estos momentos, quien tiene potencia suficiente en su ordenador, está programando Java desde el Java Workshop de Sun.
  • Y una ventana más donde tendremos acceso al prompt del sistema para poder invocar al compilador y a las demás herramientas del JDK.
  • Los que tengan algo de dinero podrán estar utilizando alguno de los entornos shareware que ya hay disponibles como RadJa, Kawa o JavaMaker.
  • Los que sí disponen de poder adquisitivo suficiente habrán empezado con el Symantec Café de Symantec, porque ahora estarán trabajando con Borland C+ + 5.0, que soporta Java o estarán entusiasmados con el Visual J+ + de Microsoft o estarán deseando ver ya en sus pantallas el Borland Latte.
  • Y los enamorados de OS2, que en su versión 4.0 ya incluye una Máquina Virtual Java (JVM) en su kernel, se lo pasarán en grande con el IBM VisualAge for Java.

Objetivos

Los objetivos que nos hemos marcado, para que tú, navegante lector, cuando llegues al final del tutorial, seas capaz de:

  • Crear páginas HTML que llamen a applets Java
  • Crear contextos gráficos en Java
  • Utilizar los componentes del interfaz gráfico que proporciona Java
  • Crear aplicaciones autónomas en Java
  • Crear aplicaciones multi-threaded
  • Utilizar las librerías de E/S para manipular ficheros de texto y datos
  • Crear servidores y clientes TCP/IP que se comuniquen vía socket

Intentaremos cumplir lo que acabamos de prometer, aunque también pedimos paciencia, ya que Java, en este momento, es un lenguaje con una notable vitalidad, que todavía estamos aprendiendo nosotros. Por otro lado, no es nada sencillo el hacerse entender, por lo que tenemos que dar varias vueltas a cada uno de los conceptos que escribimos para cerciorarnos de que tú que lo lees comprendes exactamente lo que nosotros te queremos transmitir.

Además, tampoco esperes que te proporcionemos ungüentos milagrosos, que por arte de magia traspasen el conocimiento. El estudio de Java, y sus applets, no será sino el examen de una particular forma de ver las cosas, con un poco de estructuración en la presentación y un cierto trasfondo de Internet; el resto es, como siempre, tarea del programador. Es decir, uno puede aprender a construir un applet, o dejar que alguna de las herramientas lo construyan automáticamente, igual que puede enseñarse a codificar un diálogo en un entorno gráfico, pero... la inteligencia de esa pieza siempre dependerá de la habilidad y experiencia del programador respecto del lenguaje usado y de sus recursos. En fin, un buen applet será únicamente resultado del trabajo de un buen programador Java.
A empezar...

Te recomendamos que, si vas a seguir el Tutorial a la vez que nosotros vayamos publicando capítulos, utilices las opciones Hotlist o Bookmark de tu navegador para marcar la página por la que vas y poder pasar fácilmente de unos capítulos a otros.

Hemos escrito el Tutorial de Java, enfocado a Netscape, en cualquier otro navegador se verá decentemente, pero bien, bien... con Netscape, porque nos será imprescindible, al ser el más extendido de los navegadores con soporte Java.

Y ya puedes pasar a ver lo que hemos preparado, para ello puedes empezar viendo como Navegar a través del Tutorial o pasar directamente a la Tabla de Contenido, donde encontrarás enlaces con cada uno de los capítulos y apartados.

Buena Suerte.

INTRODUCCIÓN A JAVA

El uso principal que se hace de Internet e incluso de las redes internas (corporativas) es correo electrónico (e-mail), aunque actualmente hay un auge sorprendente de la navegación web. Los documentos web pueden contener variedad de texto, gráficos de todas clases y proporcionar enlaces hipertexto hacia cualquier lugar de la red. Los navegadores utilizan documentos escritos en lenguaje HTML. La combinación actual de navegadores HTML/WWW están limitados pues, a texto y gráficos. Si se quiere reproducir un sonido o ejecutar un programa de demostración, primero hemos de bajarnos (download) el fichero en cuestión y luego utilizar un programa en nuestro ordenador capaz de entender el formato de ese fichero, o bien cargar un módulo (plug-in) en nuestro navegador para que pueda interpretar el fichero que hemos bajado.

Hasta ahora, la única forma de realizar una página web con contenido interactivo, era mediante la interfaz CGI (Common Gateway Interface), que permite pasar parámetros entre formularios definidos en lenguaje HTML y programas escritos en Perl o en C. Esta interfaz resulta muy incómoda de programar y es pobre en sus posibilidades.

El lenguaje Java y los navegadores con soporte Java, proporcionan una forma diferente de hacer que ese navegador sea capaz de ejecutar programas. Con Java se puede reproducir sonido directamente desde el navegador, se pueden visitar Tutoriales pages con animaciones, se puede enseñar al navegador a manejar nuevos formatos de ficheros, e incluso, cuando se pueda transmitir video por las líneas telefónicas, nuestro navegador estará preparado para mostrar esas imágenes.

Utilizando Java, se pueden eliminar los inconvenientes de la interfaz CGI y también se pueden añadir aplicaciones que vayan desde experimentos científicos interactivos de propósito educativo a juegos o aplicaciones especializadas para la televenta. Es posible implementar publicidad interactiva y periódicos personalizados. Por ejemplo, alguien podría escribir un programa Java que implementara una simulación química interactiva (una cadena de adn). Utilizando un navegador con soporte Java, un usuario podría recibir fácilmente esa simulación e interaccionar con ella, en lugar de conseguir simplemente un dibujo estático y algo de texto. Lo recibido cobra vida. Además, con Java podemos estar seguros de que el código que hace funcionar el experimento químico no contiene ningún trozo de código malicioso que dañe al sistema. El código que intente actuar destructivamente o que contenga errores, no podrá traspasar los muros defensivos colocados por las características de seguridad y robustez de Java.

Además, Java proporciona una nueva forma de acceder a las aplicaciones. El software viaja transparentemente a través de la red. No hay necesidad de instalar las aplicaciones, ellas mismas vienen cuando se necesitan. Por ejemplo, la mayoría de los navegadores del Web pueden procesar un reducido número de formatos gráficos (típicamente GIF y JPEG). Si se encuentran con otro tipo de formato, el navegador estándar no tiene capacidad para procesarlo, tendría que ser actualizado para poder aprovechar las ventajas del nuevo formato. Sin embargo, un navegador con soporte Java puede enlazar con el servidor que contiene el algoritmo que procesa ese nuevo formato y mostrar la imagen. Por lo tanto, si alguien inventa un nuevo algoritmo de compresión para imágenes, el inventor sólo necesita estar seguro de que hay una copia en código Java de ese algoritmo instalada en el servidor que contiene las imágenes que quiere publicar. Es decir, los navegadores con soporte Java se actualizan a sí mismos sobre la marcha, cuando encuentran un nuevo tipo de fichero o algoritmo.

Origen de JAVA

Sun Microsystems, líder en servidores para Internet, uno de cuyos lemas desde hace mucho tiempo es "the network is the computer" (lo que quiere dar a entender que el verdadero ordenador es la red en su conjunto y no cada máquina individual), es quien ha desarrollado el lenguaje Java, en un intento de resolver simultáneamente todos los problemas que se le plantean a los desarrolladores de software por la proliferación de arquitecturas incompatibles, tanto entre las diferentes máquinas como entre los diversos sistemas operativos y sistemas de ventanas que funcionaban sobre una misma máquina, añadiendo la dificultad de crear aplicaciones distribuidas en una red como Internet.

He podido leer más de cinco versiones distintas sobre el origen, concepción y desarrollo de Java, desde la que dice que este fue un proyecto que rebotó durante mucho tiempo por distintos departamentos de Sun sin que nadie le prestara ninguna atención, hasta que finalmente encontró su nicho de mercado en la aldea global que es Internet; hasta la más difundida, que justifica a Java como lenguaje de pequeños electrodomésticos.

Hace algunos años, Sun Microsystems decidió intentar introducirse en el mercado de la electrónica de consumo y desarrollar programas para pequeños dispositivos electrónicos. Tras unos comienzos dudosos, Sun decidió crear una filial, denominada FirstPerson Inc., para dar margen de maniobra al equipo responsable del proyecto.

El mercado inicialmente previsto para los programas de FirstPerson eran los equipos domésticos: microondas, tostadoras y, fundamentalmente, televisión interactiva. Este mercado, dada la falta de pericia de los usuarios para el manejo de estos dispositivos, requería unos interfaces mucho más cómodos e intuitivos que los sistemas de ventanas que proliferaban en el momento.

Otros requisitos importantes a tener en cuenta eran la fiabilidad del código y la facilidad de desarrollo. James Gosling, el miembro del equipo con más experiencia en lenguajes de programación, decidió que las ventajas aportadas por la eficiencia de 'C++ no compensaban el gran coste de pruebas y depuración. Gosling había estado trabajando en su tiempo libre en un lenguaje de programación que él había llamado Oak, el cual, aún partiendo de la sintaxis de C++, intentaba remediar las deficiencias que iba observando.

Los lenguajes al uso, como C o C++, deben ser compilados para un chip, y si se cambia el chip, todo el software debe compilarse de nuevo. Esto encarece mucho los desarrollos y el problema es especialmente acusado en el campo de la electrónica de consumo. La aparición de un chip más barato y, generalmente, más eficiente, conduce inmediatamente a los fabricantes a incluirlo en las nuevas series de sus cadenas de producción, por pequeña que sea la diferencia en precio ya que, multiplicada por la tirada masiva de los aparatos, supone un ahorro considerable. Por tanto, Gosling decidió mejorar las características de Oak y utilizarlo.

El primer proyecto en que se aplicó este lenguaje recibió el nombre de proyecto Green y consistía en un sistema de control completo de los aparatos electrónicos y el entorno de un hogar. Para ello se construyó un ordenador experimental denominado *7 (Star Seven). El sistema presentaba una interfaz basada en la representación de la casa de forma animada y el control se llevaba a cabo mediante una pantalla sensible al tacto. En el sistema aparecía Duke, la actual mascota de Java. Posteriormente se aplicó a otro proyecto denominado VOD (Video On Demand) en el que se empleaba como interfaz para la televisión interactiva. Ninguno de estos proyectos se convirtió nunca en un sistema comercial, pero fueron desarrollados enteramente en un Java primitivo y fueron como su bautismo de fuego.

Una vez que en Sun se dieron cuenta de que a corto plazo la televisión interactiva no iba a ser un gran éxito, urgieron a FirstPerson a desarrollar con rapidez nuevas estrategias que produjeran beneficios. No lo consiguieron y FirstPerson cerró en la primavera de 1994.

Pese a lo que parecía ya un olvido definitivo, Bill Joy, cofundador de Sun y uno de los desarrolladores principales del Unix de Berkeley, juzgó que Internet podría llegar a ser el campo de juego adecuado para disputar a Microsoft su primacía casi absoluta en el terreno del software, y vio en Oak el instrumento idóneo para llevar a cabo estos planes. Tras un cambio de nombre y modificaciones de diseño, el lenguaje Java fue presentado en sociedad en agosto de 1995.

Lo mejor será hacer caso omiso de las historias que pretenden dar carta de naturaleza a la clarividencia industrial de sus protagonistas; porque la cuestión es si independientemente de su origen y entorno comercial, Java ofrece soluciones a nuestras expectativas. Porque tampoco vamos a desechar la penicilina aunque haya sido su origen fruto de la casualidad.

Caracteristicas de JAVA

Las características principales que nos ofrece Java respecto a cualquier otro lenguaje de programación, son:

Es SIMPLE:

Java ofrece toda la funcionalidad de un lenguaje potente, pero sin las características menos usadas y más confusas de éstos. C++ es un lenguaje que adolece de falta de seguridad, pero C y C++ son lenguajes más difundidos, por ello Java se diseñó para ser parecido a C++ y así facilitar un rápido y fácil aprendizaje.

Java elimina muchas de las características de otros lenguajes como C++, para mantener reducidas las especificaciones del lenguaje y añadir características muy útiles como el garbage collector (reciclador de memoria dinámica). No es necesario preocuparse de liberar memoria, el reciclador se encarga de ello y como es un thread de baja prioridad, cuando entra en acción, permite liberar bloques de memoria muy grandes, lo que reduce la fragmentación de la memoria.

Java reduce en un 50% los errores más comunes de programación con lenguajes como C y C++ al eliminar muchas de las características de éstos, entre las que destacan:

  • aritmética de punteros
  • no existen referencias
  • registros (struct)
  • definición de tipos (typedef)
  • macros (#define)
  • necesidad de liberar memoria (free)


Aunque, en realidad, lo que hace es eliminar las palabras reservadas (struct, typedef), ya que las clases son algo parecido.

Además, el intérprete completo de Java que hay en este momento es muy pequeño, solamente ocupa 215 Kb de RAM.

Es ORIENTADO A OBJETOS:

Java implementa la tecnología básica de C++ con algunas mejoras y elimina algunas cosas para mantener el objetivo de la simplicidad del lenguaje. Java trabaja con sus datos como objetos y con interfaces a esos objetos. Soporta las tres características propias del paradigma de la orientación a objetos: encapsulación, herencia y polimorfismo. Las plantillas de objetos son llamadas, como en C++, clases y sus copias, instancias. Estas instancias, como en C++, necesitan ser construidas y destruidas en espacios de memoria.

Java incorpora funcionalidades inexistentes en C++ como por ejemplo, la resolución dinámica de métodos. Esta característica deriva del lenguaje Objective C, propietario del sistema operativo Next. En C++ se suele trabajar con librerías dinámicas (DLLs) que obligan a recompilar la aplicación cuando se retocan las funciones que se encuentran en su interior. Este inconveniente es resuelto por Java mediante una interfaz específica llamada RTTI (RunTime Type Identification) que define la interacción entre objetos excluyendo variables de instancias o implementación de métodos. Las clases en Java tienen una representación en el runtime que permite a los programadores interrogar por el tipo de clase y enlazar dinámicamente la clase con el resultado de la búsqueda.

Es DISTRIBUIDO:

Java se ha construido con extensas capacidades de interconexión TCP/IP. Existen librerías de rutinas para acceder e interactuar con protocolos como http y ftp. Esto permite a los programadores acceder a la información a través de la red con tanta facilidad como a los ficheros locales.

La verdad es que Java en sí no es distribuido, sino que proporciona las librerías y herramientas para que los programas puedan ser distribuidos, es decir, que se corran en varias máquinas, interactuando.

Es ROBUSTO:

Java realiza verificaciones en busca de problemas tanto en tiempo de compilación como en tiempo de ejecución. La comprobación de tipos en Java ayuda a detectar errores, lo antes posible, en el ciclo de desarrollo. Java obliga a la declaración explícita de métodos, reduciendo así las posibilidades de error. Maneja la memoria para eliminar las preocupaciones por parte del programador de la liberación o corrupción de memoria. También implementa los arrays auténticos, en vez de listas enlazadas de punteros, con comprobación de límites, para evitar la posibilidad de sobreescribir o corromper memoria resultado de punteros que señalan a zonas equivocadas. Estas características reducen drásticamente el tiempo de desarrollo de aplicaciones en Java.

Además, para asegurar el funcionamiento de la aplicación, realiza una verificación de los byte-codes, que son el resultado de la compilación de un programa Java. Es un código de máquina virtual que es interpretado por el intérprete Java. No es el código máquina directamente entendible por el hardware, pero ya ha pasado todas las fases del compilador: análisis de instrucciones, orden de operadores, etc., y ya tiene generada la pila de ejecución de órdenes.

Java proporciona, pues:

  • Comprobación de punteros
  • Comprobación de límites de arrays
  • Excepciones
  • Verificación de byte-codes


Es de ARQUITECTURA NEUTRAL:

Para establecer Java como parte integral de la red, el compilador Java compila su código a un fichero objeto de formato independiente de la arquitectura de la máquina en que se ejecutará. Cualquier máquina que tenga el sistema de ejecución (run-time) puede ejecutar ese código objeto, sin importar en modo alguno la máquina en que ha sido generado. Actualmente existen sistemas run-time para Solaris 2.x, SunOs 4.1.x, Windows 95, Windows NT, Linux, Irix, Aix, Mac, Apple y probablemente haya grupos de desarrollo trabajando en el porting a otras plataformas.

El código fuente Java se "compila" a un código de bytes de alto nivel independiente de la máquina. Este código (byte-codes) está diseñado para ejecutarse en una máquina hipotética que es implementada por un sistema run-time, que sí es dependiente de la máquina.

En una representación en que tuviésemos que indicar todos los elementos que forman parte de la arquitectura de Java sobre una plataforma genérica, obtendríamos una figura como la siguiente:

En ella podemos ver que lo verdaderamente dependiente del sistema es la Máquina Virtual Java (JVM) y las librerías fundamentales, que también nos permitirían acceder directamente al hardware de la máquina. Además, habrá APIs de Java que también entren en contacto directo con el hardware y serán dependientes de la máquina, como ejemplo de este tipo de APIs podemos citar:

* Java 2D: gráficos 2D y manipulación de imágenes
* Java Media Framework : Elementos críticos en el tiempo: audio, video...
* Java Animation: Animación de objetos en 2D
* Java Telephony: Integración con telefonía
* Java Share: Interacción entre aplicaciones multiusuario
* Java 3D: Gráficos 3D y su manipulación


Es SEGURO:

La seguridad en Java tiene dos facetas. En el lenguaje, características como los punteros o el casting implícito que hacen los compiladores de C y C++ se eliminan para prevenir el acceso ilegal a la memoria. Cuando se usa Java para crear un navegador, se combinan las características del lenguaje con protecciones de sentido común aplicadas al propio navegador.

El lenguaje C, por ejemplo, tiene lagunas de seguridad importantes, como son los errores de alineación. Los programadores de C utilizan punteros en conjunción con operaciones aritméticas. Esto le permite al programador que un puntero referencie a un lugar conocido de la memoria y pueda sumar (o restar) algún valor, para referirse a otro lugar de la memoria. Si otros programadores conocen nuestras estructuras de datos pueden extraer información confidencial de nuestro sistema. Con un lenguaje como C, se pueden tomar números enteros aleatorios y convertirlos en punteros para luego acceder a la memoria:

   printf( "Escribe un valor entero: " );
         scanf( "%u",&puntero );
         printf( "Cadena de memoria: %s
",puntero );

Otra laguna de seguridad u otro tipo de ataque, es el Caballo de Troya. Se presenta un programa como una utilidad, resultando tener una funcionalidad destructiva. Por ejemplo, en UNIX se visualiza el contenido de un directorio con el comando ls. Si un programador deja un comando destructivo bajo esta referencia, se puede correr el riesgo de ejecutar código malicioso, aunque el comando siga haciendo la funcionalidad que se le supone, después de lanzar su carga destructiva. Por ejemplo, después de que el caballo de Troya haya enviado por correo el /etc/shadow a su creador, ejecuta la funcionalidad de ls persentando el contenido del directorio. Se notará un retardo, pero nada inusual.

El código Java pasa muchos tests antes de ejecutarse en una máquina. El código se pasa a través de un verificador de byte-codes que comprueba el formato de los fragmentos de código y aplica un probador de teoremas para detectar fragmentos de código ilegal -código que falsea punteros, viola derechos de acceso sobre objetos o intenta cambiar el tipo o clase de un objeto-.

Si los byte-codes pasan la verificación sin generar ningún mensaje de error, entonces sabemos que:

* El código no produce desbordamiento de operandos en la pila
* El tipo de los parámetros de todos los códigos de operación son conocidos y correctos
* No ha ocurrido ninguna conversión ilegal de datos, tal como convertir enteros en punteros
* El acceso a los campos de un objeto se sabe que es legal: public, private, protected
* No hay ningún intento de violar las reglas de acceso y seguridad establecidas


El Cargador de Clases también ayuda a Java a mantener su seguridad, separando el espacio de nombres del sistema de ficheros local, del de los recursos procedentes de la red. Esto limita cualquier aplicación del tipo Caballo de Troya, ya que las clases se buscan primero entre las locales y luego entre las procedentes del exterior.

Las clases importadas de la red se almacenan en un espacio de nombres privado, asociado con el origen. Cuando una clase del espacio de nombres privado accede a otra clase, primero se busca en las clases predefinidas (del sistema local) y luego en el espacio de nombres de la clase que hace la referencia. Esto imposibilita que una clase suplante a una predefinida.

En resumen, las aplicaciones de Java resultan extremadamente seguras, ya que no acceden a zonas delicadas de memoria o de sistema, con lo cual evitan la interacción de ciertos virus. Java no posee una semántica específica para modificar la pila de programa, la memoria libre o utilizar objetos y métodos de un programa sin los privilegios del kernel del sistema operativo. Además, para evitar modificaciones por parte de los crackers de la red, implementa un método ultraseguro de autentificación por clave pública. El Cargador de Clases puede verificar una firma digital antes de realizar una instancia de un objeto. Por tanto, ningún objeto se crea y almacena en memoria, sin que se validen los privilegios de acceso. Es decir, la seguridad se integra en el momento de compilación, con el nivel de detalle y de privilegio que sea necesario.

Dada, pues la concepción del lenguaje y si todos los elementos se mantienen dentro del estándar marcado por Sun, no hay peligro. Java imposibilita, también, abrir ningún fichero de la máquina local (siempre que se realizan operaciones con archivos, éstas trabajan sobre el disco duro de la máquina de donde partió el applet), no permite ejecutar ninguna aplicación nativa de una plataforma e impide que se utilicen otros ordenadores como puente, es decir, nadie puede utilizar nuestra máquina para hacer peticiones o realizar operaciones con otra. Además, los intérpretes que incorporan los navegadores de la Web son aún más restrictivos. Bajo estas condiciones (y dentro de la filosofía de que el único ordenador seguro es el que está apagado, desenchufado, dentro de una cámara acorazada en un bunker y rodeado por mil soldados de los cuerpos especiales del ejército), se puede considerar que Java es un lenguaje seguro y que los applets están libres de virus.

Respecto a la seguridad del código fuente, no ya del lenguaje, JDK proporciona un desemsamblador de byte-code, que permite que cualquier programa pueda ser convertido a código fuente, lo que para el programador significa una vulnerabilidad total a su código. Utilizando javap no se obtiene el código fuente original, pero sí desmonta el programa mostrando el algoritmo que se utiliza, que es lo realmente interesante. La protección de los programadores ante esto es utilizar llamadas a programas nativos, externos (incluso en C o C++) de forma que no sea descompilable todo el código; aunque así se pierda portabilidad. Esta es otra de las cuestiones que Java tiene pendientes.

Es PORTABLE:

Más allá de la portabilidad básica por ser de arquitectura independiente, Java implementa otros estándares de portabilidad para facilitar el desarrollo. Los enteros son siempre enteros y además, enteros de 32 bits en complemento a 2. Además, Java construye sus interfaces de usuario a través de un sistema abstracto de ventanas de forma que las ventanas puedan ser implantadas en entornos Unix, Pc o Mac.

Es INTERPRETADO:

El intérprete Java (sistema run-time) puede ejecutar directamente el código objeto. Enlazar (linkar) un programa, normalmente, consume menos recursos que compilarlo, por lo que los desarrolladores con Java pasarán más tiempo desarrollando y menos esperando por el ordenador. No obstante, el compilador actual del JDK es bastante lento. Por ahora, que todavía no hay compiladores específicos de Java para las diversas plataformas, Java es más lento que otros lenguajes de programación, como C++, ya que debe ser interpretado y no ejecutado como sucede en cualquier programa tradicional.

Se dice que Java es de 10 a 30 veces más lento que C, y que tampoco existen en Java proyectos de gran envergadura como en otros lenguajes. La verdad es que ya hay comparaciones ventajosas entre Java y el resto de los lenguajes de programación, y una ingente cantidad de folletos electrónicos que supuran fanatismo en favor y en contra de los distintos lenguajes contendientes con Java. Lo que se suele dejar de lado en todo esto, es que primero habría que decidir hasta que punto Java, un lenguaje en pleno desarrollo y todavía sin definición definitiva, está maduro como lenguaje de programación para ser comparado con otros; como por ejemplo con Smalltalk, que lleva más de 20 años en cancha.

La verdad es que Java para conseguir ser un lenguaje independiente del sistema operativo y del procesador que incorpore la máquina utilizada, es tanto interpretado como compilado. Y esto no es ningún contrasentido, me explico, el código fuente escrito con cualquier editor se compila generando el byte-code. Este código intermedio es de muy bajo nivel, pero sin alcanzar las instrucciones máquina propias de cada plataforma y no tiene nada que ver con el p-code de Visual Basic. El byte-code corresponde al 80% de las instrucciones de la aplicación. Ese mismo código es el que se puede ejecutar sobre cualquier plataforma. Para ello hace falta el run-time, que sí es completamente dependiente de la máquina y del sistema operativo, que interpreta dinámicamente el byte-code y añade el 20% de instrucciones que faltaban para su ejecución. Con este sistema es fácil crear aplicaciones multiplataforma, pero para ejecutarlas es necesario que exista el run-time correspondiente al sistema operativo utilizado.

Es MULTITHREADED:

Al ser multithreaded (multihilvanado, en mala traducción), Java permite muchas actividades simultáneas en un programa. Los threads (a veces llamados, procesos ligeros), son básicamente pequeños procesos o piezas independientes de un gran proceso. Al estar los threads contruidos en el lenguaje, son más fáciles de usar y más robustos que sus homólogos en C o C++.

El beneficio de ser miltithreaded consiste en un mejor rendimiento interactivo y mejor comportamiento en tiempo real. Aunque el comportamiento en tiempo real está limitado a las capacidades del sistema operativo subyacente (Unix, Windows, etc.), aún supera a los entornos de flujo único de programa (single-threaded) tanto en facilidad de desarrollo como en rendimiento.

Cualquiera que haya utilizado la tecnología de navegación concurrente, sabe lo frustrante que puede ser esperar por una gran imagen que se está trayendo. En Java, las imágenes se pueden ir trayendo en un thread independiente, permitiendo que el usuario pueda acceder a la información en la página sin tener que esperar por el navegador.

Es DINÁMICO:

Java se beneficia todo lo posible de la tecnología orientada a objetos. Java no intenta conectar todos los módulos que comprenden una aplicación hasta el tiempo de ejecución. Las librería nuevas o actualizadas no paralizarán las aplicaciones actuales (siempre que mantengan el API anterior).

Java también simplifica el uso de protocolos nuevos o actualizados. Si su sistema ejecuta una aplicación Java sobre la red y encuentra una pieza de la aplicación que no sabe manejar, tal como se ha explicado en párrafos anteriores, Java es capaz de traer automáticamente cualquiera de esas piezas que el sistema necesita para funcionar.

<< Texto nuevo >>Java, para evitar que los módulos de byte-codes o los objetos o nuevas clases, haya que estar trayéndolos de la red cada vez que se necesiten, implementa las opciones de persistencia, para que no se eliminen cuando de limpie la caché de la máquina.

HotJava

HotJava, en pocas palabras, es un navegador con soporte Java (Java-enabled), desarrollado en Java. Como cualquier navegador de Web, HotJava puede decodificar HTML estándar y URLs estándares, aunque no soporta completamente el estándar HTML 3.0. La ventaja sobre el resto de navegadores, sin soporte Java, es que puede ejecutar programas Java sobre la red. La diferencia con Netscape, es que tiene implementado completamente los sistemas de seguridad que propone Java, esto significa que puede escribir y leer en el disco local, aunque esto hace disminuir la seguridad, ya que se pueden grabar en nuestro disco programas que contengan código malicioso e introducirnos un virus, por ejemplo. No obstante, el utilizar esta característica de HotJava es decisión del usuario.

JAVA para aplicaciones corporativas

Java actualmente está en boca de todos, Java e Intranet son las palabras de moda. Pero, surge la pregunta de si esta es una buena tecnología para desarrollar aplicaciones corporativas. Y la respuesta es afirmativa y voy a proponer argumentos para esa afirmación. En donde la red sea algo crítico, Java facilita tremendamente la vida de la programación corporativa.

Durante años, las grandes empresas se han convencido de que la "red" corporativa es la arteria por donde fluye la sangre que mantiene vivo su negocio. Desde el gran servidor de sus oficinas centrales, hasta los servidores de las delegaciones, las estaciones de trabajo de los programadores y la marabunta de PCs, la información va fluyendo de unos a otros. Para muchas compañías, la Red es la Empresa.

Si esta red no se mantiene sana, los pedidos no llegan, el inventario no se actualiza, el software no se desarrolla adecuadamente, los clientes no están satisfechos y, fundamentalmente, el dinero no entra. La necesidad de diagnosticar y reducir la arterioesclerosis de la red, hace que se estén inyectando continuamente nuevas metodologías que subsanen este grave problema.

¿Es Java la medicina? Está claro que cuando vemos un cepillo animado limpiando los dientes, cubos moviéndose en 3-D, o una banda de gatos locos en applets de Java, nos convencemos de que es el lenguaje idóneo para Internet. Pero, qué pasa con las aplicaciones corporativas, ¿sería una buena tecnología allí donde la red es el punto crítico? Vamos a intentar responder comparando las capacidades de Java contra la lista de necesidades de la red corporativa.
Desarrollo rápido de aplicaciones

Hace años, se decía que los programadores pronto desaparecerían. Los generadores automáticos de programas, eliminarían a los generadores humanos y el mundo sería un lugar mejor para vivir. Desafortunadamente, quienes decían esto no tuvieron en cuenta una acelerada demanda de software de calidad para muy diferentes aplicaciones. Sin embargo, la tecnología de objetos pronto vino a intentar facilitar la tarea, adoptando el modelo de "generar parte de un programa", así, generando la parte básica de un programa (los objetos), se podría conectar con otras partes para proporcionar diferentes utilidades al usuario.

El lenguaje C++ es una buena herramienta, pero no cumple totalmente la premisa. Visual Basic y NextStep, se acercan cada vez más al poder de los objetos. Java facilita la creación de entornos de desarrollo-aplicaciones de modo similar, pero además es flexible, poderoso y efectivo. Los programadores ahora disponen de herramientas de programación de calidad beta, que apuntan hacia esa meta, como son el Java WorkShop de SunSoft, el entorno Java de Borland, el Café de Symantec, y pronto, herramientas más sofisticadas como Netcode o FutureTense. Esto proporciona una gran progresión a los entornos de desarrollo Java.
Aplicaciones efectivas y eficientes

Las aplicaciones que se crean en grandes empresas deben ser más efectivas que eficientes; es decir, conseguir que el programa funcione y el trabajo salga adelante es más importante que el que lo haga eficientemente. Esto no es una crítica, es una realidad de la programación corporativa. Al ser un lenguaje más simple que cualquiera de los que ahora están en el cajón de los programadores, Java permite a éstos concentrarse en la mecánica de la aplicación, en vez de pasarse horas y horas incorporando APIs para el control de las ventanas, controlando minuciosamente la memoria, sincronizando los ficheros de cabecera y corrigiendo los agónicos mensajes del linker. Java tiene su propio toolkit para interfaces, maneja por sí mismo la memoria que utilice la aplicación, no permite ficheros de cabecera separados (en aplicaciones puramente Java) y solamente usa enlace dinámico.

Muchas de las implementaciones de Java actuales son puros intérpretes. Los byte-codes son interpretados por el sistema run-time de Java, la Máquina Virtual Java (JVM), sobre el ordenador del usuario. Aunque ya hay ciertos proveedores que ofrecen compiladores nativos Just-In-Time (JIT). Si la Máquina Virtual Java dispone de un compilador instalado, las secciones (clases) del byte-code de la aplicación se compilarán hacia la arquitectura nativa del ordenador del usuario. Los programas Java en ese momento rivalizarán con el rendimiento de programas en C++. Los compiladores JIT no se utilizan en la forma tradicional de un compilador; los programadores no compilan y distribuyen binarios Java a los usuarios. La compilación JIT tiene lugar a partir del byte-code Java, en el sistema del usuario, como una parte (opcional) del entorno run-time local de Java.

Muchas veces, los programadores corporativos, ansiosos por exprimir al máximo la eficiencia de su aplicación, empiezan a hacerlo demasiado pronto en el ciclo de vida de la aplicación. Java permite algunas técnicas innovadoras de optimización. Por ejemplo, Java es inherentemente multithreaded, a la vez que ofrece posibilidades de multithread como la clase Thread y mecanismos muy sencillos de usar de sincronización; Java en sí utiliza threads. Los desarrolladores de compiladores inteligentes pueden utilizar esta característica de Java para lanzar un thread que compruebe la forma en que se está utilizando la aplicación. Más específicamente, este thread podría detectar qué métodos de una clase se están usando con más frecuencia e invocar a sucesivos niveles de optimización en tiempo de ejecución de la aplicación. Cuanto más tiempo esté corriendo la aplicación o el applet, los métodos estarán cada vez más optimizados (Guava de Softway es de este tipo).

Si un compilador JIT está embebido en el entorno run-time de Java, el programador no se preocupa de hacer que la aplicación se ejecute óptimamente. Siempre he pensado que en los Sistemas Operativos tendría que aplicarse esta filosofía; un optimizador progresivo es un paso más hacia esta idea.
Portabilidad para programador y programa

En una empresa de relativo tamaño hay una pléyade diferente de ordenadores. Probablemente nos encontremos con estaciones de trabajo Sun para el desarrollo de software, hordas de PCs para cada empleado, algún Mac en el departamento de documentación, una estación de trabajo HP en administración y una estación SGI en la sala de demos. Desarrollar aplicaciones corporativas para un grupo tan diferente de plataformas en excesivamente complejo y caro. Hasta ahora era complicado convencer a los programadores de cada arquitectura que utilizasen un API común para reducir el coste de las aplicaciones.

Con un entorno run-time de Java portado a cada una de las arquitecturas de las plataformas presentes en la empresa y una buena librería de clases ("packages" en Java), los programadores pueden entenderse y encontrar muy interesante trabajar con Java. Esta posibilidad hará tender a los programadores hacia Java, justo donde otros intentos anteriores con entornos universales (como Galaxy o XVT) han fracasado. Estos APIs eran simplemente inadecuados, no orientados a redes y, verdaderamente, pesados.

Una vez que los programas estén escritos en Java, otro lado interesante del asunto es que los programadores también son portables. El grupo de programadores de la empresa puede ahora enfrentarse a un desarrollo para cualquiera de las plataformas. La parte del cliente y del servidor de una aplicación estarán ahora escritas en el mismo lenguaje. Ya no será necesario tener un grupo que desarrolle en Solaris en del departamento de I+D, programadores trabajando sobre Visual Basic en el departamento de documentación y programadores sobre GNU en proyectos especiales; ahora todos ellos podrán estar juntos y formar el grupo de software de la empresa.

Cortes de desarrollo

En contraste con el alto coste de los desarrollos realizados sobre estaciones de trabajo, el coste de creación de una aplicación Java es similar al de desarrollar sobre un PC.

Desarrollar utilizando un software caro para una estación de trabajo (ahora barata) es un problema en muchas empresas. La eficiencia del hardware y el poco coste de mantenimiento de una estación de trabajo Sun, por ejemplo, resulta muy atractivo para las empresas; pero el coste adicional del entorno de desarrollo con C++ es prohibitivo para la gran mayoría de ellas. La llegada de Java e Intranet reducen considerablemente estos costes. Las herramientas Java ya no están en el entorno de precios de millones de pesetas, sino a los niveles confortables de precio de las herramientas de PCs. Y con el crecimiento cada día mayor de la comunidad de desarrolladores de software freeware y shareware que incluso proporcionan el código fuente, los programadores corporativos tienen un amplio campo donde moverse y muchas oportunidades de aprender y muchos recursos a su disposición.

El éxito que Internet ha proporcionado a los equipos de software corporativos es un regalo. El precio del software es ahora el mismo para un poderoso equipo corriendo Unix que para un PC. Incluso Netscape tiene al mismo precio la versión Unix de su servidor Web SuiteSpot que la versión PC/NT. Esta es la filosofía de precios que parece ser será la que se siga con las herramientas basadas en Java.

Mantenimiento y soporte

Un problema bien conocido que ocurre con el software corporativo es la demanda de cuidados y realimentación. Java no es, ciertamente, la cura para la enfermedad del mantenimiento, pero tiene varias características que harán la vida del enfermero más fácil.

Uno de los componentes del JDK es javadoc. Si se usan ciertas convenciones en el código fuente Java (como comenzar un comentario con /** y terminarlo con */), javadoc se puede fácilmente generar páginas HTML con el contenido de esos comentarios, que pueden visualizarse en cualquier navegador. La documentación del API de Java ha sido creada de este modo. Esto hace que el trabajo de documentar el código de nuevas clases Java sea trivial.

Otro gran problema del desarrollador corporativo es la creación y control de makefiles. Leerse un makefile es como estar leyendo la historia de empresa. Normalmente se pasan de programador a programador, quitando la información que no es esencial, siempre que se puede. Esto hace que muchos de los makefiles de las aplicaciones contengan docenas de librerías, una miríada de ficheros de cabecera y ultra-confusos macros. Es como mirar en el estómago de la ballena de Jonás.

Java reduce las dependencia de complejos makefiles drásticamente. Primero, no hay ficheros de cabecera. Java necesita que todo el código fuente de una clase se encuentre en un solo fichero. Java tiene la inteligencia de make en el propio lenguaje para simplificar la compilación de byte-codes. Por ejemplo:

public class pepe { // Fichero: pepe.java Guitarra flamenca ; } public class guitarra { // Fichero: guitarra.java } % javac -verbose pepe.java [parsed pepe.java in 720ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 220ms] [checking class pepe] [parsed .\\Guitarra.java in 50ms] [wrote pepe.class] [checking class Guitarra] [wrote .\\Guitarra.class] [done in 2300ms]


El compilador Java se da cuenta de que necesita compilar el fichero guitarra.java. Ahora vamos a forzarlo a que recompile pepe.java sin cambiar guitarra.java, podremos comprobar que el compilador de byte-code Java no recompila innecesariamente el fichero guitarra.java.

% javac -verbose pepe.java [parsed pepe.java in 440ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 160ms] [checking class pepe] [loaded .\\Guitarra.java in 0ms] [wrote pepe.class] [done in 1860ms]

Ahora, si modificamos guitarra.java (añadiendo, por ejemplo, otro miembro a la clase) y compilamos pepe.java, el compilador Java se dará cuenta de que debe recompilar tanto pepe.java como guitarra.java

% javac -verbose pepe.java [parsed pepe.java in 710ms] [loaded C:\JAVA\BIN\..\classes\java\lang\Object.class in 220ms] [checking class pepe] [parsed .\\Guitarra.java in 0ms] [wrote pepe.class] [checking class Guitarra] [wrote .\\Guitarra.class] [done in 2640ms]

En el libro Just Java de Peter van der Linden hay un capítulo excelente acerca del compilador de Java, si tienes oportunidad, no dejes de leerlo.
Aprendizaje

Si la empresa está llena de programadores de >C++ con alguna experiencia en el manejo de librería gráficas, aprenderán rápidamente lo esencial de Java. Si el equipo de ingenieros no conoce C++, pero maneja cualquier otro lenguaje de programación orientada a objetos, les llevará pocas semanas dominar la base de Java. Lo que sí que no es cierto es que haya que aprender C++ antes de aprender Java.

Si los ingenieros de la empresa no conocen ningún lenguaje orientado a objetos, sí que tienen que aprender los fundamentos de esta tecnología antes de nada, y luego aplicarlos a la programación con Java. El análisis y diseño orientado a objetos debe ser comprendido antes de intentar nada con Java. Los programadores de Java sin un fondo de conocimientos de OOA/D producirán código pobre. Además, los libros sobre Java crecen como la espuma, ya hay más de 25 publicados, y si buscas "Progamming in Java" en la Red, encontrarás 312 Web sites, y 30 más dedicados a "Learning Java". Y si esto, evidentemente, no es el sustituto de un instructor humano, hay ya varias empresas que ofrecen enseñanza de Java, entre ellas, Sun.
Resumen

En base a los argumentos que acabamos de exponer, ¿podría una empresa utilizar Java para sus aplicaciones críticas? En este instante, sería suficiente un acercamiento a Java. Porque más importante que la elección de Java o cualquier otro lenguaje de programación es un buen diseño de la arquitectura de la aplicación. Diseñar la aplicación para que esté distribuida entre servidores y clientes, y la línea de partida debe ser el diseño modular.

Algunas sugerencias para adoptar Java como tecnología corporativa, serían:

  1. Usar Java en el desarrollo de la interface del cliente; Java es suficientemente estable para desarrollar una interface portable. Utilizar herramientas de programación más estables en los servidores, porque son la parte crítica.
  2. Portar o crear un servidor no-crítico en Java, de forma que tanto cliente como servidor estén escritos en Java.
  3. Utilizar Java en proyectos de envergadura tanto en el cliente como en el servidor, para valorar la efectividad de Java.


Intranet está creciendo actualmente más rápido que Internet. Las organizaciones corporativas están adoptando la metodología Internet para proporcionar soluciones a sus usuarios y clientes. Java tiene todas las cartas para ser una herramienta de inestimable valor en el desarrollo de aplicaciones corporativas

INSTALACIÓN DEL JDK

En el instante en que estoy escribiendo esto, Java todavía no es un producto comercial. No todas las máquinas disponen de la versión del Java Development Kit para ejecutarse en ellas. Por ello, solamente comentaré la instalación de JDK en Solaris, Windows 95 y Linux.

Actualmente ya hay entornos de desarrollo integrados completos para Java, diferentes del JDK de Sun. Symantec dispone de un compilador de Java para Windows 95 y Windows NT, con las ventajas del aumento de velocidad de proceso y capacidades multimedia que esto proporciona, Symantec Café. Borland también está trabajando en ello y la nueva versión de su entorno de desarrollo soporta Java. Sun ha lanzado la versión comercial de su propio entorno de desarrollo para Java, el Java Workshop, enteramente escrito en Java. Y Microsoft ha puesto en el mercado Visual J++, que sigue el estilo de todas sus herramientas de desarrollo.

No obstante, trataremos solamente el JDK, que hasta el momento es lo más conocido. El entorno básico del JDK de Java que proporciona Sun está formado por herramientas en modo texto, que son: java, intérprete que ejecuta programas en byte-code. javac, compilador de Java que convierte el código fuente en byte-code. javah, crea ficheros de cabecera para implementar métodos para cualquier clase. javap, es un descompilador de byte-code a código fuente Java. javadoc, es un generador automático de documentos HTML a partir del código fuente Java. javaprof, es un profiler para aplicaciones de un solo thread. HotJava, es un navegador Web escrito completamente en Java.

El entorno habitual pues, consiste en un navegador que pueda ejecutar applets, un compilador que convierta el código fuente Java a byte-code y el intérprete Java para ejecutar los programas. Estos son los componenetes básicos para desarrollar algo en Java. No obstante se necesita un editor para escribir el código fuente, y no son estrictamente necesarias otras herramientas como el debugger, un entorno visual, la documentación o un visualizador de jerarquía de clases. Tan es así, que disponiendo del navegador Netscape 2.0 no se necesita ni tan siquiera el JDK (a petición de varios amigos que disfrutan del uso de Linux pero no disponen de soporte ELF para poder utilizar el JDK portado por Randy Chapman, les indicaré como conseguir utilizar el compilador embebido en Netscape).

Windows

La versión del JDK para Windows es un archivo autoextraible (instalador), el que actualmente (la última versión al 20 de junio de 2007 es la 1.6) tiene un tamaño cercano a los 55 MB. El instalador, una vez ejecutado, nos preguntará por las diversas opciones de instalación, a las que contestaremos según nos parezca conveniente.

Solaris

La versión del JDK para Solaris es un fichero tar comprimido. Se necesitan alrededor de 9 Mb de disco para descomprimir el JDK, aunque el doble de espacio sería una cifra más cómoda. Ejecutar los siguientes comandos:

                 % uncompress JDK-beta-solaris2-sparc.tar.Z
                 % tar xvf JDK-beta-solaris2-sparc-tar

Puedes descomprimir el archivo en tu directorio Tutoriales, o,
si tienes privilegios de supervisor,
en algún sitio más conveniente de /usr/local para
que todos los usuarios tengan acceso a los ficheros. Sin embargo,
los privilegios del supervisor no son necesarios para instalar
y ejecutar Java. Por simplicidad,
supondré que has descomprimido el JDK en /usr/local, aunque el path completo
donde se haga no tiene relevancia
(también es posible colocarlo en /opt
que es donde
residen todas las aplicaciones de Solaris).
Si lo has colocado en un sitio diferente,
simplemente sustituye /usr/local por ese directorio
(si lo has descomprimido en tu Tutoriales,
puedes utilizar ~/java y ~/hotjava, en vez del path completo).

Es necesario añadir /usr/local/java/bin a la variable de entorno PATH.
Utiliza el siguiente comando (suponiendo que tengas el shell csh o tcsh):

set path=($PATH /usr/local/java/bin)
También puedes añadir esta línea al final del fichero .profile y .cshrc, y 
ya tienes el sistema listo para ejecutar applets. Si quieres desembarazarte
de la ventana que aparece cada vez que lances el appletviewer
con la licencia de Sun,
crea un directorio que se llame .hotjava en el directorio
java/bin y ya no volverás a verla.

Linux

Para instalar el JDK en linux, debemos descargar el binario correspondiente desde la página de descargas de sun, el que pesa cerca de 50 MB.

Una vez descargado, se procede a la ejecución de este binario. Si no tiene permisos de ejecución, se le deben otorgar con chmod.
Al ejecutar, nos muestra la licencia de Sun, a la que respondemos 'yes' (sin las comillas) una vez que la hayamos leído. Al hacer esto, se crea en el directorio donde está el instalador un nuevo directorio: jdkXYZ, donde XYZ representan la versión del JDK descargada. Posteriormente, se agrega a la variable de entorno PATH la ruta hasta el directorio bin dentro del directorio donde se instaló el JDK.

Si se desea instalar el JDK para todos los usuarios del sistema, una vez terminada la instalación se deben hacer los links simbólicos desde el directorio bin de nuestro directorio de instalación del JDK en el directorio /usr/bin del sistema.

Compilación sin JDK

Parece raro, pero se puede conseguir. Lo único necesario es el navegador Netscape 2.0. Este navegador, junto con la máquina virtual Java (JVM) y el sistema run-time, tiene un compilador Java. Si no se dispone del Java Development Kit (JDK), que no está disponible para todas las plataformas, pero sí de la versión de Netscape para nuestra plataforma, aquí van los pasos a seguir para utilizar el compilador de Java embebido en Netscape.

Como necesito partir de algún punto para tomarlo como referencia, voy a suponer que estamos sobre Linux y que vamos a prescindir del JDK de Randy Chapman. Lo que habría que hacer sería lo siguiente.

Primero. Instalar Netscape en el ordenador. Asegurarse de entender perfectamente y leerse hasta el final el fichero README, para seguir las instrucciones específicas de la instalación de Netscape en la plataforma y que Netscape funcione perfectamente. En nuestro caso, en que vamos a intentar compilar código Java con Netscape sobre Linux, la pieza clave es la situación del fichero moz2_0.zip, que en mi máquina está en /usr/local/netscape/java/classes.

Segundo. Extraer de una copia cualquiera del JDK (aunque sea de otra plataforma), el fichero java/lib/classes.zip y guardarlo en el mismo sitio que el fichero moz2_0.zip; esta localización no es necesaria, pero simplifica la estructura.

Tercero. Fijar la variable de entorno CLASSPATH para que Netscape pueda encontrar sus propias clases además de las clases del Java de Sun. Asegurarse de incluir el "directorio actual", para poder compilar a la vez que se usan los ficheros .zip de Netscape y Sun. Por ejemplo:

setenv CLASSPATH .:/usr/local/netscape/java/classes/moz2_0.zip :
/usr/local/netscape/java/classes/classes.zip

Cuarto. Compilar el código Java (applet o aplicación) con el comando:

netscape -java sun.tools.javac.Main [fichero].java

(sustituir el nombre del fichero con el código Java en vez de [fichero]). Esto convertirá el código fuente Java en byte-code, generándose el archivo [fichero].class.

Quinto. Comprobar si se puede ejecutar la aplicación con el comando:

netscape -java [clase]

(sustituir el nombre de la clase de la aplicación -la que contiene la rutina main- en vez de [clase]).

Sexto. Si se ha compilado un applet Java, construir una página html que lo utilice para visualizarlo con el navegador en su forma normal. O también se puede visualizar utilizando el appletviewer, ejecutando:

netscape -java sun.applet.AppletViewer [clase]

Desgraciadamente, la sentencia anterior no parece funcionar en todos los sistemas. Hay amigos míos que no han sido capaces de visualizar applets con este método.

Para aprovechar el tiempo, se puede crear un script que recoja los pasos 3, 4 y 6. Si estamos utilizando el csh, el contenido del script sería:

#/bin/csh -f setenv CLASSPATH .:/usr/local/netscape/java/classes/moz2_0.zip:
/usr/local/netscape/java/classes/classes.zip netscape -java sun.tools.javac.Main $1

y lo almacenaríamos como javac. Se ha de hacer el script ejecutable y cambiar /bin/csh por el path completo donde esté situado el csh. De forma semejante podemos definir el intérprete java y el appletviewer, sustituyendo la línea adecuada de llamada a Netscape.

CONCEPTOS BÁSICOS DE JAVA

Ahora que ya hemos visto a grandes rasgos lo que Java puede ofrecernos, y antes de entrar a saco en la generación de nuestro primer código Java, vamos a echar un vistazo al lenguaje Java en sí. Lo básico resultará muy familiar a los que tengan conocimientos de C/C++. Los programadores con experiencia en otros lenguajes procedurales reconocerán la mayor parte de las construcciones. Esperemos que este capítulo no resulte demasiado intenso, no obstante, sí debe estar presente, porque más de una vez recurriremos a él como referencia. En posteriores capítulos profundizaremos sobre aspectos de la programación en Java por los que aquí pasaremos de puntillas e iremos presentando ejemplos de código de cada uno de esos aspectos de la programación en Java.

Programación en JAVA

Cuando se programa en Java, se coloca todo el código en métodos, de la misma forma que se escriben funciones en lenguajes como C.

Comentarios

En Java hay tres tipos de comentarios:

 

    // comentarios para una sola línea
/* comentarios de una o más líneas */
/** comentario de documentación, de una o más líneas */

Los dos primeros tipos de comentarios son los que todo programador conoce y se utilizan del mismo modo. Los comentarios de documentación, colocados inmediatamente antes de una declaración (de variable o función), indican que ese comentario ha de ser colocado en la documentación que se genera automáticamente cuando se utiliza la herramienta de Java, javadoc. Dichos comentarios sirven como descripción del elemento declarado permitiendo generar una documentación de nuestras clases escrita al mismo tiempo que se genera el código.

En este tipo de comentario para documentación, se permite la introducción de algunos tokens o palabras clave, que harán que la información que les sigue aparezca de forma diferente al resto en la documentación.

Identificadores

Los identificadores nombran variables, funciones, clases y objetos; cualquier cosa que el programador necesite identificar o usar.

 

En Java, un identificador comienza con una letra, un subrayado (_) o un símbolo de dólar ($). Los siguientes caracteres pueden ser letras o dígitos. Se distinguen las mayúsculas de las minúsculas y no hay longitud máxima.

Serían identificadores válidos:

    identificador
         nombre_usuario
         Nombre_Usuario
         _variable_del_sistema
         $transaccion
 

y su uso sería, por ejemplo:

    int contador_principal;
         char _lista_de_ficheros;
         float $cantidad_en_Ptas;
 

Palabras clave
Las siguientes son las palabras clave que están definidas en Java y que no se pueden utilizar como indentificadores:

    abstract    continue    for         new         switch
         boolean     default     goto        null        synchronized
         break       do          if          package     this
         byte        double      implements  private     threadsafe
         byvalue     else        import      protected   throw
         case        extends     instanceof  public      transient
         catch       false       int         return      true
         char        final       interface   short       try
         class       finally     long        static      void
         const       float       native      super       while
 

Palabras Reservadas
Además, el lenguaje se reserva unas cuantas palabras más, pero que hasta ahora no tienen un cometido específico. Son:

    cast        future      generic     inner
         operator    outer       rest        var

Literales

Un valor constante en Java se crea utilizando una representación literal de él. Java utiliza cinco tipos de elementos: enteros, reales en coma flotante, booleanos, caracteres y cadenas, que se pueden poner en cualquier lugar del código fuente de Java. Cada uno de estos literales tiene un tipo correspondiente asociado con él.

Enteros:

    byte    8 bits    complemento a dos
         short    16 bits    complemento a  dos
         int    32 bits    complemento a dos
         long    64 bits    complemento a dos
         Por ejemplo: 21      077     0xDC00
 

Reales en coma flotante:

    float    32 bits    IEEE 754
         double    64 bits    IEEE 754
         Por ejemplo: 3.14    2e12    3.1E12
 

Booleanos:

    true
         false
 

Caracteres:

    Por ejemplo: a       \t      \u????    [????] es un número unicode
 

Cadenas:

    Por ejemplo: "Esto es una cadena literal"

Arrays

Se pueden declarar en Java arrays de cualquier tipo:

 

    char s[];
         int iArray[];
 

Incluso se pueden construir arrays de arrays:

    int tabla[][] = new int[4][5];
 

Los límites de los arrays se comprueban en tiempo de ejecución para evitar desbordamientos y la corrupción de memoria.

En Java un array es realmente un objeto, porque tiene redefinido el operador []. Tiene una función miembro: length. Se puede utilizar este método para conocer la longitud de cualquier array.

    int a[][] = new int[10][3];
         a.length;         /* 10 */
         a[0].length;      /*  3 */
 

Para crear un array en Java hay dos métodos básicos. Crear un array vacío:

    int lista[] = new int[50];
 

o se puede crear ya el array con sus valores iniciales:

    String nombres[] = {
             "Juan","Pepe","Pedro","Maria"
             };
 

Esto que es equivalente a:

    String nombres[];
         nombres = new String[4];
         nombres[0] = new String( "Juan" );
         nombres[1] = new String( "Pepe" );
         nombres[2] = new String( "Pedro" );
         nombres[3] = new String( "Maria" );
 

No se pueden crear arrays estáticos en tiempo de compilación:

    int lista[50];  // generará un error en tiempo de compilación
 

Tampoco se puede rellenar un array sin declarar el tamaño con el operador new:

    int lista[];
         for( int i=0; i < 9; i++ )
             lista[i] = i;
 

Es decir, todos los arrays en Java son estáticos. Para convertir un array en el equivalente a un array dinámico en C/C++, se usa la clase vector, que permite operaciones de inserción, borrado, etc. en el array.

Operadores

Los operadores de Java son muy parecidos en estilo y funcionamiento a los de C. En la siguiente tabla aparecen los operadores que se utilizan en Java, por orden de precedencia:

 

    .       []      ()
         ++      --
         !       ~       instanceof
         *       /       %
         +       -
         <<      >>      >>>
         <       >       <=       >=       ==       !=
         &       ^       |
         &&      ||
         ?  :
         =       op=     (*=     /=      %=      +=      -=      etc.)    ,
 

Los operadores numéricos se comportan como esperamos:

    int + int = int
 

Los operadores relacionales devuelven un valor booleano.

Para las cadenas, se pueden utilizar los operadores relacionales para comparaciones además de + y += para la concatenación:

    String nombre = "nombre" + "Apellido";
 

El operador = siempre hace copias de objetos, marcando los antiguos para borrarlos, y ya se encargará el garbage collector de devolver al sistema la memoria ocupada por el objeto eliminado.

Separadores

Sólo hay un par de secuencias con otros caracteres que pueden aparecer en el código Java; son los separadores simples, que van a definir la forma y función del código. Los separadores admitidos en Java son:

() - paréntesis. Para contener listas de parámetros en la definición y llamada a métodos. También se utiliza para definir precedencia en expresiones, contener expresiones para control de flujo y rodear las conversiones de tipo.

{} - llaves. Para contener los valores de matrices inicializadas automáticamente. También se utiliza para definir un bloque de código, para clases, métodos y ámbitos locales.

[] - corchetes. Para declarar tipos matriz. También se utiliza cuando se referencian valores de matriz.

; - punto y coma. Separa sentencias.

, - coma. Separa identificadores consecutivos en una declaración de variables. También se utiliza para encadenar sentencias dentro de una sentencia for.

. - punto. Para separar nombres de paquete de subpaquetes y clases. También se utiliza para separar una variable o método de una variable de referencia.

Control del Flujo

Muchas de las sentencias de control del flujo del programa se han tomado del C:



Sentencias de Salto




if( Boolean ) {
sentencias;
}
else {
sentencias;
}



switch( expr1 ) {
case expr2:
sentencias;
break;
case expr3:
sentencias;
break;
default:
sentencias;
break;
}



Sentencias de Bucle




for( expr1 inicio; expr2 test; expr3 incremento ) {
sentencias;
}

El siguiente trocito de código Java que dibuja varias líneas en pantalla alternando sus colores entre rojo, azul y verde. Este fragmento sería parte de una función Java (método):

int contador;
for( contador=1; contador <= 12; contador++ ) {
switch( contador % 3 ) {
case 0:
setColor( Color.red );
break;
case 1:
setColor( Color.blue );
break;
case 2:
setColor( Color.green );
break;
}
g.drawLine( 10,contador*10,80,contador*10 );
}

También se soporta el operador coma (,) en los bucles for

for( a=0,b=0; a < 7; a++,b+=2 )



while( Boolean ) {
sentencias;
}



do {
sentencias;
}while( Boolean );



Excepciones




try {
sentencias;
} catch( Exception ) {
sentencias;
}

Java implementa excepciones para facilitar la construcción de código robusto. Cuando ocurre un error en un programa, el código que encuentra el error lanza una excepción, que se puede capturar y recuperarse de ella. Java proporciona muchas excepciones predefinidas.




Control General del Flujo


break [etiqueta]
continue [etiqueta]
return expr;
etiqueta: sentencia;

En caso de que nos encontremos con bucles anidados, se permite el uso de etiquetas para poder salirse de ellos, por ejemplo:

uno: for( )
{
dos: for( )
{
continue; seguiría en el bucle interno
continue uno;
seguiría en el bucle principal
break uno; se saldría del bucle principal
}
}
return final para salir de esa función, independientemente de que haya otros en medio del código que también provoquen la salida de la función. En caso de no hacerlo se generará un Warning, y el código Java no se puede compilar con Warnings.

int func()
{
if( a
0 ) return 1;
return 0;
}es imprescindible porque se retorna un entero

En el código de una función siempre hay que ser consecuentes con la declaración que se haya hecho de ella. Por ejemplo, si se declara una función para que devuelva un entero, es imprescindible que se coloque un

Clases

Las clases son lo más simple de Java. Todo en Java forma parte de una clase, es una clase o describe como funciona una clase. El conocimiento de las clases es fundamental para poder entender los programas Java.

Todas las acciones de los programas Java se colocan dentro del bloque de una clase o un objeto. Todos los métodos se definen dentro del bloque de la clase, Java no soporta funciones o variables globales. Esto puede despistar a los programadores de C++, que pueden definir métodos fuera del bloque de la clase, pero esta posibilidad es más un intento de no separarse mucho y ser compatible con C, que un buen diseño orientado a objetos. Así pues, el esqueleto de cualquier aplicación Java se basa en la definición de una clase.

Todos los datos básicos, como los enteros, se deben declarar en las clases antes de hacer uso de ellos. En C la unidad fundamental son los ficheros con código fuente, en Java son las clases. De hecho son pocas las sentencias que se pueden colocar fuera del bloque de una clase. La palabra clave import (equivalente al #include) puede colocarse al principio de un fichero, fuera del bloque de la clase. Sin embargo, el compilador reemplazará esa sentencia con el contenido del fichero que se indique, que consistirá, como es de suponer, en más clases.



Tipos de Clases


Hasta ahora sólo se ha utilizado la palabra clave public para calificar el nombre de las clases que hemos visto, pero hay tres modificadores más. Los tipos de clases que podemos definir son:

abstract

Una clase abstract tiene al menos un método abstracto. Una clase abstracta no se instancia, sino que se utiliza como clase base para la herencia.

final

Una clase final se declara como la clase que termina una cadena de herencia. No se puede heredar de una clase final. Por ejemplo, la clase Math es una clase final.

public

Las clases public son accesibles desde otras clases, bien sea directamente o por herencia. Son accesibles dentro del mismo paquete en el que se han declarado. Para acceder desde otros paquetes, primero tienen que ser importadas.

synchronizable

Este modificador especifica que todos los métodos definidos en la clase son sincronizados, es decir, que no se puede acceder al mismo tiempo a ellos desde distintos threads; el sistema se encarga de colocar los flags necesarios para evitarlo. Este mecanismo hace que desde threads diferentes se puedan modificar las mismas variables sin que haya problemas de que se sobreescriban.

Variables y Métodos de Instancia

Una clase en Java puede contener variables y métodos. Las variables pueden ser tipos primitivos como int, char, etc. Los métodos son funciones.
Por ejemplo, en el siguiente trozo de código podemos observarlo:

 public MiClase {
              int i;
              public MiClase() {
                  i = 10;
                  }
              public void Suma_a_i( int j ) {
                  i = i + j;
                  }
              }


La clase MiClase contiene una variable (i) y dos métodos, MiClase que es el constructor de la clase y Suma_a_i( int j ).

Ámbito de una variable

<P>Los bloques de sentencias compuestas en Java se delimitan con dos llaves. Las variables de Java sólo son válidas desde el punto donde están declaradas hasta el final de la sentencia compuesta que la engloba. Se pueden anidar estas sentencias compuestas, y cada una puede contener su propio conjunto de declaraciones de variables locales. Sin embargo, no se puede declarar una variable con el mismo nombre que una de ámbito exterior.

El siguiente ejemplo intenta declarar dos variables separadas con el mismo nombre. En C y C++ son distintas, porque están declaradas dentro de ámbitos diferentes. En Java, esto es ilegal.

Class Ambito {
         int i = 1;        // ámbito exterior
             {             // crea un nuevo ámbito
             int i = 2;    // error de compilación
             }
         }
 


Métodos y Constructores


Los métodos son funciones que pueden ser llamadas dentro de la clase o por otras clases. El constructor es un tipo específico de método que siempre tiene el mismo nombre que la clase.

Cuando se declara una clase en Java, se pueden declarar uno o más constructores opcionales que realizan la inicialización cuando se instancia (se crea una ocurrencia) un objeto de dicha clase.

Utilizando el código de ejemplo anterior, cuando se crea una nueva instancia de MiClase, se crean (instancian) todos los métodos y variables, y se llama al constructor de la clase:

    MiClase mc;
         mc = new MiClase();
 


La palabra clave new se usa para crear una instancia de la clase. Antes de ser instanciada con new no consume memoria, simplemente es una declaración de tipo. Después de ser instanciado un nuevo objeto mc, el valor de i en el objeto mc será igual a 10. Se puede referenciar la variable (de instancia) i con el nombre del objeto:

    mc.i++; // incrementa la instancia de i de mc
 


Al tener mc todas las variables y métodos de MiClase, se puede usar la primera sintaxis para llamar al método Suma_a_i() utilizando el nuevo nombre de clase mc:

    mc.Suma_a_i( 10 );
 

y ahora la variable mc.i vale 21.


Finalizadores


Java no utiliza destructores (al contrario que C++) ya que tiene una forma de recoger automáticamente todos los objetos que se salen del alcance. No obstante proporciona un método que, cuando se especifique en el código de la clase, el reciclador de memoria (garbage collector) llamará:

    // Cierra el canal cuando este objeto es reciclado
         protected void finalize() {
             close();

}</PRE>""

Alcance de Objetos y Reciclado de Memoria

Los objetos tienen un tiempo de vida y consumen recursos durante el mismo. Cuando un objeto no se va a utilizar más, debería liberar el espacio que ocupaba en la memoria de forma que las aplicaciones no la agoten (especialmente las grandes).

En Java, la recolección y liberación de memoria es responsabilidad de un thread llamado automatic garbage collector (recolector automático de basura). Este thread monitoriza el alcance de los objetos y marca los objetos que se han salido de alcance. Veamos un ejemplo:

String s; no se ha asignado todavia s = new String( "abc" ); memoria asignada s = "def"; se ha asignado nueva memoria (nuevo objeto)

Más adelante veremos en detalle la clase String, pero una breve descripción de lo que hace esto es; crear un objeto String y rellenarlo con los caracteres "abc" y crear otro (nuevo) String y colocarle los caracteres "def".

En esencia se crean dos objetos:

Objeto String "abc" Objeto String "def"

Al final de la tercera sentencia, el primer objeto creado de nombre s que contiene "abc" se ha salido de alcance. No hay forma de acceder a él. Ahora se tiene un nuevo objeto llamado s y contiene "def". Es marcado y eliminado en la siguiente iteración del thread reciclador de memoria.

Herencia

La Herencia es el mecanismo por el que se crean nuevos objetos definidos en términos de objetos ya existentes. Por ejemplo, si se tiene la clase Ave, se puede crear la subclase Pato, que es una especialización de Ave.

class Pato extends Ave { int numero_de_patas; }

La palabra clave extends se usa para generar una subclase (especialización) de un objeto. Una Pato es una subclase de Ave. Cualquier cosa que contenga la definición de Ave será copiada a la clase Pato, además, en Pato se pueden definir sus propios métodos y variables de instancia. Se dice que Pato deriva o hereda de Ave.

Además, se pueden sustituir los métodos proporcionados por la clase base. Utilizando nuestro anterior ejemplo de MiClase, aquí hay un ejemplo de una clase derivada sustituyendo a la función Suma_a_i():

import MiClase; public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); } }

Ahora cuando se crea una instancia de MiNuevaClase, el valor de i también se inicializa a 10, pero la llamada al método Suma_a_i() produce un resultado diferente:

MiNuevaClase mnc; mnc = new MiNuevaClase(); mnc.Suma_a_i( 10 );

En Java no se puede hacer herencia múltiple. Por ejemplo, de la clase aparato con motor y de la clase animal no se puede derivar nada, sería como obtener el objeto toro mecánico a partir de una máquina motorizada (aparato con motor) y un toro (aminal). En realidad, lo que se pretende es copiar los métodos, es decir, pasar la funcionalidad del toro de verdad al toro mecánico, con lo cual no sería necesaria la herencia múltiple sino simplemente la compartición de funcionalidad que se encuentra implementada en Java a través de interfaces.

Control de Acceso

Cuando se crea una nueva clase en Java, se puede especificar el nivel de acceso que se quiere para las variables de instancia y los métodos definidos en la clase:

public

public void CualquieraPuedeAcceder(){}
Cualquier clase desde cualquier lugar puede acceder a las variables y métodos de instacia públicos.

protected

protected void SoloSubClases(){}
Sólo las subclases de la clase y nadie más puede acceder a las variables y métodos de instancia protegidos.

private

private String NumeroDelCarnetDeIdentidad;
Las variables y métodos de instancia privados sólo pueden ser accedidos desde dentro de la clase. No son accesibles desde las subclases.

friendly (sin declaración específica)

void MetodoDeMiPaquete(){}
Por defecto, si no se especifica el control de acceso, las variables y métodos de instancia se declaran friendly (amigas), lo que significa que son accesibles por todos los objetos dentro del mismo paquete, pero no por los externos al paquete. Es lo mismo que protected.

Los métodos protegidos (protected) pueden ser vistos por las clases derivadas, como en C++, y también, en Java, por los paquetes (packages). Todas las clases de un paquete pueden ver los métodos protegidos de ese paquete. Para evitarlo, se deben declarar como private protected, lo que hace que ya funcione como en C++ en donde sólo se puede acceder a las variables y métodos protegidos de las clases derivadas.

Variables y Métodos Estaticos

En un momento determinado se puede querer crear una clase en la que el valor de una variable de instancia sea el mismo (y de hecho sea la misma variable) para todos los objetos instanciados a partir de esa clase. Es decir, que exista una única copia de la variable de instancia. Se usará para ello la palabra clave static.

      class Documento extends Pagina {
             static int version = 10;
             }


El valor de la variable version será el mismo para cualquier objeto instanciado de la clase Documento. Siempre que un objeto instanciado de Documento cambie la variable version, ésta cambiará para todos los objetos.

De la misma forma se puede declarar un método como estático, lo que evita que el método pueda acceder a las variables de instancia no estáticas:

 class Documento extends Pagina {
             static int version = 10;
             int numero_de_capitulos;
             static void annade_un_capitulo() {
                 numero_de_capitulos++;        // esto no funciona
                 }
             static void modifica_version( int i ) {
                 version++;                    // esto si funciona
                 }
             }


La modificación de la variable numero_de_capitulos no funciona porque se está violando una de las reglas de acceso al intentar acceder desde un método estático a una variable no estática.

Todas las clases que se derivan, cuando se declaran estáticas, comparten la misma página de variables; es decir, todos los objetos que se generen comparten la misma zona de memoria. Las funciones estáticas se usan para acceder solamente a variables estáticas.

 class UnaClase {
             int var;
             UnaClase()
                 {
                 var = 5;
                 }
             UnaFuncion()
                 {
                 var += 5;
                 }
             }


En el código anterior, si se llama a la función UnaFuncion a través de un puntero a función, no se podría acceder a var, porque al utilizar un puntero a función no se pasa implícitamente el puntero al propio objeto (this). Sin embargo, sí se podría acceder a var si fuese estática, porque siempre estaría en la misma posición de memoria para todos los objetos que se creasen de UnaClase.

This y super

Al acceder a variables de instancia de una clase, la palabra clave this hace referencia a los miembros de la propia clase. Volviendo al ejemplo de MiClase, se puede añadir otro constructor de la forma siguiente:

public class MiClase { int i; public MiClase() { i = 10; } Este constructor establece el valor de i public MiClase( int valor ) { this.i = valor; i = valor } public void Suma_a_i( int j ) { i = i + j; } }

Aquí this.i se refiere al entero i en la clase MiClase.

Si se necesita llamar al método padre dentro de una clase que ha reemplazado ese método, se puede hacer referencia al método padre con la palabra clave super:

import MiClase; public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); super.Suma_a_i( j ); } }

En el siguiente código, el constructor establecerá el valor de i a 10, después lo cambiará a 15 y finalmente el método Suma_a_i() de la clase padre (MiClase) lo dejará en 25:

MiNuevaClase mnc; mnc = new MiNuevaClase(); mnc.Suma_a_i( 10 );

Clases Abstractas

Una de las características más útiles de cualquier lenguaje orientado a objetos es la posibilidad de declarar clases que definen como se utiliza solamente, sin tener que implementar métodos. Esto es muy útil cuando la implementación es específica para cada usuario, pero todos los usuarios tienen que utilizar los mismos métodos. Un ejemplo de clase abstracta en Java es la clase Graphics:

public abstract class Graphics { public abstract void drawLine( int x1,int y1,int x2, int y2 ); public abstract void drawOval( int x,int y,int width, int height ); public abstract void drawArc( int x,int y,int width, int height,int startAngle,int arcAngle ); . . . }

Los métodos se declaran en la clase Graphics, pero el código que ejecutará el método está en algún otro sitio:

public class MiClase extends Graphics { public void drawLine( int x1,int y1,int x2,int y2 ) { <código para pintar líneas -específico de la arquitectura-> } }

Cuando una clase contiene un método abstracto tiene que declararse abstracta. No obstante, no todos los métodos de una clase abstracta tienen que ser abstractos. Las clases abstractas no pueden tener métodos privados (no se podrían implementar) ni tampoco estáticos. Una clase abstracta tiene que derivarse obligatoriamente, no se puede hacer un new de una clase abstracta.

Una clase abstracta en Java es lo mismo que en C++ virtual func() = 0; lo que obliga a que al derivar de la clase haya que implementar forzosamente los métodos de esa clase abstracta.

Interfaces

Los métodos abstractos son útiles cuando se quiere que cada implementación de la clase parezca y funcione igual, pero necesita que se cree una nueva clase para utilizar los métodos abstractos.

Los interfaces proporcionan un mecanismo para abstraer los métodos a un nivel superior.

Un interface contiene una colección de métodos que se implementan en otro lugar. Los métodos de una clase son public, static y final.

La principal diferencia entre interface y abstract es que un interface proporciona un mecanismo de encapsulación de los protocolos de los métodos sin forzar al usuario a utilizar la herencia.

Por ejemplo:

public interface VideoClip { comienza la reproduccion del video void play(); reproduce el clip en un bucle void bucle(); detiene la reproduccion void stop(); }

Las clases que quieran utilizar el interface VideoClip utilizarán la palabra
implements y proporcionarán el código necesario para implementar los métodos que se han definido para el interface:

class MiClase implements VideoClip { void play() { <
código> } void bucle() { <código> } void stop() { <código> }

Al utilizar implements para el interface es como si se hiciese una acción de copiar-y-pegar del código del interface, con lo cual no se hereda nada, solamente se pueden usar los métodos.

La ventaja principal del uso de interfaces es que una clase interface puede ser implementada por cualquier número de clases, permitiendo a cada clase compartir el interfaz de programación sin tener que ser consciente de la implementación que hagan las otras clases que implementen el interface.

class MiOtraClase implements VideoClip { void play() { <
código nuevo> } void bucle() { <código nuevo> } void stop() { <código nuevo> }

Métodos Nativos

Java proporciona un mecanismo para la llamada a funciones C y C++ desde nuestro código fuente Java. Para definir métodos como funciones C o C++ se utiliza la palabra clave native.

public class Fecha { int ahora; public Fecha() { ahora = time(); } private native int time(); static { System.loadLibrary( "time" ); } }

Una vez escrito el código Java, se necesitan ejecutar los pasos siguientes para poder integrar el código C o C++:

  • Utilizar javah para crear un fichero de cabecera (.h)
  • Utilizar javah para crear un fichero de stubs, es decir, que contiene la declaración de las funciones
  • Escribir el código del método nativo en C o C++, es decir, rellenar el código de la función, completando el trabajo de javah al crear el fichero de stubs
  • Compilar el fichero de stubs y el fichero .c en una librería de carga dinámica (DLL en Windows '95 o libXX.so en Unix)
  • Ejecutar la aplicación con el appletviewer

Más adelante trataremos en profundidad los métodos nativos, porque añaden una gran potencia a Java, al permitirle integrar a través de librería dinámica cualquier algoritmo desarrollado en C o C++, lo cual, entre otras cosas, se utiliza como método de protección contra la descompilación completa del código Java.

Paquetes

La palabra clave package permite agrupar clases e interfaces. Los nombres de los paquetes son palabras separadas por puntos y se almacenan en directorios que coinciden con esos nombres.

Por ejemplo, los ficheros siguientes, que contienen código fuente Java:

Applet.java, AppletContext.java, AppletStub.java, AudioClip.java

contienen en su código la línea:

package java.applet;

Y las clases que se obtienen de la compilación de los ficheros anteriores, se encuentran con el nombre nombre_de_clase.class, en el directorio:

java/applet

Import


Los paquetes de clases se cargan con la palabra clave import, especificando el nombre del paquete como ruta y nombre de clase (es lo mismo que #include de C/C++). Se pueden cargar varias clases utilizando un asterisco.

import java.Date; import java.awt.*;

Si un fichero fuente Java no contiene ningún package, se coloca en el paquete por defecto sin nombre. Es decir, en el mismo directorio que el fichero fuente, y la clase puede ser cargada con la sentencia import:

import MiClase;



Paquetes de Java


El lenguaje Java proporciona una serie de paquetes que incluyen ventanas, utilidades, un sistema de entrada/salida general, herramientas y comunicaciones. En la versión actual del JDK, los paquetes Java que se incluyen son:

java.applet


Este paquete contiene clases diseñadas para usar con applets. Hay una clase Applet y tres interfaces: AppletContext, AppletStub y AudioClip.

java.awt


El paquete Abstract Windowing Toolkit (awt) contiene clases para generar widgets y componentes GUI (Interfaz Gráfico de Usuario). Incluye las clases Button, Checkbox, Choice, Component, Graphics, Menu, Panel, TextArea y TextField.

java.io


El paquete de entrada/salida contiene las clases de acceso a ficheros: FileInputStream y FileOutputStream.

java.lang


Este paquete incluye las clases del lenguaje Java propiamente dicho: Object, Thread, Exception, System, Integer, Float, Math, String, etc.

java.net


Este paquete da soporte a las conexiones del protocolo TCP/IP y, además, incluye las clases Socket, URL y URLConnection.

java.util


Este paquete es una miscelánea de clases útiles para muchas cosas en programación. Se incluyen, entre otras, Date (fecha), Dictionary (diccionario), Random (números aleatorios) y Stack (pila FIFO).

Referencias

Java se asemeja mucho a C y C++. Esta similitud, evidentemente intencionada, es la mejor herramienta para los programadores, ya que facilita en gran manera su transición a Java. Desafortunadamente, tantas similitudes hacen que no nos paremos en algunas diferencias que son vitales. La terminología utilizada en estos lenguajes, a veces es la misma, pero hay grandes diferencias subyacentes en su significado.

C tiene tipos de datos básicos y punteros. C++ modifica un poco este panorama y le añade los tipos referencia. Java también especifica sus tipos primitivos, elimina cualquier tipo de punteros y tiene tipos referencia mucho más claros.

Todo este maremágnum de terminología provoca cierta consternación, así que vamos a intentar aclarar lo que realmente significa.

Conocemos ya ampliamente todos los tipos básicos de datos: datos base, integrados, primitivos e internos; que son muy semejantes en C, C++ y Java; aunque Java simplifica un poco su uso a los desarrolladores haciendo que el chequeo de tipos sea bastante más rígido. Además, Java añade los tipos boolean y hace imprescindible el uso de este tipo booleano en sentencias condicionales.

Punteros y Referencias C

Punteros


C y C++ permiten la declaración y uso de punteros, que pueden ser utilizados en cualquier lugar. Esta tremenda flexibilidad resulta muy útil, pero también es la causa de que podamos colgar todo el sistema.

La intención principal en el uso de los punteros es comunicarnos más directamente con el hardware, haciendo que el código se acelere. Desafortunadamente, este modelo de tan bajo nivel hace que perdamos robustez y seguridad en la programación y hace muy difíciles tareas como la liberación automática de memoria, la defragmentación de memoria, o realizar programación distribuida de forma clara y eficiente.



Referencias en C++


Las referencias se incorporaron a C++ en un intento de manejar punteros de C de forma más limpia y segura. Sin embargo, como no elimina los punteros, la verdad es que su propósito lo consigue a medias. Es más, podríamos decir que con las referencias C++, el lenguaje se vuelve más complicado y no es más poderoso que antes.

Las referencias deben ser inicializadas cuando se declaran y no se pueden alterar posteriormente. Esto permite incrementar la eficiencia en tiempo de ejecución sobre la solución basada en punteros, pero es más por las deficiencias de los punteros que por las ventajas de las referencias.

Referencias en Java

Las referencias en Java no son punteros ni referencias como en C++. Este hecho crea un poco de confusión entre los programadores que llegan por primera vez a Java. Las referencias en Java son identificadores de instancias de las clases Java. Una referencia dirige la atención a un objeto de un tipo específico. No tenemos por qué saber cómo lo hace ni necesitamos saber qué hace ni, por supuesto, su implementación.

Pensemos en una referencia como si se tratase de la llave electrónica de la habitación de un hotel. Vamos a utilizar precisamente este ejemplo del Hotel para demostrar el uso y la utilización que podemos hacer de las referencias en Java. Primero crearemos la clase Habitacion, implementada en el fichero Habitacion.java, mediante instancias de la cual construiremos nuestro Hotel:

public class Habitacion { private int numHabitacion; private int numCamas; public Habitacion() { habitacion( 0 ); } public Habitacion( int numeroHab ) { habitacion( numeroHab ); } public Habitacion( int numeroHab,int camas ) { habitacion( numeroHab ); camas( camas ); } public synchornized int habitacion() { return( numHabitacion ); } public synchronized void habitacion( int numeroHab ) { numHabitacion = numeroHab; } public synchronized int camas() { return( camas ); } public syncronized void camas( int camas ) { numCamas = camas; } }

El código anterior sería el corazón de la aplicación. Vamos pues a construir nuestro Hotel creando Habitaciones y asignándole a cada una de ellas su llave electrónica; tal como muestra el código siguiente, Hotel1.java:

public class Hotel1 { public static void main( String args[] ) { Habitacion llaveHab1; paso 1 Habitacion llaveHab2; llaveHab1 = new Habitacion( 222 ); pasos 2, 3, 4 y 5 llaveHab2 = new Habitacion( 1144,3 ); ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^ A B y D C } }

Para explicar el proceso, dividimos las acciones en los cinco pasos necesarios para poder entrar en nuestra habitación. Aunque no se incluye, podemos también considerar el caso de que necesitemos un cerrajero, para que cuando perdamos la llave, nos abra la puerta; y que en nuestro caso sería el garbage collector, que recicla la habitación una vez que se hayan perdido todas las llaves.

El primer paso es la creación de la llave, es decir, definir la variable referencia, por defecto nula.

El resto de los pasos se agrupan en una sola sentencia Java. La parte B en el código anterior indica al gerente del Hotel que ya dispone de una nueva habitación. La parte C llama al decorador de interiores para que "vista" la habitación según un patrón determinado, para que no desentonen unas habitaciones con otras y no se pierdan las señas de identidad del hotel. El código electrónico que nos permitirá acceder a la habitación se genera en la parte D, una vez conocido el interior de la habitación y se programa en la llave en la parte A.

Si dejamos el ejemplo real a un lado y nos vamos a lo que ocurre en la ejecución del código, vemos que el operador new busca espacio para una instancia de un objeto de una clase determinada e inicializa la memoria a los valores adecuados. Luego invoca al método constructor de la clase, proporcionándole los argumentos adecuados. El operador new devuelve una referencia a sí mismo, que es inmediatamente asignada a la variable referencia.

Podemos tener múltiples llaves para una misma habitación:

. . . Habitacion llaveHab3,llaveHab4; llaveHab3 = llaveHab1; llaveHab4 = llavehab2;

De este modo conseguimos copias de las llaves. Las habitaciones en sí mismas no se han tocado en este proceso. Así que, ya tenemos dos llaves para la habitación 222 y otras dos para la habitación 1144.

Una llave puede ser programada para que funcione solamente con una habitación en cualquier momento, pero podemos cambiar su código electrónico para que funcione con alguna otra habitación; por ejemplo, para cambiar una habitación anteriormente utilizada por un empedernido fumador por otra limpia de olores y con vistas al mar. Cambiemos pues la llave duplicada de la habitación del fumador (la 222) por la habitación con olor a sal marina, 1144:

. . . llaveHab3 = llaveHab2;

Ahora tenemos una llave para la habitación 222 y tres para la habitación 1144. Mantendremos una llave para cada habitación en la conserjería, para poder utilizarla como llave maestra, en el caso de que alguien pierda su llave propia.

Alguien con la llave de una habitación puede hacer cambios en ella, y los compañeros que tengan llave de esa misma habitación, no tendrán conocimiento de esos cambios hasta que vuelvan a entrar en la habitación. Por ejemplo, vamos a quitar una de las camas de la habitación, entrando en ella con la llave maestra:

. . . llaveHab2.camas( 2 );

Ahora cuando los inquilinos entren en la habitación podrán comprobar el cambio realizado:

. . . llaveHab4.printData();

Referencias y Arrays

Como en C y C++, Java dispone de arrays de tipos primitivos o de clases. Los arrays en C y C++ son básicamente un acompañante para los punteros. En Java, sin embargo, son ciudadanos de primera clase.

Vamos a expandir nuestro hotel creando todo un ala de habitaciones, Hotel2.java. Crearemos un juego de llaves maestras y luego construiremos las habitaciones:

public class Hotel2 { Número de habitaciones por ala public static final int habPorAla = 12; public static void main( String args[] ) { Habitacion llavesMaestras[]; paso 1 llavesMaestras = new Habitacion[ habPorAla ]; pasos 2-5 ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ A B, C, D y E int numPiso = 1; for( int i=0; i < habPorAla; i++ ) pasos 6-9 llavesMaestras[ i ] = new Habitacion( numPiso * 100 + i, ( 0
(i%2)) ? 2 : 1 ); for( int i=0; i < habPorAla; i++ ) pasos 10-11 llavesMaestras[i].printData(); } }
Cada paso en el ejemplo es semejante al que ya vimos antes. El paso 1 especifica que el juego de llaves maestras es un grupo de llaves de habitaciones.

Los pasos 2 a 5 son, en este caso, la parte principal. En lugar de crear una habitación, el gerente ordena construir un grupo contiguo de habitaciones. El número de llaves se especifica entre corchetes y todas se crean en blanco.

Los pasos 6 a 9 son idénticos a los pasos 2 a 5 del ejemplo anterior, excepto en que en este caso todas las llaves pasan a formar parte del juego maestro. Los números de piso se dan en miles para que cuando se creen las habitaciones, todas tengan el mismo formato. También todas las habitaciones de número par tienen una sola cama, mientras que las habitaciones impares tendrán dos camas.

Los pasos 10 y 11 nos permiten obtener información de cada una de las habitaciones.

Referencias y Listas

Hay gente que piensa que como Java no dispone de punteros, resulta demasiado complejo construir listas enlazadas, árboles binarios y grafos. Vamos a demostrar que quien así piense está bastante equivocado.

Retomemos el ejemplo de los arrays, y en vez de éstos vamos a usar una lista doblemente enlazada. El paquete de la lista simple se compone de dos clases. Cada elemento de la lista es un NodoListaEnlazada, NodoListaEnlazada.java:

public class NodoListaEnlazada { private NodoListaEnlazada siguiente; private NodoListaEnlazada anterior; private Object datos; . . . }

Cada NodoListaEnlazada contiene una referencia a su nodo precedente en la lista y una referencia al nodo que le sigue. También contiene una referencia genérica a cualquier clase que se use para proporcionar acceso a los datos que el usuario proporcione.

La lista enlazada, ListaEnlazada.java, contiene un nodo principio-fin y un contador para el número de nodos en la lista:

public class ListaEnlazada { public NodoListaEnlazada PrincipioFin; private int numNodos;
. . . }

El nodo especial PrincipioFin es sencillo, para simplificar el código. El contador se usa para optimizar los casos más habituales.

Revisemos pues el código de nuestro Hotel, ahora Hotel3.java, que será prácticamente el mismo que en el caso de los arrays:

public class Hotel3 { Número de habitaciones por ala public static final int habPorAla = 12; public static void main( String args[] ) { ListaEnlazada llaveMaestra; paso 1 llaveMaestra = new ListaEnlazada(); pasos 2-5 int numPiso = 1; for( int i=0; i < habPorAla; i++ ) pasos 6-9 llaveMaestra.insertAt( i, new Habitacion( numPiso * 100 + i, ( 0
(i%2)) ? 2 : 1 ); for( int i=0; i < habPorAla; i++ ) pasos 10-12 ( (Habitacion)llaveMaestra.getAt(i) ).printData(); } }
El paso 1 es la llave maestra de la lista. Está representada por una lista genérica; es decir, una lista de llaves que cumple la convención que nosotros hemos establecido. Podríamos acelerar el tiempo de compilación metiendo la lista genérica ListaEnlazada dentro de una ListaEnlazadaHabitacion.

Los pasos 2 a 5 son equivalentes a los del primer ejemplo. Construimos e inicializamos una nueva ListaEnlazada, que usaremos como juego de llaves maestras.

Los pasos 6 a 9 son funcionalmente idénticos a los del ejemplo anterior con arrays, pero con diferente sintaxis. En Java, los arrays y el operador [] son internos del lenguaje. Como Java no soporta la sobrecarga de operadores por parte del usuario, tenemos que usarlo siempre en su forma normal.

La ListaEnlazada proporciona el método
insertAt() que coge el índice en la lista, donde el nuevo nodo ha de ser insertado, como primer argumento. El segundo argumento es el objeto que será almacenado en la lista. Obsérvese que no es necesario colocar moldeo alguno para hacer algo a una clase descendiente que depende de uno de sus padres.

Los pasos 10 a 12 provocan la misma salida que los pasos 10 y 11 del ejemplo con arrays. El paso 10 coge la llave del juego que se indica en el método
getAt(). En este momento, el sistema no sabe qué datos contiene la llave, porque el contenido de la habitación es genérico. Pero nosotros sí sabemos lo que hay en la lista, así que informamos al sistema haciendo un moldeado a la llave de la habitación (este casting generará un chequeo en tiempo de ejecución por el compilador, para asegurarse de que se trata de una Habitacion). El paso 12 usa la llave para imprimir la información.

Punteros C/C++ y Referencias Java

Ahora que ya sabemos un poco más sobre las referencias en Java, vamos a compararlas con los punteros de C y C++.

Los punteros en C y C++ están orientados hacia un modelo físico de funcionamiento. Es decir, que el modelo de punteros se mapea directamente sobre el modelo hardware. Este modelo asume cosas como el no movimiento, lo que hace que mecanismos como la liberación automática resulten mucho menos eficientes o simplemente imposibles. Cosas como la distribución en redes y la persistencia de objetos son muy difíciles de conseguir en C y C++. Aunque no hay implementaciones en Java, por ahora, para la persistencia y la distribución, la característica opaca de las referencias en Java hace que el soporte para estas prestaciones sea mucho más simple.

C y C++ permiten el uso de punteros de tal forma que podemos corromper el sistema, cosa que no puede suceder con las referencias en Java. Cualquier intento de hacer esto sería abortado por el compilador o por el sistema en ejecución (lanzando una excepción). C y C++ dejan la protección de memoria al sistema operativo, que solamente tiene el recurso de generar un error del sistema cuando un puntero accede a una posición no válida. Por el contrario, con el uso de las referencias, Java nos protege contra nuestras propias tendencias autodestructivas.

PROGRAMAS BÁSICOS EN JAVA

Como cualquier otro lenguaje, Java se usa para crear aplicaciones. Pero, también Java tiene la particularidad especial de poder crear aplicaciones muy especiales, son los applets, que es una mini (let) aplicación (app) diseñada para ejecutarse en un navegador. Vamos a ver en detalle lo mínimo que podemos hacer en ambos casos.

Una mínima aplicación en Java

La aplicación más pequeña posible es la que simplemente imprimir un mensaje en la pantalla. Tradicionalmente, el mensaje suele ser "Hola Mundo!". Esto es justamente lo que hace el siguiente fragmento de código:

Aplicación HolaMundo de ejemplo class HolaMundoApp { public static void main( String args[] ) { System.out.println( "Hola Mundo!" ) ; } }


HolaMundo Aplicación HolaMundo de ejemplo

Estas tres primera líneas son comentarios. Hay tres tipos de comentarios en Java, es un comentario orientado a línea.

class HolaMundoApp {

Esta línea declara la clase HolaMundoApp. El nombre de la clase especificado en el fichero fuente se utiliza para crear un fichero
nombredeclase.class en el directorio en el que se compila la aplicación. En nuestro caso, el compilador creará un fichero llamado HolaMundoApp.class.

public static void main( String args[] ) {

Esta línea especifica un método que el intérprete Java busca para ejecutar en primer lugar. Igual que en otros lenguajes, Java utiliza una palabra clave
main para especificar la primera función a ejecutar. En este ejemplo tan simple no se pasan argumentos.

significa que el método
publicmain puede ser llamado por cualquiera, incluyendo el intérprete Java.

es una palabra clave que le dice al compilador que
staticmain se refiere a la propia clase HolaMundoApp y no a ninguna instancia de la clase. De esta forma, si alguien intenta hacer otra instancia de la clase, el método main no se instanciaría.

indica que
voidmain no devuelve nada. Esto es importante ya que Java realiza una estricta comprobación de tipos, incluyendo los tipos que se ha declarado que devuelven los métodos.

es la declaración de un array de Strings. Estos son los argumentos escritos tras el nombre de la clase en la línea de comandos:

%java HolaMundoApp arg1 arg2 ... System.out.println( "Hola Mundo!" );

Esta es la funcionalidad de la aplicación. Esta línea muestra el uso de un nombre de clase y método. Se usa el método
args[]println() de la clase out que está en el paquete System.

El método
println() toma una cadena como argumento y la escribe en el stream de salida estándar; en este caso, la ventana donde se lanza la aplicación.

} }

Finalmente, se cierran las llaves que limitan el método
main() y la clase HolaMundoApp


Vamos ver en detalle la aplicación anterior, línea a línea. Esas líneas de código contienen los componenetes mínimos para imprimir Hola Mundo! en la pantalla.

Compilación y Ejecución de HolaMundo

Vamos a ver a continuación como podemos ver el resultado de nuestra primera aplicación Java en pantalla. Generaremos un fichero con el código fuente de la aplicación, lo compilaremos y utilizaremos el intérprete java para ejecutarlo.




Ficheros Fuente Java


Los ficheros fuente en Java terminan con la extensión ".java". Crear un fichero utilizando cualquier editor de texto ascii que tenga como contenido el código de las ocho líneas de nuestra mínima aplicación, y salvarlo en un fichero con el nombre de HolaMundoApp.java. Para crear los ficheros con código fuente Java no es necesario un procesador de textos, aunque puede utilizarse siempre que tenga salida a fichero de texto plano o ascii, sino que es suficiente con cualquier otro editor.




Compilación


El compilador javac se encuentra en el directorio bin por debajo del directorio java, donde se haya instalado el JDK. Este directorio bin, si se han seguido las instrucciones de instalación, debería formar parte de la variable de entorno PATH del sistema. Si no es así, tendría que revisar la Instalación del JDK. El compilador de Java traslada el código fuente Java a byte-codes, que son los componentes que entiende la Máquina Virtual Java que está incluida en los navegadores con soporte Java y en appletviewer.

Una vez creado el fichero fuente HolaMundoApp.java, se puede compilar con la línea siguiente:

%javac HolaMundoApp.java

Si no se han cometido errores al teclear ni se han tenido problemas con el path al fichero fuente ni al compilador, no debería aparecer mensaje alguno en la pantalla, y cuando vuelva a aparecer el prompt del sistema, se debería ver un fichero HolaMundoApp.class nuevo en el directorio donde se encuentra el fichero fuente.

Si ha habido algún problema, en Problemas de compilación al final de esta sección, hemos intentado reproducir los que más frecuentemente se suelen dar, se pueden consultar por si pueden aportar un poco de luz al error que haya aparecido.




Ejecución


Para ejecutar la aplicación HolaMundoApp, hemos de recurrir al intérprete java, que también se encuentra en el directorio bin, bajo el directorio java. Se ejecutará la aplicación con la línea:

%java HolaMundoApp

y debería aparecer en pantalla la respuesta de Java:

%Hola Mundo!

El símbolo % representa al prompt del sistema, y lo utilizaremos para presentar las respuestas que nos ofrezca el sistema como resultado de la ejecución de los comandos que se indiquen en pantalla o para indicar las líneas de comandos a introducir.




PROBLEMAS DE COMPILACIÓN


A continuación presentamos una lista de los errores más frecuentes que se presentan a la hora de compilar un fichero con código fuente Java, nos basaremos en errores provocados sobre nuestra mínima aplicación Java de la sección anterior, pero podría generalizarse sin demasiados problemas.


%javac: Command not found

No se ha establecido correctamente la variable PATH del sistema para el compilador javac. El compilador javac se encuentra en el directorio bin, que cuelga del directorio java, que cuelga del directorio donde se haya instalado el JDK (Java Development Kit).

%HolaMundoApp.java:3: Method printl(java.lang.String) not found in class java.io.PrintStream. System.out.printl( "HolaMundo!); ^

Error tipográfico, el método es println no printl.

%In class HolaMundoApp: main must be public and static

Error de ejecución, se olvidó colocar la palabra static en la declaración del método main de la aplicación.

%Can´t find class HolaMundoApp

Este es un error muy sutil. Generalmente significa que el nombre de la clase es distinto al del fichero que contiene el código fuente, con lo cual el fichero nombre_fichero.class que se genera es diferente del que cabría esperar. Por ejemplo, si en nuestro fichero de código fuente de nuestra aplicación HolaMundoApp.java colocamos en vez de la declaración actual de la clase HolaMundoApp, la línea:

class HolaMundoapp {

se creará un fichero HolaMundoapp.class, que es diferente del HolaMundoApp.class, que es el nombre esperado de la clase; la diferencia se encuentra en la a minúscula y mayúscula.

El visor de Applets de Sun (appletviewer)

El visualizador de applets (appletviewer) es una aplicación que permite ver en funcionamiento applets, sin necesidad de la utilización de un navegador World-Wide-Web como HotJava, Microsoft Explorer o Nescape. En adelante, recurriremos muchas veces a él, ya que el objetivo del tutorial es el lenguaje Java.




Applet


La definición más extendida de applet, muy bien resumida por Patrick Naughton, indica que un applet es "una pequeña aplicación accesible en un servidor Internet, que se transporta por la red, se instala automáticamente y se ejecuta in situ como parte de un documento web". Claro que así la definición establece el entorno (Internet, Web, etc.). En realidad, un applet es una aplicación pretendidamente corta (nada impide que ocupe más de un gigabyte, a no ser el pensamiento de que se va a transportar por la red y una mente sensata) basada en un formato gráfico sin representación independiente: es decir, se trata de un elemento a embeber en otras aplicaciones; es un componente en su sentido estricto.

Un ejemplo en otro ámbito de cosas podría ser el siguiente: Imaginemos una empresa, que cansada de empezar siempre a codificar desde cero, diseña un formulario con los datos básicos de una persona (nombre, dirección, etc.). Tal formulario no es un diálogo por sí mismo, pero se podría integrar en diálogos de clientes, proveedores, empleados, etc. El hecho de que se integre estática (embebido en un ejecutable) o dinámicamente (intérpretes, DLLs, etc.) no afecta en absoluto a la esencia de su comportamiento como componente con que construir diálogos con sentido autónomo.

Pues bien, así es un applet. Lo que ocurre es que, dado que no existe una base adecuada para soportar aplicaciones industriales Java en las que insertar nuestras miniaplicaciones (aunque todo se andará), los applets se han construido mayoritariamente, y con gran acierto comercial (parece), como pequeñas aplicaciones interactivas, con movimiento, luces y sonido... en Internet.



Llamadas a Applets con appletviewer


Un applet es una mínima aplicación Java diseñada para ejecutarse en un navegador Web. Por tanto, no necesita preocuparse por un método main() ni en dónde se realizan las llamadas. El applet asume que el código se está ejecutando desde dentro de un navegador. El appletviewer se asemeja al mínimo navegador. Espera como argumento el nombre del fichero html que debe cargar, no se le puede pasar directamente un programa Java. Este fichero html debe contener una marca que especifica el código que cargará el appletviewer:

<HTML> <APPLET CODE=HolaMundo.class WIDTH=300 HEIGHT=100> </APPLET> </HTML>

El appletviewer crear un espacio de navegación, incluyendo un área gráfica, donde se ejecutará el applet, entonces llamará a la clase applet apropiada. En el ejemplo anterior, el appletviewer cargará una clase de nombre HolaMundo y le permitirá trabajar en su espacio gráfico.

Arquitectura de appletviewer

El appletviewer representa el mínimo interfaz de navegación. En la figura se muestran los pasos que seguiría appletviewer para presentarnos el resultado de la ejecución del código de nuestra clase.

Esta es una visión simplificada del appletviewer. La función principal de esta aplicación es proporcionar al usuario un objeto de tipo Graphics sobre el que dibujar, y varias funciones para facilitar el uso del objeto Graphics.

 

Ciclo de vida de un Applet

Cuando un applet se carga en el appletviewer, comienza su ciclo de vida, que pasaría por las siguientes fases:

 

  • Se crea una instancia de la clase que controla el applet. En el ejemplo de la figura anterior, sería la clase HolaMundo.
  • El applet se incializa.
  • El applet comienza a ejecutarse.
  • El applet empieza a recibir llamadas. Primero recibe una llamada init (inicializar), seguida de un mensaje start (empezar) y paint (pintar). Estas llamadas pueden ser recibidas asíncronamente.

Un Applet basico en Java

Vamos a comenzar la creación del código fuente del un applet que satisfaga nuestras necesidades. Recordamos que Java utiliza la extensión .java para designar los ficheros fuente.




HolaMundo


A continuación está el código fuente del applet HolaMundo, que es la versión applet de la mínima aplicación Java que antes habíamos escrito. Guardar este código en un fichero fuente Java como HolaMundo.java.

Applet HolaMundo de ejemplo import java.awt.Graphics; import java.applet.Applet; public class HolaMundo extends Applet { public void paint( Graphics g ) { g.drawString( "Hola Mundo!",25,25 ) ; } }





El lenguaje Java implementa un modelo de Programación Orientada a Objetos. Los objetos sirven de bloques centrales de construcción de los programas Java. De la misma forma que otros lenguajes de programación, Java tiene variables de estado y métodos.

Veamos como se descompone un applet en sus piezas/objetos:

/* Sección de importaciones */ public class NombreDelNuevoApplet extends Applet { /* Aquí se declaran las variables de estado (public y private) */ /* Los métodos para la interacción con los objetos se declaran y definen aquí */ public void MetodoUno( parámetros ) { /* Aquí viene para cada método, el código Java que desempeña la tarea. Qué código se use depende del applet */ } }

Para HolaMundo, se importan las dos clases que necesita. No hay variables de estado, y sólo se tiene que definir un método para que el applet tenga el comportamiento esperado.



El comando import carga otras clases dentro de nuestro código fuente. El importar una clase desde un paquete de Java hace que esa clase importada esté disponible para todo el código incluido en el fichero fuente Java que la importa. Por ejemplo, en el applet HolaMundo se importa la clase Componentes básicos de un Applet java.awt.Graphics, y podremos llamar a los métodos de esta clase desde cualquier método de nuestro programa que se encuentre en el fichero HolaMundo.java. Esta clase define una área gráfica y métodos para poder dibujar dentro de ella. La función paint() declara a g como un objeto de tipo Graphics; luego, paint() usa el método drawString() de la clase Graphics para generar su salida.



Se puede crear una nueva clase, en este caso HolaMundo, extendiendo la clase básica de Java: Applet. De esta forma, se hereda todo lo necesario para crear un applet. Modificando determinados métodos del applet, podemos lograr que lleve a cabo las funciones que deseamos.

import java.applet.Applet; . . . public class HolaMundo extends Applet {




La parte del applet a modificar es el método
paint(). En la clase Applet, se llama al método paint() cada vez que el método arranca o necesita ser refrescado, pero no hace nada. En nuestro caso, lo que hacemos es:

public void paint( Graphics g ) { g.drawString( "Hola Mundo!",25,25 ); }

De acuerdo a las normas de sobrecarga, se ejecutará este último
paint() y no el paint() vacío de la clase Applet. Luego, aquí se ejecuta el método drawString(), que le dice al applet cómo debe aparecer un texto en el  área de dibujo.

Otros métodos básicos para dibujar son:

drawLine( int x1,int y1,int x2,int y2 ) drawRect( int x,int y,int ancho,int alto ) drawOval( int x,int y,int ancho,int alto )

Tanto para
drawRect() como para drawOval(), las coordenadas (x,y) son la esquina superior izquierda del rectángulo (para drawOval, el óvalo es encajado en el rectángulo que lo circunscribe).

Compilación de un Applet

Ahora que tenemos el código de nuestro applet básico y el fichero fuente Java que lo contiene, necesitamos compilarlo y obtener un fichero .class ejecutable. Se utiliza el compilador Java, javac, para realizar la tarea. El comando de compilación será:

%javac HolaMundo.java

Eso es todo. El compilador javac generará un fichero HolaMundo.class que podrá ser llamado desde cualquier navegador con soporte Java y, por tanto, capaz de ejecutar applets Java.



Llamada a Applets


¿Qué tienen de especial HotJava, Microsoft Explorer o Netscape con respecto a otros navegadores? Con ellos se puede ver html básico y acceder a todo el texto, gráfico, sonido e hipertexto que se pueda ver con cualquier otro navegador. Pero además, pueden ejecutar applets, que no es html estándar. Ambos navegadores entienden código html que lleve la marca <APPLET>:

<APPLET CODE="SuCodigo.class" WIDTH=100 HEIGHT=50> </APPLET>

Esta marca html llama al applet SuCodigo.class y establece su ancho y alto inicial. Cuando se acceda a la página Web donde se encuentre incluida la marca, se ejecutará el byte-code contenido en SuCodigo.class, obteniéndose el resultado de la ejecución del applet en la ventana del navegador, con soporte Java, que estemos utilizando.



Prueba de un Applet


El JDK, Kit de Desarrollo de Java, incluye el visor de applets básico, //appletviewer//, que puede utilizarse para la visualización rápida y prueba de nuestros applets, tal como se ha visto ya. La ejecución de un applet sobre appletviewer se realiza a través de la llamada:

%appletviewer fichero.html

En nuestro caso el fichero con el código html que ejecutará nuestro applet HolaMundo es HolaMundo.html que generará la salida que se mostraba en la sección sobre el Ejemplo de uso de appletviewer.

La marca APPLET de html
Dado que los applets están mayormente destinados a ejecutarse en navegadores Web, había que preparar el lenguaje HTML para soportar Java, o mejor, los applets. El esquema de marcas de HTML, y la evolución del estándar marcado por Netscape hicieron fácil la adición de una nueva marca que permitiera, una vez añadido el correspondiente código gestor en los navegadores, la ejecución de programas Java en ellos.

La sintaxis de las etiquetas <APPLET> y <PARAM> es la que se muestra a continuación y que iremos explicando en párrafos posteriores:

<APPLET CODE= WIDTH= HEIGTH= [CODEBASE=] [ALT=] [NAME=] [ALIGN=] [VSPACE=] [HSPACE=]> <PARAM NAME= VALUE= > </APPLET>


//**Atributos obligatorios**//:

CODE : Nombre de la clase principal

WIDTH : Anchura inicial

HEIGHT : Altura inicial

//**Atributos opcionales**//:

CODEBASE : URL base del applet

ALT : Texto alternativo

NAME : Nombre de la instancia

ALIGN : Justificación del applet

VSPACE : Espaciado vertical

HSPACE : Espaciado horizontal

Los applets se incluyen en las páginas Web a través de la marca <APPLET>, que para quien conozca html resultará muy similar a la marca <IMG>. Ambas necesitan la referencia a un fichero fuente que no forma parte de la página en que se encuentran embebidos. IMG hace esto a través de SRC=parámetro y APPLET lo hace a través CODE=parámetro. El parámetro de CODE indica al navegador dónde se encuentra el fichero con el código Java compilado .class. Es una localización relativa al documento fuente.

Por razones que no entiendo muy bien, pero posiblemente relacionadas con los packages y classpaths, si un applet reside en un directorio diferente del que contiene a la página en que se encuentra embebido, entonces no se indica un URL a esta localización, sino que se apunta al directorio del fichero .class utilizando el parámetro CODEBASE, aunque todavía se puede usar CODE para proporcionar el nombre del fichero .class.

Al igual que IMG, APPLET tiene una serie de parámetros que lo posicionan en la página. WIDTH y HEIGHT especifican el tamaño del rectángulo que contendrá al applet, se indican en pixels. ALIGN funciona igual que con IMG (en los navegadores que lo soportan), definiendo cómo se posiciona el rectángulo del applet con respecto a los otros elementos de la página. Los valores posibles a especificar son: LEFT, RIGHT, TOP, TEXTTOP, MIDDLE, ABSMIDDLE, BASELINE, BOTTOM y ABSBOTTOM. Y, finalmente, lo mismo que con IMG, se puede especificar un HSPACE y un VSPACE en pixels para indicar la cantidad de espacio vacío que habrá de separación entre el applet y el texto que le rodea.

APPLET tiene una marca ALT. La utilizaría un navegador que entendiese la marca APPLET, pero que por alguna razón, no pudiese ejecutarlo. Por ejemplo, si un applet necesita escribir en el disco duro de nuestro ordenador, pero en las características de seguridad tenemos bloqueada esa posibilidad, entonces el navegador presentaría el texto asociado a ALT.

ALT no es utilizado por los navegadores que no entienden la marca APPLET, por ello se ha definido la marca </APPLET>, que finaliza la descripción del applet. Un navegador con soporte Java ignorará todo el texto que haya entre las dos marcas <APPLET> y </APPLET>, sin embargo, un navegador que no soporte Java ignorará las marcas y presentará el texto que se encuentre entre ellas.

Atributos de APPLET

Los atributos que acompañan a la etiqueta <APPLET>, algunos son obligatorios y otros son opcionales. Todos los atributos, siguiendo la sintaxis de html, se especifican de la forma: atributo=valor. Los son:


CODE

Indica el fichero de clase ejecutable, que tiene la extensión .class. No se permite un URL absoluto, como ya se ha dicho, aunque sí puede ser relativo al atributo opcional CODEBASE.

WIDTH

Indica la anchura inicial que el navegador debe reservar para el applet en pixels.

HEIGHT

Indica la altura inicial en pixels. Un applet que disponga de una geometría fija no se verá redimensionado por estos atributos. Por ello, si los atributos definen una zona menor que la que el applet utiliza, únicamente se verá parte del mismo, como si se visualiza a través de una ventana, eso sí, sin ningún tipo de desplazamiento.

Los que pueden acompañar a la marca APPLET comprenden los que se indican a continuación:

CODEBASE

Se emplea para utilizar el URL base del applet. En caso de no especificarse, se utilizará el mismo que tiene el documento.

ALT

Como ya se ha dicho, funciona exactamente igual que el ALT de la marca <IMG>, es decir, muestra un texto alternativo, en este caso al applet, en navegadores en modo texto o que entiendan la etiqueta APPLET pero no implementen una máquina virtual Java.

NAME

Otorga un nombre simbólico a esta instancia del applet en la página que puede ser empleado por otros applets de la misma página para localizarlo. Así, un applet puede ser cargado varias veces en la misma página tomando un nombre simbólico distinto en cada momento.

ALIGN

Se emplea para alinear el applet permitiendo al texto fluir a su alrededor. Puede tomas los siguientes valores: LEFT, RIGHT, TOP, TEXTTOP, MIDDLE, ABSMIDDLE, BASELINE, BOTTOM y ABSBOTTOM.

VSPACE

Indica el espaciado vertical entre el applet y el texto, en pixels. Sólo funciona cuando se ha indicado ALIGN = LEFT o RIGHT.

HSPACE

Funciona igual que el anterior pero indicando espaciamiento horizontal, en pixels. Sólo funciona cuando se ha indicado ALIGN = LEFT o RIGHT.

Es probable encontrar en algunas distribuciones otras etiquetas para la inclusión de applets, como <APP>. Esto se debe a que estamos ante la tercera revisión de la extensión de HTML para la incrustación de applets y ha sido adoptada como la definitiva. Por ello, cualquier otro medio corresponde a implementaciones obsoletas que han quedado descartadas.

Paso de parametros a Applets

 

El espacio que queda entre las marcas de apertura y cierre de la definición de un applet, se utiliza para el paso de parámetros al applet. Para ello se utiliza la marca PARAM en la página HTML para indicar los parámetros y el método getParameter() de la clase java.applet.Applet para leerlos en el código interno del applet. La construcción puede repetirse cuantas veces se quiera, una tras otra.


Los atributos que acompañan a la marca PARAM son los siguientes:

NAME

Nombre del parámetro que se desea pasar al applet.

VALUE

Valor que se desea transmitir en el parámetro que se ha indicado antes.

Texto HTML

Texto HTML que será interpretado por los navegadores que no entienden la marca APPLET en sustitución del applet mismo.

Para mostar esta posibilidad vamos a modificar nuestro applet básico HolaMundo para que pueda saludar a cualquiera. Lo que haremos será pasarle al applet el nombre de la persona a quien queremos saludar. Generamos el código para ello y lo guardamos en el fichero HolaTal.java

import java.awt.Graphics; import java.applet.Applet; public class HolaTal extends Applet { String nombre; public void init() { nombre = getParameter( "Nombre" ); } public void paint( Graphics g ) { g.drawString( "Hola "+nombre+"!",25,25 ); } }

Si compilamos el ejemplo obtendremos el fichero HolaTal.class que incluiremos en nuestra página Web. Vamos a generar el fichero HolaTal.html, en el que incluiremos nuestro applet, y que debería tener el siguiente contenido:

<HTML> <APPLET CODE=HolaTal.class WIDTH=300 HEIGHT=100> <PARAM NAME="Nombre" VALUE="Agustin"> </APPLET> </HTML>

Por supuesto, que puedes sustituir mi nombre por el tuyo. Este cambio no afectará al código Java, no será necesario recompilarlo para que te salude a ti el applet.

Los parámetros no se limitan a uno solo. Se puede pasar al applet cualquier número de parámetros y siempre hay que indicar un nombre y un valor para cada uno de ellos.

El método getParameter() es fácil de entender. El único argumento que necesita es el nombre del parámetro cuyo valor queremos recuperar. Todos los parámetros se pasan como Strings, en caso de necesitar pasarle al applet un valor entero, se ha de pasar como String, recuperarlo como tal y luego convertirlo al tipo que deseemos. Tanto el argumento de NAME como el de VALUE deben ir colocados entre dobles comillas (") ya que son String.

El hecho de que las marcas <APPLET> y <PARAM> sean ignoradas por los navegadores que no entienden Java, es inteligentemente aprovechado a la hora de definir un contenido alternativo a ser mostrado en este último caso. Así la etiqueta es doble:

<APPLET atributos> parámetros contenido alternativo </APPLET>

Nuestro fichero para mostrar el applet de ejemplo lo modificaremos para que pueda ser visualizado en cualquier navegador y en unos casos presente la información alternativa y en otros, ejcute nuestro applet:

<HTML> <APPLET CODE=HolaTal.class WIDTH=300 HEIGHT=100> <PARAM NAME="Nombre" VALUE="Agustin"> No verás lo bueno hasta que consigas un navegador <I>Java Compatible</I> </APPLET> </HTML>

 

Tokens en parametros de llamada

Ya de forma un poco más avanzada vamos a ver como también se pueden pasar varios parámetros en la llamada utilizando separadores, o lo que es lo mismo, separando mediante delimitadores los parámetros, es decir, tokenizando la cadena que contiene el valor del parámetro, por ejemplo:

<PARAM NAME=Nombre VALUE="Agustin|Antonio">

En este caso el separador es la barra vertical "|", que delimita los dos tokens, pero también podemos redefinirlo y utilizar cualquier otro símbolo como separador:

<PARAM NAME=Separador VALUE="#"> <PARAM NAME=Nombre VALUE="Agustin#Antonio">

Si ahora intentamos cambiar de color de fondo en que aparecen los textos en el applet, utilizando el mismo método, podríamos tener:

<PARAM NAME=Nombre VALUE="Agustin|Antonio"> <PARAM NAME=Color VALUE="green|red">

Es más, podríamos hacer que parpadeasen los mensajes en diferentes colores, cambiando el color de fondo y el del texto:

<PARAM NAME=Nombre1 VALUE="Agustin|green|yellow"> <PARAM NAME=Nombre2 VALUE="Antonio|red|white">

Para recoger los parámetros pasados en este último caso, bastaría con hacer un pequeño bucle de lectura de los parámetros que deseamos:

for( int i=1; ; i++ ) p = getParameter( "Nombre"+i ); if( p
null ) break; . . . }
incluso podríamos utilizar un fichero para pasar parámetros al applet. La llamada sería del mismo tipo:

<PARAM NAME=Fichero VALUE="FicheroDatos">

y el FicheroDatos debería tener un contenido, en este caso, que sería el siguiente:

Agustin fondoColor=green textoColor=yellow fuente=Courier fuenteTam=14 Antonio fondoColor=red textocolor=white

E incluso ese FicheroDatos, podríamos hacer que se encontrase en cualquier URL, de forma que utilizando el método getContent() podríamos recuperar el contenido del fichero que contiene los parámetros de funcionamiento del applet:

String getContent( String url ) { URL url = new URL( null,url ); return( (String).url.getContent() ); }

Para recuperar los parámetros que están incluidos en la cadena que contiene el valor podemos utilizar dos métodos:

StringTokenizer( string,delimitadores ) treamTokenizer( streamentrada )

Así en la cadena Agustin|Antonio si utilizamos el método:

StringTokenizer( cadena,"|" );

obtenemos el token Agustin, el delimitador "|" y el token Antonio. El código del método sería el que se muestra a continuación:

Capturamos el parámetro p = getParameter( "p" ); Creamos el objeto StringTokenizer st = new StringTokenizer( p,"|" ); Creamos el almacenamiento cads = new String[ st.countTokens() ]; Separamos los tokens de la cadena del parámetro for( i=0; i < cads.length; i++ ) cadenas[i] = st.nextToken();

En el caso de que utilicemos un fichero como verdadera entrada de parámetros al applet y el fichero se encuentre en una dirección URL, utilizamos el método StreamTokenizer() para obtener los tokens que contiene ese fichero:

Creamos el objeto URL para acceder a él url = new URL( "http://www.prueba.es/Fichero" ); Creamos el canal de entrada ce = url.openStream(); Creamos el objeto StreamTokenizer st = new StreamTokenizer( ce ); Capturamos los tokens st.nextToken();

El parametro ARCHIVE


 

Una de las cosas que se achacan a Java es la rapidez. El factor principal en la percepción que tiene el usuario de la velocidad y valor de los applets es el tiempo que tardan en cargarse todas las clases que componen el applet. Algunas veces tenemos que estar esperando más de un minuto para ver una triste animación, ni siquiera buena. Y, desafortunadamente, esta percepción de utilidad negativa puede recaer también sobre applets que realmente sí son útiles.

Para entender el porqué de la necesidad de un nuevo método de carga para acelerarla, necesitamos comprender porqué el método actual es lento. Normalmente un applet se compone de varias clases, es decir, varios ficheros .class. Por cada uno de estos ficheros .class, el cargador de clases debe abrir una conexión individual entre el navegador y el servidor donde reside el applet. Así, si un applet se compone de 20 ficheros .class, el navegador necesitará abrir 20 sockets para transmitir cada uno de los ficheros. La sobrecarga que representa cada una de estas conexiones es relativamente significante. Por ejemplo, cada conexión necesita un número de paquetes adicionales que incrementan el tráfico en la Red.

Me imagino que ya el lector habrá pensado la solución al problema: poner todos los ficheros en uno solo, con lo cual solamente sería necesaria una conexión para descargar todo el código del applet. Bien pensado. Esto es lo mismo que han pensado los dos grandes competidores en el terreno de los navegadores, Netscape y Microsoft.

Desafortunadamente, las soluciones que han implementado ambas compañías no son directamente compatibles. Microsoft, en su afán de marcar diferencia, crea su propio formato de ficheros CAB. La solución de Netscape es utilizar el archiconocido formato ZIP. Por suerte, nosotros podemos escribir nuestro código HTML de forma que maneje ambos formatos, en caso necesario. Esto es así porque podemos especificar cada uno de estos formatos de ficheros especiales en extensiones separadas de la marca <APPLET>.

No vamos a contar la creación de ficheros CAB; quien esté interesado puede consultar la documentación de Java que proporciona Microsoft con su SDK para Java, que es bastante exhaustiva al respecto. Una vez que disponemos de este fichero, podemos añadir un parámetro CABBASE a la marca <APPLET>:

<APPLET NAME="Hola" CODE="HolaMundo" WIDTH=50 HEIGHT=50 > <PARAM NAME=CODEBASE VALUE="http://www.ejemplo.es/classes">; <PARAM NAME=CABBASE VALUE="hola.cab"> </APPLET>

El VALUE del parámetro CABBASE es el nombre del fichero CAB que contiene los ficheros .class que componen el conjunto de applet.

Crear un archivo ZIP para utilizarlo con Netscape es muy fácil. Se deben agrupar todos los ficheros .class necesarios en un solo fichero .zip. Lo único a tener en cuenta es que solamente hay que almacenar los ficheros .class en el archivo; es decir, no hay que comprimir.

Si se está utilizando pkzip, se haría:

Pkzip -e0 archivo.zip listaFicherosClass

El parámetro de la línea de comandos es el número cero, no la "O" mayúscula.

Para utilizar un fichero .zip hay que indicarlo en la marca ARCHIVE de la sección <APPLET>:

<APPLET NAME="Hola" CODE="HolaMundo" WIDTH=50 HEIGHT=50 CODEBASE VALUE="http://www.ejemplo.es/classes" ARCHIVE="hola.zip"> </APPLET>

Pero hay más. Podemos crear ambos tipos de ficheros y hacer que tanto los usuarios de Netscape Navigator como los de Microsoft Internet Explorer puedan realizar descargas rápidas del código del applet. No hay que tener en cuenta los usuarios de otros navegadores, o de versiones antiguas de estos dos navegadores, porque ellos todavía podrán seguir cargando los ficheros a través del método lento habitual. Para compatibilizarlo todo, ponemos las piezas anteriores juntas:

<APPLET NAME="Hola" CODE="HolaMundo" WIDTH=50 HEIGHT=50 CODEBASE VALUE="http://www.ejemplo.es/classes" ARCHIVE="hola.zip"> <PARAM NAME=CABBASE VALUE="hola.cab"> <PARAM NAME=CODEBASE VALUE="http://www.ejemplo.es/classes">; <PARAM NAME=CABBASE VALUE="hola.cab"> </APPLET>

Ahora que se puede hacer esto con ficheros .cab y .zip, JavaSoft ha definido un nuevo formato de ficheros, que incorporará en del JDK 1.1, para incluir juntos todos los ficheros de imágenes, sonido y class. JavaSoft llama a esto formato JAR (Java Archive). La marca <APPLET> de HTML se modificará para manejar este nuevo formato JAR a través del parámetro ARCHIVES. Y dejamos al lector el trabajo de poner los tres formatos juntos bajo el mismo paraguas de la marca <APPLET>.

 

Depuración general


 

Compilar y ejecutar el programa HolaMundo.java a través del fichero HolaMundo.html no debería suponer ningún problema, pero alguna vez nos encontraremos frente a programas más difíciles y se necesitará el truco de depuración al que se recurre en el desarrollo de programas en cualquier lenguaje.

System.out.println


Una de las herramientas de depuración más efectivas en cualquier lenguaje de programación es simplemente la salida de información por pantalla. El comando System.out.println imprime la cadena que se le especifique en la ventana de texto en la que se invocó al navegador. La forma de usarlo se muestra a continuación:

public void paint( Graphics g ) { g.drawString( "Hola Mundo!",25,25 ); System.out.println( "Estamos en paint()" ); }

 

Ciclo de vida de un Applet

 

Para seguir el ciclo de vida de un applet, supondremos que estamos ejecutando en nuestro navegador el applet básico HolaMundo, a través de la página HTML que lo carga y corre.

Lo primero que aparece son los mensajes "initializing... starting...", como resultado de la carga del applet en el navegador. Una vez cargado, lo que sucede es:



Se crea una instancia de la clase que controla al applet
El applet se inicializa a si mismo
Comienza la ejecución del applet

Cuando se abandona la página, por ejemplo, para ir a la siguiente, el applet detiene la ejecución. Cuando se regresa a la página que contiene el applet, se reanuda la ejecución.

Si se utiliza la opción del navegador de Reload, es decir, volver a cargar la página, el applet es descargado y vuelto a cargar. El applet libera todos los recursos que hubiese acaparado, detiene su ejecución y ejecuta su finalizador para realizar un proceso de limpieza final de sus trazas. Después de esto, el applet se descarga de la memoria y vuelve a cargarse volviendo a comenzar su inicialización.

Finalmente, cuando se concluye la ejecución del navegador, o de la aplicación que está visualizando el applet, se detiene la ejecución del applet y se libera toda la memoria y recursos ocupados por el applet antes de salir del navegador.

 

Protección de Applets

Como curiosidad, más que como algo verdaderamente útil, podemos proteger nuestros applets de forma muy sencilla, o por lo menos evitar que nadie pueda ocultar en sus páginas HTML que nosotros somos los legales autores de un applet.

El método es muy sencillo y se basa en la utilización de un parámetro del cual comprobamos su existencia, por ejemplo:

<PARAM NAME=copyright VALUE="Applet de Prueba, A.Froufe (C)1996, Todos los derechos reservados">

y en el código Java de nuestro applet, comprobaríamos que efectivamente el parámetro copyright existe y ese es su contenido:

if( !getParameter( "copyright").equals( "..." ) throw( new Exception( "Violacion del Copyright" ) );

donde "..." es el texto completo del valor del parámetro. Pero también podemos hacerlo de forma más elegante:

copyright = getParameter( "copyright" ); System.out.println( copyright.hashCode() ); if( copyright != -913936799 ) throw( new Exception( "Violacion del Copyright" ) );

en donde la sentencia comentada nos proporciona el valor del copyright para poder introducirlo en la comparación de la presencia o no del parámetro en la llamada al applet. Habría que declarar y definir correctamente tipos y variables, pero la idea básica es la que expuesta.


 

Escribir Applets Java 

Para escribir applets Java, hay que utilizar una serie de métodos, algunos de los cuales ya se hay sumariado al hablar de los métodos del appletviewer, que es el visualizador de applets de Sun. Incluso para el applet más sencillo necesitaremos varios métodos. Son los que se usan para arrancar (start) y detener (stop) la ejecución del applet, para pintar (paint) y actualizar (update) la pantalla y para capturar la información que se pasa al applet desde el fichero HTML a través de la marca APPLET.



init()


Esta función miembro es llamada al crearse el applet. Es llamada sólo una vez. La clase Applet no hace nada en init(). Las clases derivadas deben sobrecargar este método para cambiar el tamaño durante su inicialización, y cualquier otra inicialización de los datos que solamente deba realizarse una vez. Deberían realizarse al menos las siguientes acciones:

  • Carga de imágenes y sonido
  • El resize del applet para que tenga su tamaño correcto
  • Asignación de valores a las variables globales

Por ejemplo:

public void init() { if( width < 200 || height < 200 ) resize( 200,200 ); valor_global1 = 0; valor_global2 = 100; cargaremos imágenes en memoria sin mostrarlas cargaremos música de fondo en memoria sin reproducirla }



destroy()


Esta función miembro es llamada cuando el applet no se va a usar más. La clase Applet no hace nada en este método. Las clases derivadas deberían sobrecargarlo para hacer una limpieza final. Los applet multithread deberán usar destroy() para "matar" cuanquier thread del applet que quedase activo.



start()


Llamada para activar el applet. Esta función miembro es llamada cuando se visita el applet. La clase Applet no hace nada en este método. Las clases derivadas deberían sobrecargarlo para comenzar una animación, sonido, etc.

public void start() { estaDetenido = false; comenzar la reproducción de la música musicClip.play(); }

También se puede utilizar
start() para eliminar cualquier thread que se necesite.




Llamada para detener el applet. Se llama cuando el applet desaparece de la pantalla. La clase Applet no hace nada en este método. Las clases derivadas deberían sobrecargarlo para detener la animación, el sonido, etc.

public void stop() { estaDetenido = true; if( /* ¿se está reproduciendo música? */ ) musicClip.stop(); }




El método stop() resize( int width,int height ) init() debería llamar a esta función miembro para establecer el tamaño del applet. Puede utilizar las variables ancho y alto, pero no es necesario. Cambiar el tamaño en otro sitio que no sea init() produce un reformateo de todo el documento y no se recomienda.

En el navegador Netscape, el tamaño del applet es el que se indica en la marca APPLET del HTML, no hace caso a lo que se indique desde el código Java del applet.




Variable entera, su valor es el ancho definido en el parámetro WIDTH de la marca HTML del APPLET. Por defecto es el ancho del icono.




Variable entera, su valor es la altura definida en el parámetro HEIGHT de la marca HTML del APPLET. Por defecto es la altura del icono. Tanto width como height están siempre disponibles para que se puede chequear el tamaño del applet.

Podemos retomar el ejemplo dewidth height init():

public void init() { if( width < 200 || height < 200 ) resize( 200,200 ); ...




Se llama cada vez que se necesita refrescar el área de dibujo del applet. La clase Applet simplemente dibuja una caja con sombreado de tres dimensiones en el área. Obviamente, la clase derivada debería sobrecargar este método para representar algo inteligente en la pantalla.

Para repintar toda la pantalla cuando llega un evento paint( Graphics g ) Paint, se pide el rectángulo sobre el que se va a aplicar paint() y si es más pequeño que el tamaño real del applet se invoca a repaint(), que como va a hacer un update(), se actualizará toda la pantalla.

Podemos utilizar
paint() para imprimir nuestro mensaje de bienvenida:

void public paint( Graphics g ) { g.drawString( "Hola Java!",25,25 );
Dibujaremos la imágenes que necesitemos }



update( Graphics g )


Esta es la función que se llama realmente cuando se necesita actualizar la pantalla. La clase Applet simplemente limpia el área y llama al método paint(). Esta funcionalidad es suficiente en la mayoría de los casos. De cualquier forma, las clases derivadas pueden sustituir esta funcionalidad para sus propósitos.

Podemos, por ejemplo, utilizar update() para modificar selectivamente partes del área gráfica sin tener que pintar el área completa:

public void update( Graphics g ) { if( estaActualizado ) { g.clear(); garantiza la pantalla limpia repaint(); podemos usar el método padre: super.update() } else Información adicional g.drawString( "Otra información",25,50 ); }




A esta función se la debería llamar cuando el applet necesite ser repintado. No debería sobrecargarse, sino dejar que Java repinte completamente el contenido del applet.

Al llamar a repaint() repaint(), sin parámetros, internamente se llama a update() que borrará el rectángulo sobre el que se redibujará y luego se llama a paint(). Como a repaint() se le pueden pasar parámetros, se puede modificar el rectángulo a repintar.




Este método carga los valores parados al applet vía la marca APPLET de HTML. El argumento getParameter( String attr ) String es el nombre del parámetro que se quiere obtener. Devuelve el valor que se le haya asignado al parámetro; en caso de que no se le haya asignado ninguno, devolverá null.

Para usar
getParameter(), se define una cadena genérica. Una vez que se ha capturado el parámetro, se utilizan métodos de cadena o de números para convertir el valor obtenido al tipo adecuado.

public void init() { String pv; pv = getParameter( "velocidad" ); if( pv
null ) velocidad = 10; else velocidad = Integer.parseInt( pv ); }



Indica la ruta getDocumentBase() http, o el directorio del disco, de donde se ha recogido la página HTML que contiene el applet, es decir, el lugar donde está la hoja en todo Internet o en el disco.




Indica la ruta getCodeBase() http, o el directorio del disco, de donde se ha cargado el código bytecode que forma el applet, es decir, el lugar donde está el fichero .class en todo Internet o en el disco.




Para imprimir en impresora, al igual que print( Graphics g ) paint() se puede utilizar print(), que pintará en la impresora el mapa de bits del dibujo.

 

La aplicación Fecha (Aproximación a OOP) 

Veamos ahora una aplicación un poco más útil que HolaMundo, presentaremos en pantalla la fecha y hora del sistema. Aprovecharemos también para realizar un introducción muy sencilla a los conceptos fundamentales de la programación orientada a objetos, clases y objetos, a través de esta simple aplicación.

import java.util.Date; class FechaApp { public static void main( String args[] ) { Date hoy = new Date(); System.out.println( hoy ); } }

Esta aplicación es una versión modificada de HolaMundoApp de la que difiere porque se importa la clase Date, la aplicación se llama ahora FechaApp en vez de HolaMundoApp, se crea un objeto Date y el mensaje de salida a pantalla es diferente. Almacenaremos esta nueva aplicación en el fichero FechaApp.java.

La línea de código:

class FechaApp {

es el inicio del bloque de la declaración de nuestra clase. Ya hemos dicho que todas las funciones y variables en Java, existen dentro de una clase o un objeto, Java no soporta funciones o variables globales. Por tanto, la declaración de la clase se convierte en el esqueleto de cualquier aplicación Java. La clase, el bloque básico de un lenguaje orientado a objetos como Java, es la plantilla que usamos para describir los datos y el entorno asociado a las instancias de esa clase. Cuando se instancia una clase, se crea un objeto del tipo definido por la clase y exactamente igual que cualquier otra instancia realizada de la misma clase. Los datos asociados a la clase u objeto son las variables y el entorno asociado con la clase u objeto son los métodos.

Un ejemplo de clase es la clase que representa un rectángulo. Esta clase contiene las variables que indican las coordenadas del origen del rectángulo y su ancho y alto. La clase puede contener un método para calcular el área de ese rectángulo. Ahora podemos instanciarlo para muy diferentes propósitos, es decir, podemos tener objetos rectángulo específicos, así podremos obtener información de las dimensiones de nuestro dormitorio o de las dimensiones de la ventana en donde se está visualizando esta página.

class NombreDeLaClase { . . . }

Esta es la forma general de definición de una clase en Java, donde la palabra clave class inicia la definición de la clase NombreDeLaClase. Las variables y métodos de la clase han de ir colocados entre las llaves que delimitan el bloque de definición de la clase. FechaApp no tiene variables y solamente tiene un método llamado main().

Este método, main(), es el cerebro de cualquier aplicación Java. Cuando se ejecuta una aplicación Java utilizando el intérprete Java, se debe especificar el nombre de la clase que se desea ejecutar. El intérprete entonces, invoca al método main() definido dentro de esa clase, que debe controlar el flujo del programa, pedir al sistema los recursos que necesite y ejecutar cualquier otro método necesario para completar la funcionalidad de la aplicación.

La definición del método main() debe estar precedida por tres modificadores:

  • public indica que el método main() puede ser llamado por cualquier objeto
  • static indica que el método main() es un método estático, es decir, un método propio de la clase
  • void indica que el método main() no devolverá ningún valor

El método main() en Java es similar a la función main() de C y C++. Cuando se ejecuta un programa escrito en C o C++, arranca llamando en primer lugar a la función main(), que llamará a las demás funciones necesarias en la ejecución del programa. De forma similar, en el lenguaje Java, cuando se ejecuta una clase con el intérprete Java, el sistema comienza llamando al método main() de la clase, que llamará a los demás métodos necesarios para completar la ejecución de la aplicación. Si se intenta ejecutar una clase con el intérprete Java que no contenga el método main(), el intérprete generará un mensaje de error.

El método main() acepta como argumento un array de Strings:

public static void main( Strings args[] ) {

Este array de Strings es el mecanismo a través del cual el sistema puede pasar información a la aplicación. Cada una de las cadenas String es un argumento de línea de comandos. Permiten pasar información a la aplicación, para variar su ejecución, sin necesidad de recompilarla. Por ejemplo, si desarrollamos una aplicación de ordenación, podríamos permitir al usuario seleccionar el método, ascendente o descendente, en la línea de comandos de ejecución de la aplicación.

-descendente

Nuestra aplicación FechaApp ignora los argumentos de la línea de comandos, así que no nos extendemos más, pero volveremos sobre ello más adelante. No obstante, los programadores de C y C++ deben tener en cuenta que en Java el número y tipo de argumentos de la línea de comandos es diferente a los que se pasan a la función main() en C y C++.

La aplicación FechaApp es el programa más simple que podemos hacer que realice algo interesante, pero por su misma sencillez no necesita ninguna clase adicional. Sin embargo, la mayoría de los programas que escribamos serán más complejos y necesitarán que escribamos otras clases y utilizar las que nos proporciona Java como soporte.

Nuestra aplicación FechaApp utiliza dos clases, la clase System y la clase Date, que nos proporciona el entorno de desarrollo de Java. La clase System proporciona un acceso al sistema independiente del hardware sobre el que estemos ejecutando la aplicación y la clase Date proporciona un acceso a las funciones de Fecha independientemente del sistema en que estemos ejecutando la aplicación.

 

EL DEPURADOR DE JAVA - JD

El depurador de Java, jdb es un depurador de línea de comandos, similar al que Sun proporciona en sus Sistemas, dbx. Es complicado de utilizar y un tanto críptico, por lo que, en principio, tiene escasa practicidad y es necesaria una verdadera emergencia para tener que recurrir a él.

Trataremos por encima los comandos que proporciona el jdb, pero sin entrar en detalles de su funcionamiento, porque no merece la pena. Casi es mejor esperar a disponer de herramientas visuales para poder depurar con cierta comodidad nuestro código Java.

Para poder utilizar el depurador, las aplicaciones Java deben estar compiladas con la opción de depuración activada, -g. Posteriormente se puede lanzar appletviewer con la opción de depuración, debug, y habremos puesto en marcha jdb.




Depurar HolaMundo


Hemos modificado nuestro applet de ejemplo para utilizarlo en nuestra sesión de ejemplo con el depurador. Se compilaría con el comando:

%javac -g hm.java

y el contenido de nuestro applet HolaMundo modificado y guardado en el fichero hm.java sería el siguiente:

Applet HolaMundo de ejemplo, para depurar import java.awt.Graphics; import java.applet.Applet; public class hm extends Applet { int i; public void paint( Graphics g ) { i = 10; g.drawString( "Hola Mundo!",25,25 ); } }

Una vez compilado, iniciamos la sesión lanzando el visor de applets de Sun con la opción de depuración, utilizando el comando:

%appletviewer -debug hm.html

El fichero hm.html contiene las líneas mínimas para poder activar el applet, estas líneas son las que reproducimos:

<html> <applet code=hm.class width=100 height=100> </applet> </html>

Se inicia pues la sesión con el depurador y vamos a ir reproduciendo lo que aparece en la pantalla a medida que vamos introduciendo comandos:

%appletviewer -debug hm.html Loading jdb... 0xee301bf0:class(sun.applet.AppletViewer) >





El comando Comando help help proporciona una lista de los comandos que están disponibles en la sesión de jdb. Esta lista es la que sigue, en donde hemos aprovechado la presencia de todos los comandos para comentar la acción que cada uno de ellos lleva a cabo.

>help
command list
threads [threadgroup] -- lista threads
thread <thread id> -- establece el thread por defecto
suspend [thread id(s)] -- suspende threads (por defecto, todos)
resume [thread id(s)] -- continúa threads (por defecto, todos)
where [thread id]|all -- muestra la pila de un thread
threadgroups -- lista los grupos de threads
threadgroup <name> -- establece el grupo de thread actual

print <id> [id(s)] -- imprime un objeto o campo
dump <id> [id(s)] -- imprime toda la información del objeto

locals -- imprime las variables locales de la pila actual
classes -- lista las clases conocidas
methods <class id> -- lista los métodos de una clase

stop in <class id>.<method> -- fija un punto de ruptura en un método
stop at <class id>:<line> -- establece un punto de ruptura en una línea
up [n frames] -- ascender en la pila de threads
down [n frames] -- descender en la pila de threads
clear <class id>:<line> -- eliminar un punto de ruptura
step -- ejecutar la línea actual
cont -- continuar la ejecución desde el punto de ruptura

catch <class id> -- parar por la excepción especificada
ignore <class id> -- ignorar la excepción especificada

list [line number] -- imprimir código fuente
use [source file path] -- ver o cambiar la ruta del fichero fuente
memory -- informe del uso de la memoria
load <classname> - carga la clase Java a ser depurada
run <args> - comienza la ejecución de la clase cargada
!! - repite el último comando
help (or ?) - lista los comandos
exit (or quit) - salir del depurador
>




El comando Comando threadgroups threadgroups permite ver la lista de threads que se están ejecutando. Los grupos system y main deberían estar siempre corriendo.

>threadgroups 1.(java.lang.ThreadGroup)0xee300068 system 2.(java.lang.ThreadGroup)0xee300a98 main >





El comando Comando threads threads se utiliza para ver la lista completa de los threads que se están ejecutando actualmente.

>threads Group system: 1.(java.lang.Thread)0xee300098 clock handler cond 2.(java.lang.Thread)0xee300558 Idle thread run 3.(java.lang.Thread)0xee3005d0 sync Garbage Collector cond 4.(java.lang.Thread)0xee300620 Finalizer thread cond 5.(java.lang.Thread)0xee300a20 Debugger agent run 6.(java.tools.debug.BreakpointHandler)0xee300b58) Breakpoint handler cond Group main: 7.(java.lang.Thread)0xee300048 main suspended >





El comando Comando run run es el que se utiliza para arrancar el appletviewer en la sesión de depuración. Lo teclearemos y luego volveremos a listar los threads que hay en ejecución.

>run run sun.applet.AppletViewer hm.html running... main[1]threads threads Group sun.applet.AppletViewer.main: 1.(java.lang.Thread)0xee3000c0 AWT-Motif running 2.(sun.awt.ScreenUpdater)0xee302ed0 ScreenUpdater cond. Waiting Group applet-hm.class: 3.(java.lang.Thread)0xee302f38 Thread-6 cond. Waiting main[1]

El visor de applets de Sun aparecerá en la pantalla y mostrará el conocido mensaje de saludo al Mundo. Ahora vamos a rearrancar el appletviewer con un punto de ruptura, para detener la ejecución del applet, y podamos seguir mostrando los comandos disponibles en el
jdb.

main[1]exit %appletviewer -debug hm.html Loading jdb... 0xee3009c8:class(sun.applet.AppletViewer) >stop in hm.paint Breakpoint set in hm.paint >run run sun.applet.AppletViewer hm.html running... Breakpoint hit: hm.paint(hm.java:9) AWT-Motif[1]





El comando Comando where where mostrará la pila de ejecución del applet.

AWT-Motif[1]where [1]hm.paint(hm.java:9) [2]sun.awt.motif.MComponentPeer.paint(MComponenetPeer.java:109) [3]sun.awt.motif.MComponentPeer.handleExpose(MComponenetPeer.java:170) AWT-Motif[1]





El comando Comando use use nos informa del camino donde jdb va a buscar los ficheros fuentes que contienen el código Java de las clases que se están depurando. Por defecto, utilizará el camino que se especifique en la variable de entorno CLASSPATH.

AWT-Motif[1]use /usr/local/java/classes: AWT-Motif[1]





El comando Comando list list mostrará el código fuente actual al comienzo del punto de ruptura que hayamos fijado.

AWT-Motif[1]list 9 public void paint( Graphics g ) { 10 => i = 10; 11 g.drawString( "Hola Mundo!",25,25 ) ; 12 } 13 } AWT-Motif[1]





El comando Comando dump dump nos permitirá ahora ver el valor del objeto g pasado desde el appletviewer.

AWT-Motif[1]dump g g = (sun.awt.motif.X11Graphics)0xee303df8 { int pData = 1342480 Color foreground = (java.awt.Color)0xee302378 Font font = (java.awt.Font)0xee302138 int originX = 0 int originY = 0 float scaleX = 1 float scaleY = 1 Image image = null } AWT-Motif[1]





El comando Comando step step nos porporciona el método para ejecutar la línea actual, que estará siendo apuntada por el indicador si hemos utilizado el comando list.

AWT-Motif[1]step Breakpoint hit: hm.paint(hm.java:11) AWT-Motif[1]list 9 public void paint( Graphics g ) { 10 i = 10; 11 => g.drawString( "Hola Mundo!",25,25 ); 12 } 13 } AWT-Motif[1]

B

CLASES JAVA
 

En cualquier lenguaje orientado a objetos, las clases definen cualquier objeto que se pueda manipular. Java tiene muchas clases útiles, no solamente aquellas que se utilizan para gráficos y sonido, usadas en la construcción de applets mucho más complejos.

 

Continuara...