Curso de Java. por Michael Moossen

Tamaño: px
Comenzar la demostración a partir de la página:

Download "Curso de Java. por Michael Moossen"

Transcripción

1 Curso de Java por Michael Moossen Versión , 16 de marzo de 2003

2 Este documento es de dominio público. Se puede imprimir y distribuir libre de gastos en su forma original, incluyendo la lista de los autores. Si se altera o se utilizan partes de éste dentro de otro documento, la lista de autores debe incluir todos los autores originales y el autor o autores que hayan realizado los cambios. En caso de que este documento sea utlizado con fines comerciales, se aplicarán los términos de la GNU General Public Licence. Copyright c by Michael Moossen Manual del Departamento de Informática de la Universidad Técnica Federico Santa María Valparaíso, Chile II Departamento de Informática. U.T.F.S.M. Michael Moossen

3 Índice general Índice General III Prefacio IX Convenciones de Escritura XI 1. Introducción a Java Reseña Histórica Características Portabilidad Lenguaje Interpretado Orientación a Objetos Integración en la Red Multitarea Robustez Seguridad Instalación Distribuciones Ayuda en Línea Primer Programa Java Compilación Ejecución Principios de Programación con Java La Sintaxis del Lenguaje Palabras Clave Identificadores Literales Separadores Comentarios Variables Declaración Ámbito Control de Acceso Arreglos Tipos de Dato Básicos Conversión de Tipos Objetos Operadores Operadores Unarios III

4 ÍNDICE GENERAL Operadores Aritméticos Operadores de Rotación Operadores de Comparación Operadores Lógicos Operador Ternario Operadores de Asignación Precedencia de Operadores Operaciones con Objetos Control de Flujo La Sentencia if La Sentencia switch Ciclos La Sentencia while La Sentencia for Control de Flujo en Ciclos Manejo de Excepciones try...catch...finally La Clase Exception Capturar Excepciones Lanzar Excepciones Declaración de Excepciones en Métodos Recursividad Números de Fibonacci La Clase Object Programación Orientada a Objetos Taxonomía de Lenguajes Orientados a Objetos Ventajas de la Tecnología Orientada a Objetos Desventajas de la Tecnología Orientada a Objetos Características de la Tecnología Orientada a Objetos Abstracción Modularidad Encapsulamiento Jerarquía Polimorfismo Características Adicionales de los Lenguajes Orientados a Objetos Clases Atributos Métodos Constructores Destructores Objetos Operador new Objeto this Objeto super Modificadores de Atributos y Métodos El Modificador static El Modificador final El Modificador abstract El Modificador transient El Modificador volatile Clases Internas IV Departamento de Informática. U.T.F.S.M. Michael Moossen

5 ÍNDICE GENERAL Clases Internas Estáticas Clases Internas Miembro Clases Internas Locales Clases Anónimas Interfaces Declaración de una Interfaz Uso de una Interfaz Paquetes Uso de Paquetes Nombre de Paquetes y Clases Uso de Otros Paquetes Estructura Jerárquica de Directorios Ejecución de las Clases Programación con la API Estándar Manejo de Strings La Clase String La Clase StringBuffer La Clase StringTokenizer Utilidades Iteradores Vectores La Clase Hashtable La Clase System Funciones Matemáticas La Clase Abstracta Number La Clase Math El paquete java.math Manejo de Fechas La Clase Date La Clase GregorianCalendar La Clase SimpleDateFormat La Clase SimpleTimeZone Entrada y Salida de Datos Archivos y Directorios Lectura y Escritura en Archivos de Texto Lectura y Escritura en Archivos Binarios Archivos de Acceso Aleatorio Serialización de Objetos Conectividad a Base de Datos (JDBC) Introducción a los Drivers JDBC Registrar un Driver JDBC: La Clase DriverManager La Conexión: La Clase Connection y URLs de Conexión Ejecutar una Instrucción SQL: La Clase Statement Obtención de Resultados: La Interfaz ResultSet Manejo de Transacciones Hilos y Sincronización Creación de Hilos Ciclo de Vida de un Thread Sincronización Prioridades Grupos de Thread Michael Moossen Departamento de Informática. U.T.F.S.M. V

6 ÍNDICE GENERAL 5. Java e Internet Trabajo en Red Conceptos Básicos URL Sockets Datagramas HTML Primera Página Formateo Básico Caracteres Especiales Enlaces Listas Imágenes Formateo Fino Estructura del Documento Formularios Tablas Marcos Java Server Pages Plantilla de Texto Elementos de Script Directivas Variables Predefinidas Accciones Ejemplo Applets El Tag APPLET de HTML Ciclo de Vida de un Applet Métodos para Dibujar un Applet Comunicación entre Applet y Browser Sonidos en Applets Imágenes en Applets Threads en Applets Applets como Aplicaciones Restricciones de Seguridad Seguridad Controlador de Seguridad Un Controlador de Seguridad Propio Instalar un Controlador de Seguridad Decidir qué Métodos Sobreescribir del SecurityManager Interfaces Gráficas Contexto Histórico Introducción a Swing Contenedores Componentes Manejo de Eventos Diseño de Interfaces Administración de Disposición Posicionamiento Absoluto Relleno Bordes VI Departamento de Informática. U.T.F.S.M. Michael Moossen

7 ÍNDICE GENERAL Look & Feel Contenedores Ventanas de Diálogo Contenedores Generales Componentes Etiquetas Botones Listas Desplegables Cajas de Texto Campos de Password Áreas de Texto Listas Tablas Lo que faltó Java 2 Enterprise Edition Introducción Enterprise Java Bean Contenedor EJB Arquitectura Distribución de Carga Descriptores de Instalación Enterprise Java Beans Session Beans Entity Beans Message Driven Beans Transacciones Manejo Programado Manejo Declarado A. Utilitarios de Java 185 A.1. Generador de Documentación A.2. Java ARchive A.3. Applet Viewer A.4. Java Debugger (jdb) A.5. Java IDE s B. Propiedades del Sistema 189 Bibliografía 191 Índice alfabético 193 Michael Moossen Departamento de Informática. U.T.F.S.M. VII

8

9 Prefacio Este manual tiene la intención de ser una herramienta de apoyo al aprendizaje del lenguaje de programación Java, para personas con conocimientos previos mínimos de programación, entregando por una parte una sobrevista general del lenguaje mismo y sus principales áreas y aplicaciones, como también todos los conceptos para que el alumno pueda desarrollarse más allá de este manual por sí mismo. Este curso se divide en seis capítulos: El capítulo 1 introduce el lenguaje de programación Java exponiendo sus virtudes y desventajas. Además, se ven aspectos prácticos de los pasos de configuración de una plataforma de programación totalmente operable y el primer ejemplo de código. El capítulo 2 incide en los detalles básicos sobre la programación en Java. Dando a conocer las herramientas básicas con las que cuenta el programador y mostrando de a poco los conceptos de orientación a objetos básicos. El capítulo 3 entra de lleno en materia de programación orientada a objetos, en sus orígenes y su evolución, y de todos los aspectos prácticos presentes en Java. El capítulo 4 presenta algunos temas de programación más específicos, como son algunas librerías estándares de Java, conectividad a bases de datos vía JDBC, manipulación avanzada de Strings y manejo de archivos. El capítulo 5 entra en el mundo web pasando de HTML básico, hacía páginas JSP, servlets y applets. Además se ven temas de seguridad y manejo de Sockets. El capítulo 6 introduce la programación de interfaces de usuario gráficas, vía AWT y Swing(JFC). El apéndice A introduce algunas herramientas incluidas en la distribución de Java y que pueden ser de gran utilidad para el programador. El apéndice B presenta las propiedades del sistema que son accesibles por defecto. Es importante leer los capítulos en secuencia. Se recomienda, leer cuidadosamente los ejemplos, ya que en ellos encontrará gran parte de la información, entregada en este manual. Si tiene ideas sobre algo que debería ser añadido o alterado en este documento, por favor hágalo saber. Michael Moossen Estudiante de Magister, Departamento de Informática, Universidad Técnica Federico Santa María La versión vigente de este documento estará disponible en: mmoossen IX

10 X Departamento de Informática. U.T.F.S.M. Michael Moossen

11 Convenciones de Escritura Durante este curso se usarán las siguientes convenciones de escritura para el código Java: Código Fuente El código fuente, en general, será escrito con letra monoespaciada, como en el siguiente ejemplo: System.out.println("Ejemplo:"); for(int i=0; i<10; i++) { //itera 10 veces System.out.println(i); Palabras Reservadas Además, las palabras reservadas de Java, se presentarán en color azul: System.out.println("Ejemplo:"); for (int i=0; i<10; i++) { //itera 10 veces System.out.println(i); Comentarios Los comentarios, serán verdes: System.out.println("Ejemplo:"); for (int i=0; i<10; i++) { //itera 10 veces System.out.println(i); Literales Los literales, tanto númericos como alfabéticos, serán rosados: System.out.println("Ejemplo:"); for (int i=0; i<10; i++) { //itera 10 veces System.out.println(i); Clases e Interfaces Tanto, las clases como las interfaces, serán representadas con color rojo: System.out.println("Ejemplo:"); for (int i=0; i<10; i++) { //itera 10 veces System.out.println(i); Métodos y Atributos Finalmente, los métodos y atributos de una clase, se muestran en color turquesa: System.out.println("Ejemplo:"); for (int i=0; i<10; i++) { //itera 10 veces System.out.println(i); Números de Línea En algunos ejemplos, se incluyen los números de línea. Esta numeración es de color gris: XI

12 01 System.out.println("Ejemplo:"); 02 for (int i=0; i<10; i++) { //itera 10 veces 03 System.out.println(i); 04 Para el código HTML, se usarán convenciones de escritura similares: Tags serán escritos con color azul. Atributos serán escritos con color rojo. Literales, Comentarios y Números de Línea mantienen las misma convenciones que en código Java. Como se muestra en el siguiente ejemplo: 01 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > 02 <HTML> 03 <HEAD> 04 <TITLE> The Robot </TITLE> 05 <META NAME="Author" CONTENT="Michael Moossen" > 06 </HEAD> <!-- Aquí comienza el contenido de la página --> 09 <BODY> 10 <APPLET CODE="Robot.class" WIDTH=600 HEIGHT=400> 11 </BODY> 12 </HTML> XII Departamento de Informática. U.T.F.S.M. Michael Moossen

13 Capítulo 1 Introducción a Java 1.1. Reseña Histórica El desarrollo del lenguaje Java se inició en 1991, contemporáneamente a la creación en Suiza de la World Wide Web. En esa época, un equipo de Sun MicroSystems, en el que participaba James Gosling, trabajaba en el proyecto de crear productos electrónicos de consumo que fueran sencillos y fiables. Para ello, se necesitaba una forma de crear código, que fuera independiente de la plataforma y que permitiera ejecutar el software en cualquier CPU. Como punto de partida, para un lenguaje informático que implementara esta independencia de la plataforma, el equipo de Sun se fijó en un primer momento en C++. Pronto abandonaron la idea y no consiguieron encontrar ningún lenguaje que se adaptara a sus necesidades. Decidieron entonces desarrollar un lenguaje nuevo llamado Oak (el nombre fue tomado de un enorme roble que había frente al despacho de Mike Sheridan, coordinador y fundador del equipo de trabajo), más tarde y al existir ya un lenguaje llamado Oak, se rebautizó el lenguaje con el nombre de Java (Java, en el argot norteamericano significa café. El equipo de trabajo de Sheridan se reunía en una cafetería cercana a las instalaciones de Sun, donde discutían acaloradamente del proyecto. De aquí tomó su nombre Java). Hacia el otoño de 1992, el equipo había puesto en marcha un proyecto llamado Star 7 (*7), consistente en un control remoto personal y manual; para este proyecto el equipo se constituyó en una sociedad filial de Sun llamada la First Person, Inc. First Person perdió un concurso convocado por la Time Warner para desarrollar una televisión set-top box, First Person se disolvió y a Sun MicroSystems únicamente le quedó el lenguaje Java. A mediados de 1994, la popularidad de la Web atrajo la atención de los directivos de Sun e intuyendo las enormes posibilidades de este nuevo medio de comunicación, decidieron programar un navegador, empleando la tecnología Java. Aquel primer programa, que se llamó WebRunner, quedó listo en Mayo de 1995 y viendo las enormes posibilidades decidieron mejorar el navegador. El 23 de Mayo de 1995 en la Sun World 95 de San Francisco, Sun presenta su nuevo navegador HotJava y Netscape anuncia su intención de integrar Java en su navegador. A partir de aquel verano, los acontecimientos se desarrollan vertiginosamente para el mundo Java, sobre todo después del lanzamiento y distribución libre del Java Development Kit (JDK) 1.0. A finales de 1995 ( sólo seis meses después del lanzamiento de JDK!!) Java había firmado acuerdos con las principales firmas del software, para que pudieran utilizar Java en sus productos, entre otras Netscape, Borland, Mitsubishi Electronics, Dimension X, Adobe, IBM, Lotus, Macromedia, Oracle, SpyGlass... pero lo más espectacular fue el anuncio por parte de Bill Gates, presidente y director ejecutivo de Microsoft, el 7 de Diciembre de 1995, de la voluntad por parte de Microsoft, de obtener la licencia de utilización de Java. Aquel anuncio venía a mostrar claramente que Microsoft consideraba a Java como una parte importante en la estrategía global de Internet. Este anuncio es significativo, si se considera el desprecio que meses antes Bill Gates había mostrado hacia Java, calificándolo como un lenguaje más. El propio director general de Microsoft en España, había calificado a Java como un lenguaje para tostadoras. 1

14 1.2. CARACTERÍSTICAS Durante 1996 se planteó el debate de crear un terminal tonto o netpc, que únicamente sirviera para conectarse a la World Wide Web. En un principio se proyectó que este terminal estaría gobernado por un sistema operativo Java. La idea del netpc no cuajó como se esperaba, pero la idea de producir un sistema operativo Java, si que se ha desarrollado, se llama JavaOS. Sí algún día podrá competir con los sistemas operativos de moda como Windows 9x y Linux, sólo el tiempo lo dirá. También se sabe (aunque Sun no lo reconoce abiertamente) del proyecto de construir un chip Java que incorporaría todo el código de la Máquina Virtual Java en el propio hardware. Esto sin duda, aceleraría enormemente los tiempos de proceso de cualquier programa Java y lo equipararía a las velocidades de lenguajes que compiten directamente con Java, por ejemplo C++. El desarrollo de una tecnología de componentes (JavaBeans) al estilo OLE y ActiveX, fácilmente implementables en código, la incorporación vertiginosa por parte de Sun de nuevas API s (Java2D, APIS de telefonía, JDBC, etc..) más la renovación continua del compilador gratuito JDK (actualmente la versión más reciente de JDK es la 1.4) hacen de Java una tecnología prometedora que sin duda tiene un papel importante en la actualidad y lo tendrá cada vez más en el fúturo Características Portabilidad Efectivamente, Java está preparado para prácticamente la totalidad de las plataformas y sistemas operativos existentes hoy en día. El proceso de diseño y programación del código de un programa Java, no es distinto del de cualquier programa en otro lenguaje. Se usa un editor de texto para escribir las sentencias y el archivo de código fuente obtenido se guarda en un archivo con extensión.java. La novedad que introduce Java, viene dada en el proceso de compilación. En este proceso, el compilador traduce el código fuente a un formato especial llamado bytecode. Este bytecode es grabado en un archivo con extensión.class y no es directamente interpretable por el computador, sino por cualquier computador que tenga instalada una Máquina Virtual Java. Aquí radica la novedad que introduce Java y aunque es una idea sencilla, a la vez es potente, y es lo que ha hecho que Java sea lo mejor que le ha pasado a la informática desde la aparición del PC. No importa el procesador ni el sistema operativo, la única condición para que un programa Java pueda correr en un computador, es que tenga instalada la Máquina Virtual. Más lejos aún, ni siquiera es necesario que sea un computador. Cualquier aparato que implemente la Máquina Virtual Java o partes de ella, estará preparado para ejecutar programas Java; podría ser el sistema de menus de un video, el control de un edificio inteligente o simplemente una cafetera. Ya han habido otros lenguajes interpretados, como por ejemplo Visual Basic. Sin embargo, nunca se había empleado como punto de partida a un lenguaje multiplataforma, ni se había hecho de manera tan eficiente. Cuando Java apareció en el mercado, se hablaba de que era entre 10 y 30 veces más lento que C++. Ahora, con los compiladores JIT (Just in Time) se habla de tiempos casi comparables. Con la potencia de las máquinas actuales, esa lentitud es un precio que se puede pagar sin problemas, contemplando las ventajas de un lenguaje portable Lenguaje Interpretado Java no es exactamente un lenguaje interpretado del todo, se podría definir como un híbrido compiladointerpretado. Otros lenguajes interpretados, como por ejemplo el Perl, no necesitan de un proceso de compilación para estar preparados para correr. El caso más simple de un lenguaje interpretado, serían los archivos batch, simplemente se escriben las ordenes en un archivo y el computador las ejecuta. Si se ha cometido un error, por ejemplo escribiendo incorrectamente alguna órden, no se sabrá hasta que el computador intente ejecutar esa órden concreta. Entonces avisará con el correspondiente mensaje de error. Los archivos de código fuente Java, a diferencia de los de Perl o los archivos batch, no están preparados para correr en el computador, necesitan de un proceso de compilación previa. Entonces... Por qué se dice que Java es un lenguaje interpretado?. El archivo.class (bytecode) que se obtiene de la compilación previa del código fuente necesita de una Máquina Virtual que interprete el bytecode y lo ejecute en el computador. Así cómo los archivos batch, necesitan del interprete de comandos (command.com o sh) para ejecutarse, Java necesita de la 2 Departamento de Informática. U.T.F.S.M. Michael Moossen

15 CAPÍTULO 1. INTRODUCCIÓN A JAVA interpretación de la Máquina Virtual Orientación a Objetos Dado que Java es un lenguaje orientado a objetos, es imprescindible entender qué es esto y en qué afecta a los programas. Desde el principio, la carrera por crear lenguajes de programación, ha sido una carrera para intentar realizar abstracciones sobre la máquina. Al principio, no eran grandes abstracciones y el concepto de lenguajes imperativos es prueba de ello. Exigen pensar en términos del computador y no en términos del problema a solucionar. Esto provoca que los programas sean difíciles de escribir y mantener, al no tener una relación obvia con el problema que representan, no abstraen lo suficiente. Muchos paradigmas de programación, intentaron resolver este problema alterando la visión del mundo y adaptándola al lenguaje. Estas aproximaciones, modelaban el mundo como un conjunto de objetos o de listas. Funcionaban bien para algunos problemas pero no para otros. Los lenguajes orientados a objetos, más generales, permiten realizar soluciones que, leídas, describen el problema. Permiten escribir soluciones pensando en el problema y no en el computador que debe solucionarlo en último extremo. Se basan en cinco características: Todo es un objeto, es decir, cada elemento del problema debe ser modelizado como un objeto. Un programa es un conjunto de objetos diciéndose entre sí que deben hacer, por medio de mensajes, es decir, cuando se necesita que un objeto realice una acción, se le manda un mensaje. Más concretamente, se ejecuta un método de dicho objeto. Cada objeto tiene su propia memoria, que llena con otros objetos, es decir, cada objeto puede contener otros objetos. De este modo se puede incrementar la complejidad del problema, pero detrás de dicha complejidad siguen habiendo simples objetos. Todo objeto tiene un tipo, es decir, en jerga POO, cada objeto es una instancia (un caso particular) de una clase (el tipo general). Lo que distingue a una clase de otra, es la respuesta a la pregunta, qué mensajes puede recibir?. Todos los objetos de un determinado tipo pueden recibir los mismos mensajes. Por ejemplo, dado que un objeto de tipo Gato es también un objeto de tipo Animal, se puede hacer código pensando en los mensajes que se mandan a un animal y aplicarlo a todos los objetos de ese tipo, sin pensar si son también gatos o no. Eso de que la programación orientada a objetos intente establecer una relación biunívoca entre el espacio del problema y el de la solución está bien pero... cómo se logra que un objeto realice cosas útiles y necesarias? Por medio de su interfaz, que está definida en la clase del objeto. Por ejemplo, suponga que se tiene la clase Interruptor cuya interfaz define los métodos apagar() y encender(). Entonces se hace lo siguiente: Interruptor i = new Interruptor(); i.encender(); Primero se crea una referencia llamada i de tipo Interruptor, y luego se crea un objeto, una instancia de dicho tipo, por medio de la palabra reservada new. Se asigna ese objeto a la referencia por medio del signo =. Por último, se envía un mensaje al objeto recién creado y asignado, poniendo el nombre de la referencia, un punto, y el nombre del mensaje (método). Algunos lenguajes modernos y de éxito, como Visual Basic y Delphi, presumen de estar orientados a objetos y esto no es cierto, la verdad es que sólo están parcialmente orientados a POO y no aprovechan toda la potencia de esta filosofía de programación. C++ es el lenguaje que impulsó y popularizó la filosofía POO y sin duda es un lenguaje completamente orientado al objeto. Sin embargo, C++ incorpora algunas características no estándares (muchos autores discuten que formen parte de POO) como la herencia múltiple. Además, nada impide desarrollar un programa en C++ no orientado a objetos. En Java, esto no es posible, puesto que en Java todo son objetos. Incluso para desarrollar el más simple programa, se necesita construir por lo menos un objeto. Esto da una idea del grado de relación entre Java y POO, filosofía de programación de la que aprovecha todas sus ventajas. Michael Moossen Departamento de Informática. U.T.F.S.M. 3

16 1.3. INSTALACIÓN Integración en la Red El bytecode tiene la característica de ocupar muy poco espacio. Para tener una idea, el ejemplo final del capítulo(ver sección 1.4), ocupa en disco sólo 420 bytes. Esta virtud lo hace ideal para integrarse en la red Internet y desde un principio Sun pensó en la Worl Wide Web. En efecto, se pueden integrar pequeños programas Java (applets) en páginas web y así se tiene la posibilidad de distribuir fácilmente un contenido ejecutable. Este contenido, da la posibilidad al usuario que visita una página, de interactuar con esta y convertirla en algo más que un simple texto plano. Por otro lado, Java cuenta con una amplia biblioteca de clases para comunicarse, empleando protocolos de Internet TCP/IP e incluyendo otros protocolos tales como HTTP y FTP. El código Java, puede manipular recursos via URL con la misma facilidad a la que se está acostumbrado para acceder a un sistema de archivos local empleando Pascal, C o Basic Multitarea Java permite desarrollar programas de forma que corran varios procesos al mismo tiempo en un mismo computador. Mientras está corriendo un proceso, se puede ordenar que comience a ejecutarse otro de una forma muy sencilla. Esta es una gran ventaja que es bien aprovechada por los grandes servidores web y servidores de aplicaciones basados en Java, al explotar sin mayor esfuerzo las potencialidades del hardware de los servidores Robustez Un software robusto, es el que no se interrumpe fácilmente a consecuencia de fallos a la hora de programar o eventuales errores lógicos en la programación. Un lenguaje de programación que favorezca esta robustez del software, suele poner más restricciones al programador a la hora de escribir el código fuente. Tales restricciones incluyen las relativas a la escritura de datos y al empleo de punteros. El lenguaje de programación C, es poco riguroso a la hora de comprobar compatibilidades de escritura de datos durante la compilación y el tiempo de ejecución. El lenguaje C++ es un poco más rígido en este aspecto, pero retiene algunos errores respecto a la escritura de datos. Y para qué mencionar los problemas de C y C++ con punteros. En Java, al contrario la escritura de datos es más rigurosa. Java no permite el uso de punteros y por lo tanto es imposible la escritura de datos de forma accidental, lo que puede traer consecuencias catastróficas. Java también hace un chequeo de los límites de un arreglo antes de escribir en el, por lo que también es imposible escribir fuera de los límites de un arreglo. En definitiva, es imposible escribir en sitios indeseados de la memoria, cosa que un programador en C o C++ puede hacer accidental o deliberadamente Seguridad La posibilidad de distribuir un contenido ejecutable en la red, podría significar una auténtica fiesta para los hackers. Imagine por un momento, un applet en una página HTML que al ser descargado, formateé su disco duro o lea datos de su computador. Afortunadamente, los creadores de Java se preocuparon bastante de este detalle y crearon un modelo bastante seguro que hacen imposible que un applet pueda hacer algo dañino, por el hecho de descargarlo de un servidor web Instalación Las siguientes instrucciones ayudarán paso a paso, en la instalación de la plataforma de trabajo Java. Estas instrucciones son para usuarios de plataformas Win32, que incluye Windows 9x, Windows Me, Windows NT, Windows 2k y Windows XP. En realidad, sólo se necesitan dos cosas: 4 Departamento de Informática. U.T.F.S.M. Michael Moossen

17 CAPÍTULO 1. INTRODUCCIÓN A JAVA 1. Primero, la Edición Estándard de la Plataforma Java 2 (J2SE) que puede ser bajada desde java.sun.com/j2se/1.4/download.html. Instalar el J2SE es bastante fácil y ante cualquier duda, se pueden consultar las instrucciones de instalación del proveedor, en install-windows.html 2. Luego, se necesita un editor de texto para editar los archivos fuentes, para lo cual basta el Notepad de Windows. Pero se recomienda usar Editplus, que provee entre otras características, autocompletación de instrucciones, resaltado de sintaxis e integración con el compilador y la JVM. Editplus se puede obtener de Es aconsejable, crear un directorio para almacenar todos los programas que se escriban. Por ejemplo, C:/codebase Si se desea realizar una instalación en una plataforma Unix, los pasos a seguir son prácticamente los mismos. La mayor traba que ofrece este ambiente de desarrollo, es el editor de texto. En la mayoría de los casos habrá que conformarse con el vi Distribuciones Java está disponible en varias distribuciones, a saber: J2SE : Edición estándar que contiene todo lo necesario para empezar a programar aplicaciones profesionales. J2EE : Incluye el J2SE y además agrega un sin número de nuevas funcionalidades enfocadas al desarrollo de grandes aplicaciones empresariales. J2ME : Edición reducida(micro Edition), que permite el desarrollo de aplicaciones incrustadas, es decir, muy específicas sobre chips de escasos recursos. Por ejemplo, un controlador de lavadoras. Además existe un sin número de paquetes opcionales, tanto de SUN como de otros proveedores, que permiten ampliar las funcionalidades de Java. Por ejemplo, Java3D : Paquete de SUN que permite la programación de aplicaciones con gráficas 3D, es parte de un conjunto de paquetes adicionales agrupados bajo el nombre de Java Media, que incluye desde librerías para manipulación de imágenes, hasta herramientas de reconocimiento de voz. Para más información, ver JAXP : Java API for XML Processing, paquete de SUN que permite la manipulación de archivos XML. JavaMail : Paquete de SUN que permite la manipulación de correos electrónicos. Entre muchos otros Ayuda en Línea SUN ofrece un sin número de documentos de ayuda, manuales de referencia, tutoriales y libros online, todo disponible en Para encontrar documentación en español, ver la bibliografía de este curso [1] Primer Programa Java Ahora, se verá un primer ejemplo totalmente ejecutable, con el cual se revisarán los pasos necesarios para compilar y ejecutar un programa Java, como también los errores típicos que se podrían presentar. El código es el siguiente: Michael Moossen Departamento de Informática. U.T.F.S.M. 5

18 1.4. PRIMER PROGRAMA JAVA public class HelloWorldApp { public static void main(string[] args) { System.out.println("Hello World!"); Este código debe ser guardado en un archivo con el mismo nombre que la clase que define y con la extensión.java, en este caso, HelloWorldApp.java. Más específicamente, c:/codebase/helloworldapp.java. Hay que ser cuidadoso con el nombre de las clases y archivos, y en general, cualquier identificador, ya que Java diferencia entre mayúsculas y minúsculas. Ahora se analizará con detalle el código presentado. La primera línea define una estructura importantísima en el lenguaje Java, la estructura class. No es el momento de estudiarla en profundidad. Basta con saber por ahora, que una estructura class define una clase y que todo el código encerrado entre un par de llaves ({ ) inmediatamente después de la definición de esta estructura, pertenece a ella. No es el caso de este ejemplo, pero un archivo puede tener más de una estructura class. Si se ha programado anteriormente en cualquier lenguaje, se verá enseguida que la segunda línea define un método. En este caso, tiene un parámetro y no retorna valor alguno. Más adelante se profundizará en los métodos de Java. La finalidad de la tercera línea es simplemente escribir una línea de texto seguida por un retorno de carro en la pantalla. Luego, se indica el término del método y luego el término de la clase. El método main() es especialmente importante. Toda aplicación Java debe incluir en su código este método, puesto que es el punto de entrada de la aplicación. La primera sentencia del programa que se ejecuta, es la primera sentencia que esté dentro de este método. El método main() debe estar definido exactamente como se indica, lo único que se puede variar en la definición de este método, es el nombre que se le da al parámetro de este método. En un archivo o unidad de compilación, la clase (o estructura class ) que contiene al método main(), se denomina clase principal y tiene que cumplir dos normas. La primera, es que debe definirse como public (más adelante se verá que significa esto) y la segunda, que debe llamarse exactamente igual que el archivo en donde se encuentra definida Compilación Ahora es necesario compilar este archivo fuente para obtener un ejecutable, llamado en este caso HelloWorldApp.class. Para lograrlo es necesario ejecutar el comando: javac -classpath c:/codebase; %CLASSPATH % HelloWorldApp.java Lo que hace esta línea es, decirle al compilador que compile el archivo HelloWorldApp.java, y que para ello busque cualquier referencia que pueda hacer este archivo a otra clase, en el directorio c:/codebase, además de buscar donde normalmente lo haría. Otra manera, es setear la variable de entorno CLASSPATH, de tal forma que también incluya este directorio. Con esto, bastaría ejecutar: javac HelloWorldApp.java El compilador busca las referencias donde se le indica y si encuentra alguna que no esté compilada o esté desactualizada, la compila también. El error más típico durante la compilación es cuando se recibe el mensaje: Bad command or file name Que significa que Windows no puede encontrar el compilador Java, javac. Puede ser, que esto se deba a que no se ha instalado el J2SE adecuadamente o que sólo falta incluir el directorio %java home %/bin a la variable de entorno PATH, siendo %java home % el directorio de instalación del J2SE. 6 Departamento de Informática. U.T.F.S.M. Michael Moossen

19 CAPÍTULO 1. INTRODUCCIÓN A JAVA Ejecución Una vez creado el archivo ejecutable, se puede ejecutar usando el comando: java -cp c:/codebase; %CLASSPATH % HelloWorldApp En donde, la opción -cp tiene la misma interpretación que la opción -classpath al compilar. Y también puede ser omitida, una vez que esté bien seteada la variable de entorno. El error más típico al momento de la ejecución de un programa Java es, cuando se recibe el mensaje: Exception in thread "main"java.lang.noclassdeffounderror: HelloWorldApp Que significa que, Java no puede encontrar el archivo de bytecodes, HelloWorldApp.class. Lo que se puede deber a dos cosas: Primero, que se halla producido algún error al compilar (o no se halla compilado), o segundo, que la variable CLASSPATH está mal seteada. Michael Moossen Departamento de Informática. U.T.F.S.M. 7

20 1.4. PRIMER PROGRAMA JAVA 8 Departamento de Informática. U.T.F.S.M. Michael Moossen

21 Capítulo 2 Principios de Programación con Java 2.1. La Sintaxis del Lenguaje La sintaxis de Java, como la de (casi) cualquier otro lenguaje de programación, está dada por distintos elementos contructivos básicos: Palabras Clave Identificadores Literales Separadores Comentarios A continuación se detallará rápidamente cada uno Palabras Clave Las palabras clave son palabras reservadas por el lenguaje Java, para un propósito definido y específico. Estas no se pueden usar como identificadores. Y son: abstract catch default false goto int null return switch true boolean char do final if interface package short this try break class double finally implements long private static throw void byte const else float import native protected super throws volatile case continue extends for instanceof new public synchronized transient while Además, el lenguaje se reserva unas cuantas palabras más, que hasta ahora no tienen una función específica. Estas son: cast const future generic goto inner operator outer rest var 9

22 2.1. LA SINTAXIS DEL LENGUAJE La Palabra Clave null Este identificador es la referencia nula. Es decir, cuando se tiene una referencia a un objeto, y aún no se le quiere asignar uno, pero se desea utilizar esa variable, entonces se le asigna null : public class Nulo { public static void main(string[] args) { String cad = null ; if (cad == null ) System.out.println("Es nulo"); Como Java es un lenguaje seguro, si no se inicializa la referencia cad y se intenta acceder a ella, dará un error de compilación. Esto tiene una excepción: cuando se crea un arreglo, todos sus elementos se inicializarán automáticamente en null. Esto se debe a que en tiempo de compilación, Java no tiene manera de saber qué elementos están inicializados y cuales no, por lo que no puede dar un error Identificadores Un identificador es un nombre que se da a un elemento, es decir, una clase, un método, una propiedad, etc. Java distingue entre mayúsculas y minúsculas, y los identificadores pueden comenzar por una letra, un subrayado o un símbolo de peso, siendo los siguientes caracteres letras o dígitos, y pueden ser de hasta más de 500 carácteres de largo. Por ejemplo, son identificadores válidos: contador nombre usuario $dinero Se recomienda no usar identificadores que comiencen con el signo peso o por el signo de subrayado Literales Los literales son un mecanismo utilizado para expresar un valor constante (en tiempo de compilación). Java utiliza cinco tipos de elementos literales: enteros, reales en coma flotante, booleanos, caracteres y cadenas: Tipo Enteros Reales Booleanos Caracteres Cadenas Ejemplo 21, 0xDC 3.14, 2e12 true, false x, \t, \u0234 "Esto es una cadena" Notar que, mientras los caracteres se expresan encerrados en comillas simples ( ), las cadenas lo hacen en comillas dobles (") Separadores Existen otros elementos con significado especial en Java. Estos son los separadores, que en general, delimitan cierta región del código, dándole un cierto significado específico, según la siguienta tabla: 10 Departamento de Informática. U.T.F.S.M. Michael Moossen

23 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Separador Descripción ( ) Contienen listas de parámetros, tanto en la definición de un método, como en la llamada al mismo. También se utilizan para modificar la precedencia en una expresión (Sección 2.4.8), contener expresiones para control de flujo (Sección 2.5.1) y realizar conversiones de tipo (Sección 2.3.2). { Se utilizan para definir bloques de código, definir ámbitos (Sección 2.2.2) y contener los valores iniciales de arreglos (Sección 2.2.4). [ ] Se utilizan tanto para declarar arreglos o matrices, como para referenciar valores dentro de los mismos (Sección 2.2.4). ; Separador de sentencias., Separador de identificadores consecutivos en la declaración de variables y en las listas de parámetros. También se utiliza para encadenar sentencias dentro de una estructura for (Sección 2.6.2).. Separa un nombre de propiedad o método de una variable de referencia (Sección 2.3.3). También separa nombre de paquete de los de un subpaquete o una clase. (Sección 3.10) Comentarios Los comentarios en Java son como los de C++ pero con ciertas funcionalidades especiales, para más detalle de esto ver sección A.1. Los comentarios básicos son: Comentarios de Fin de Línea Este tipo de comentario es introducido por // y dura hasta final de línea: // Esto es un comentario Comentarios de Párrafo Los comentarios de párrafo o bloque, pueden ocupar más de una línea, comenzando con /* y terminando con */ : /* Esto es un comentario */ 2.2. Variables Declaración Declarar una variable es darla a conocer al compilador y así tenerla lista para su posterior uso. Declarar una variable en Java es muy sencillo. En primer lugar se indica el tipo de dato al que se quiere que pertenezca esa variable y en segundo lugar su nombre. Algunos ejemplos de declaraciones: int variable; short variable short; long largo; Se puede inicializar la variable a un determinado valor, en el mismo momento en que se declara. Esto es recomendable y se sugiere como regla de oro, siempre inicializar las variables, ya sea con un valor útil, o cero o null para luego asignarle un valor útil en tiempo de ejecución. Para inicializar una variable se hace uso del operador = seguido del valor que se quiere asignar a la variable, por ejemplo: int variable = 234; En realidad esta última sentencia equivaldría a estas dos sentencias seguidas: Michael Moossen Departamento de Informática. U.T.F.S.M. 11

24 2.2. VARIABLES int variable; variable = 234; Pero, por comodidad y claridad en el código, es recomendable la primera forma. Otros ejemplos de declaraciones-inicializaciones: String nombre = "Jose Antonio"; int fecha = 2000; short negativo = -231; float decimal = F; char caracter = \u0234 ; Ámbito Una variable declarada dentro de un método, tiene su ámbito restringido al bloque delimitado por llaves ({ ), en que fue declarada. Por ejemplo, void main() {... int a; ı//aqu... ı//aqu termina comienza el ámbito de a Las variables declaradas en un bloque pueden ser sobreescritas por variables declaradas en un subbloque. Por ejemplo, void main() { int a = 1;... if (cond) { String a = "abc"; Se debe ser muy cuidadoso con este tipo de código, por lo que se recomienda evitarlo. Por el contrario, en el caso de las variables declaradas a nivel de clase, también llamadas atributos o propiedades de la clase, el ámbito de acceso es global dentro de la clase y el acceso fuera de la ella está determinado por el modificador de acceso con que se declara la variable 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 por medio de los siguientes modificadores de acceso: public : Cualquier clase puede acceder a las propiedades y métodos públicos. protected : Sólo las clases heredadas pueden acceder a las propiedades y métodos protegidos. private : Las variables y métodos privados sólo pueden ser accedidos desde dentro de la clase. 12 Departamento de Informática. U.T.F.S.M. Michael Moossen

25 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Por defecto También llamado, friendly. Sólo las clases heredadas y aquellas situadas en el mismo paquete pueden acceder a las propiedades y métodos. Por ejemplo: class Acceso { public int publica = 0; private char privado = x ; protected float protegido = 2.7f; double amigo = 1.3; Arreglos Un arreglo (array) es una manera de agrupar valores de un mismo tipo bajo un mismo nombre. Para acceder a los valores individuales, se asigna a cada uno de ellos un número denominado índice, el cual comienza desde cero. En Java, se pueden declarar arreglos de dos maneras: char c[]; char [] c; De cualquiera de las dos formas, se obtiene un arreglo de caracteres. A partir de ahora, cuando se declare un arreglo, se utilizará la segunda manera, que separa la declaración de tipo del nombre de la variable; al contrario que la primera, que sólo representa un mal recuerdo de C y C++. Crear matrices (arreglos multidimensionales) es tan sencillo como añadir corchetes: char [][] c; Los arreglos en Java son en realidad objetos (como casi todo) y por lo tanto se inicializan llamando a un constructor con new (ver sección 3.6.1), aunque el constructor tenga una sintaxis totalmente distinta a la vista hasta ahora: int [][] numeros = new int [30][10]; Existe otra manera de hacerlo y es indicando los valores iniciales en la declaración: String[] paises = { "Chile", "Argentina", "Perú"; Esta declaración es equivalente a crear el arreglo por medio de un constructor, e ir asignándole los valores uno a uno: String[] paises = new String[3]; paises[0] = "Chile"; paises[1] = "Argentina"; paises[2] = "Perú"; Como se puede observar, para acceder a un valor específico de un arreglo, se pone el nombre del mismo, acompañado del índice correspondiente al valor encerrado entre corchetes. Esto también es válido cuando se quiere consultar el valor del mismo: public class Arreglos { static String[] paises = { "Chile", "Argentina", "Perú" public static void main(string[] args) { System.out.println(paises[1]); Michael Moossen Departamento de Informática. U.T.F.S.M. 13

26 2.3. TIPOS DE DATO Y, como buen objeto que son, todos tienen un método llamado length, que indica la longitud del arreglo. Por ejemplo, public static void main(string[] args) { System.out.println(paises[1]); System.out.println(paises.length ); Nota: En realidad, como se puede apreciar en el ejemplo, length es un atributo y no un método. Conviene recordar que los arreglos son objetos, porque a la hora de pasar argumentos a una función, se pasarán por referencia y no por valor, como los tipos de dato básicos. Por otra parte, hay que tener ciudado al inicializar arreglos múltidimensionales, por ejemplo: String[][] paises = { {"Chile", "Argentina", {"Perú" ; Esto produce un arreglo de dos arreglos de String, uno de largo dos y el otro de largo uno! Tipos de Dato Básicos En Java, todo lo que se mueve es un objeto... excepto los tipos de datos básicos, es decir, números enteros, reales, caracteres, valores lógicos, etc. Todo lo demás serán objetos o, mejor dicho, referencias a objetos. Son los únicos valores que se crean sin utilizar el operador new (ver sección 45). Y son los siguientes: Tipo Descripción Tamaño Clase Encapsuladora boolean Valor lógico 1 bit Boolean char Carácter 16 bit Character byte Entero muy pequeño 8 bit Byte short Entero pequeño 16 bit Short int Entero normal 32 bit Integer long Entero grande 64 bit Long float Número real de precisión simple 32 bit Float double Número real de doble precisión 64 bit Double void Tipo vacío Void Podemos crear variables de estos tipos de la manera normal en todos los lenguajes que siguen a C: int numero = 12; El definir un valor al declararlo no es necesario, especialmente porque en Java todos tienen un valor por defecto (0 en los números, false en los booleanos y el carácter \x0 ); en ese caso se puede escribir simplemente: int numero; Siempre se debería preferir el uso de las clases que encapsulan(en inglés, Wrappers) los tipos básicos, a usar los tipos básicos por si mismos. En este ejemplo sería: Integer numero = new Integer(12); Además, todas las clases encapsuladoras de tipos de datos básicos, excepto Boolean, tienen atributos MIN VALUE y MAX VALUE, con lo cual se puede obtener el rango de valores que acepta el tipo de dato particular. Así ejecutando: 14 Departamento de Informática. U.T.F.S.M. Michael Moossen

27 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Se obtiene: class Wrappers { public static void main(string[] args) { System.out.println("Byte: "+ Byte.MIN VALUE + "... "+ Byte.MAX VALUE); System.out.println("Short: "+ Short.MIN VALUE + "... "+ Short.MAX VALUE); System.out.println("Integer: "+ Integer.MIN VALUE + "... "+ Integer.MAX VALUE); System.out.println("Long: "+ Long.MIN VALUE + "... "+ Long.MAX VALUE); System.out.println("Float: "+ Float.MIN VALUE + "... "+ Float.MAX VALUE); System.out.println("Double: "+ Double.MIN VALUE + "... "+ Double.MAX VALUE); System.out.println("Character: "+ (int )Character.MIN VALUE + "... "+ (int )Character.MAX VALUE); Tipo Mínimo Máximo Byte Short Integer Long Float 1.4E E38 Double 4.9E E308 Character Notar que, para los número reales se indica sólo el rango positivo, a esto hay que agregarle el mismo rango negativo Conversión de Tipos Como en muchos otros lenguajes de programación, en Java también es posible asignar un valor de un tipo a una variable de otro tipo. A pesar que esto no representa mucha dificultad, es necesario estar consciente que se pueden dar ciertos efectos secundarios al convertir datos, para lo cual hay que tener claros algunos conceptos y procedimientos del compilador: Conversión de tipos Automática Si al hacer la conversión de un tipo a otro, se dan las dos siguientes premisas: Los dos tipos son compatibles. El tipo de la variable destino es de un rango mayor al tipo de la variable que se va a convertir. Entonces, la conversión entre tipos es automática, ésto es, la JVM tiene toda la información necesaria para realizar la conversión sin necesidad de ayuda externa. A ésto se le conoce como conversión implícita y cuando se habla de los tipos numéricos de Java, se le da el nombre de widening conversion que traducido vendría a ser algo así como conversión por ampliación. No obstante, no todos los tipos numéricos son compatibles con char o boolean (concretamente byte y short ) y estos dos tampoco lo son entre ellos. Conversión de Tipos Incompatibles Cuando alguna de las 2 premisas anteriores no se cumple, entonces la conversión automática es imposible (salvo que se esté evaluando una expresión como se ve en la sección siguiente), el compilador da un error diciendo algo así como la variable tal necesita una conversión explícita. Pero, no es que no se pueda realizar tal conversión, sino que la JVM necesita de información adicional: el tipo al que se va a convertir la variable de origen. Pues bien, esta conversión explícita es llamada un casting de tipos. Cuando se trata de tipos numéricos, a este tipo de Michael Moossen Departamento de Informática. U.T.F.S.M. 15

28 2.3. TIPOS DE DATO conversión se le llama narrowing compresion que se podría traducir como conversión por estrechamiento dado que se está recortando la variable para poder guardarla en el tipo destino. La expresión general del casting de un tipo es: (tipo destino) valor donde tipo destino indica el tipo al que se quiere hacer la conversión explícita del valor deseado. En el caso de los tipos numéricos, al recortar el valor de la variable de rango mayor para que quepa en el tipo de rango menor, resulta que se pierde información. Más adelante, se verá en qué se traduce esa pérdida de información según sean los tipos. Promoción de Tipos Automáticamente al Evaluar una Expresión Aquí radica uno de los puntos donde la conversión puede dar problemas si no se sabe, pero que una vez asimilado es una de las muchas ventajas que Java tiene frente a otros lenguajes. El caso es que cuando la JVM, tiene que analizar una expresión, existe la posibillidad real de que el resultado intermedio de una operación exceda el rango de los operandos. Para solventar este problema, lo que Java hace es una promoción automática de todos los operandos de tipo byte o short a int. Pero esto también puede llevar al error de que se haga una conversión de tipos incompatibles y, al no hacer el casting correspondiente, la compilación fallará. Como se ve en el siguiente ejemplo: Caso 1: Caso 2: byte a = 40; byte b = 50; int c = a * b; //el compilador no dará ningún error. byte a = 25; a = a*2; //ERROR: Explicit cast needed to convert int to byte. Ahora, cómo puede dar este error si el resultado no excede del rango de un tipo byte (-128, 127)?. Pues bien, lo que ocurre, es que al hacer la multiplicación, Java ha promocionado automáticamente este valor a int y por eso para volver a guardarlo en un tipo byte, se tiene que hacer una conversión explícita. En el primer caso, este error no se produce porque el resultado de la operación se guarda en un int. a = (byte )a*2; //ahora el compilador no dará ningún error. Para concluir con las reglas de promoción de tipos cuando se evaluan expresiones, se dan a conocer otros detalles más, que es conveniente tener en cuenta: Si alguno de los operandos es long, entonces la expresión entera promociona a long. Si alguno de los operandos es float, entonces la expresión entera promociona a float. Si alguno de los operandos es double, entonces el resultado será double. Por último, recuerde que cuando un método devuelve Object, dado que esta clase es la super-clase de todas, se puede y a veces se debe hacer un casting explícito al tipo de la variable que interesa. Por ejemplo, si se tiene un vector en el que se introducieron elementos del tipo MiClase, para poderlos recuperar, hay que ejecutar: MiClase var = (MiClase) vector.get(index); A continuación una serie de casos prácticos para concluir este tema. 16 Departamento de Informática. U.T.F.S.M. Michael Moossen

29 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA class ConversionTipos { public static void main(string args[]) { byte b; int i = 257; double d = ; double d2; float f = 5.65f; char c = c ; short s = 1024; b = (byte ) i;//efecto: se devuelve (i modulo 256), rango de byte. System.out.println(b); i = (int ) d;//efecto: truncamiento de la parte decimal. System.out.println(i); b = (byte )d;//efecto: truncamiento y luego (result truncado modulo 256) System.out.println(b); i = (int )f;//efecto: truncamiento de la parte decimal System.out.println(i); i = c;//correcto por ser int, pero un byte necesitaría un cast explícito. System.out.println(i); f = c; d = c;//correcto. i = b * c;//efecto: la operación promociona a int. System.out.println(i); d2 = (f*b) + (i/c) - (d*s); /* efecto: el resultado de la promoción de todas las expresiones es un double: (f*b) promociona a float, (i/c) promociona a int y (d*s) promociona a double. Luego, float + int - double promociona a double. */ System.out.println(d2); Objetos Ahora bien, a parte de los tipos de dato básicos, que pueden ser cien por ciento reemplazados por sus respectivos wrappers, todo lo demás son objetos. Para declarar un objeto en el código, primero se indica la clase del objeto a crear, seguido del identificador del objeto. El objeto declarado, también puede ser inicializado directamente, en la misma línea. Por ejemplo, String S = "abc"; Sus atributos y métodos públicos pueden ser accesados por medio del operador punto (.). Siempre y cuando la referencia no sea nula, en cuyo caso se genera un error en tiempo de ejecución (NullPointerException). Por ejemplo, Michael Moossen Departamento de Informática. U.T.F.S.M. 17

30 2.4. OPERADORES Double.MIN VALUE; S.trim(); 2.4. Operadores Los operadores sirven para unir identificadores y literales para formar expresiones, que son el resultado de una operación. Los de Java son muy parecidos a los de C y C++. De poco serviría declarar variables o inicializarlas, si no se puede operar con estas: sumarlas, restarlas, multiplicarlas, compararlas, etc. Para este tipo de operaciones sobre las variables, existen los operadores. En función del número de operandos sobre los que actúan y el tipo de operación que efectúan, los operadores en Java se clasifican según el siguiente cuadro: Operadores Unarios Tipo Operadores Unarios ! () Aritméticos * / % + - Rotación << >> >>> Comparación < <= > >= ==!= Lógicos & ˆ && Ternarios?: Asignación = Se caracterizan por actuar sobre un solo operando: Operadores Unarios ++ y -- Estos operadores incrementan o decrementan, respectivamente, la variable en una unidad. int val = 0; ++val; La variable entera val ahora tiene el valor 1. Esta ha sido su utilización prefija. Ahora, se verá su utilización sufija. int val = 0; val++; La variable entera val ahora tiene el valor 1. A pesar que a simple vista, pareciera que usar el operador ++ en prefijo o en sufijo es lo mismo. La respuesta es no. En este ejemplo, se verá la diferencia: int x = 34; int y = ++x; El valor final de x es 35 y el valor final de y es 35 también, sin embargo: int x = 34; int y = x++; El valor final de x es 35, pero el valor final de y es 34. La diferencia radica, en que al utilizar el operador en sufijo, primero se asigna el valor de x a y, y después se incrementa x en una unidad. Por el contrario, usando el operador en prefijo, primero se incrementa x en una unidad, y luego se asigna el valor de x a y. 18 Departamento de Informática. U.T.F.S.M. Michael Moossen

31 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Operadores Unarios + y - Los operadores + y - dan signo positivo o negativo a una expresión numérica. int variable = -2; short variable = -(x + 5); long siguiente = +4; Operador Unario de Negación! El operador! invierte el valor de una expresión booleana. Esto es, convierte true a false y viceversa. boolean bool=!true ; La variable bool tiene el valor false. Operador Unario de Inversión Este operador( ) actúa sobre valores numéricos enteros y realiza una inversión a nivel de bits. Todos los bits del número sobre el que actúa son cambiados, incluyendo el bit del signo. Los ceros por unos y los unos por cero. Operador Unario de Casting () Este operador es importante. Se llama operador de conversión y se utiliza para convertir expresiones numéricas de un tipo a otro, como se vió en la sección int var=234; long largo=(long )var; Operadores Aritméticos Estos operadores realizan operaciones aritméticas en los operandos sobre los que actúan. No hay mucho que explicar sobre estos operadores, porque hacen justo lo que se espera de ellos. El operador + suma, el operador - resta, el operador * multiplica y el operador / divide. Unicámente, se explicará el operador % u operador módulo. Operador Aritmético Módulo % Da como resultado el resto resultante de la división entre los dos operandos sobre los que actúa. int resto = 14 % 3; La variable resto tiene ahora el valor 2. Todos estos operadores aritméticos actúan sobre valores numéricos, excepto el operador + que también lo hace sobre cadenas de caracteres. Es posible sumar el contenido de dos cadenas de caracteres para formar una sola. Por ejemplo: String s1 = "Este curso "; String s2 = "trata sobre el lenguaje Java"; String res = s1 + s2; La cadena res tiene ahora el valor "Este curso trata sobre el lenguaje Java". También es posible, sumar una cadena y una variable. Automáticamente Java convertirá el contenido de esa variable a un valor de cadena representable. Por ejemplo: int var = 365; String s = "Un año tiene "; String res = s + var + "días"; La cadena res tiene ahora el valor "Un año tiene 365 días". Michael Moossen Departamento de Informática. U.T.F.S.M. 19

32 2.4. OPERADORES Operadores de Rotación Estos operadores actúan a nivel de bits. Operadores de Rotación >> y << Rotan a la derecha o la izquierda los bits del valor, el número de posiciones especificado. Por ejemplo: int var = 1; var <<2; La variable var ahora tiene el valor 4. Lo que se explicará a continuación. Inicialmente la variable var tiene el valor 1. Su representación binaria es: En la primera rotación, todos los bits se rotan a la zquierda una posición: Y en la segunda rotación: Cuyo valor en la numeración decimal es 4. Observese que los bits que van apareciendo por la derecha son ceros. La rotación hacia la derecha es similar, excepto que cambia el sentido de rotación de los bits. Operador de Rotación >>> El operador >>> es ligeramente distinto al operador >> y a menudo es fuente de confusión. En realidad es muy sencillo de comprender, pero se necesita entender con más detalle, como se representan los números en memoria. Como ya se ha visto el tipo de dato int es un entero con signo de 32 bits. El número 4 en memoria es representado así: Pero, para representar números negativos se utiliza el primer bit como una señal que indica si el número es positivo (bit igual a cero) o negativo (bit igual a 1). La representación del número -4 es entonces: Cuando se rotan números negativos con el operador de rotación a la derecha ( >> ) antes visto, el signo del número se respeta. Imagine que se rota el número anterior a la derecha con el operador >>, el resultado sería: Sin embargo, cuando se usa el operador >>> el bit de signo, no se respeta y es rotado también: Así, el resultado de la operación: int var = -4; var >>>1; La variable var tiene ahora el valor Sin embargo: int var = -4; var >>1; La variable var tiene ahora el valor Departamento de Informática. U.T.F.S.M. Michael Moossen

33 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Operadores de Comparación Los operadores de comparación dan como resultado siempre una expresión booleana (true o false ) independientemente de que los operandos sobre los que actuán sean numéricos, booleanos, de caracter, etc. Los operadores de comparación en Java son: <, <=, >, >=, == y!=. Literalmente y de izquierda a derecha estos operadores se leen así: menor que, menor o igual que, mayor que, mayor o igual que, igual que y distinto de. Aunque su uso es bastante obvio, se verá un ejemplo por cada tipo de operador de comparación para su mejor comprensión: boolean resultado; resultado = (5 <3); La variable resultado se inicializa con el valor false. boolean resultado = (3 <= (5-2)); La variable resultado se inicializa con el valor true. boolean resultado = (10 >23); La variable resultado se inicializa con el valor false. boolean resultado = (10 >= 10); La variable resultado se inicializa con el valor true. boolean resultado = (20 == 21); La variable resultado se inicializa con el valor false. boolean resultado = (20!= 21); La variable resultado se inicializa con el valor true Operadores Lógicos Los operadores lógicos pueden operar sobre operandos numéricos o sobre operandos booleanos. En el primer caso, devuelven una expresión numérica y en el segundo caso devuelven una expresión booleana. Cuando operan sobre expresiones numéricas, estos operadores actúan a nivel de bit. Operador Lógico AND & La tabla lógica de la operación & es la siguiente: & Cuando se usa este operador con dos operandos numéricos, este operador actúa a nivel de bit. Por ejemplo: int a = (6 & 2); El valor de a es ahora 2, puesto que efectuando la operación lógica & sobre 6 y 2 se obtiene: (representación binaria de 6) (representación binaria de 2) & (que en numeración decimal es 2) Michael Moossen Departamento de Informática. U.T.F.S.M. 21

34 2.4. OPERADORES Cuando se usa este operador con dos operandos booleanos, se obtiene una expresión booleana y su uso es aún más sencillo. Sólo hay que ver la tabla lógica de la operación & y tener en cuenta que false es cero y true es 1. Por ejemplo: boolean valor = (true & false ); La variable valor tiene ahora el valor false. Operador Lógico XOR ˆ Su uso es similar al del operador lógico &. La tabla lógica de esta operación es: Por ejemplo: int a = (6 ˆ 2); ˆ El valor de a es ahora 4 puesto que efectuando la operación lógica ˆ sobre 6 y 2, se obtiene: (representación binaria de 6) (representación binaria de 2) ˆ (que en numeración decimal es 4) En el caso de que los operandos sean booleanos, se devuelve un valor booleano. boolean valor = (true ˆtrue ); La variable valor tiene ahora el valor false. Operador Lógico OR La tabla lógica de la operación es la siguiente: Por ejemplo: int a = (6 2); El valor de a es ahora 6 puesto que efectuando la operación lógica sobre 6 y 2, se obtiene: (representación binaria de 6) (representación binaria de 2) (que en numeración decimal es 6) En el caso de que los operandos sean booleanos, se devuelve siempre un valor booleano. Por ejemplo: boolean valor = (true false ); La variable valor tiene ahora el valor true. 22 Departamento de Informática. U.T.F.S.M. Michael Moossen

35 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Operadores Lógicos && y Estos operadores sólo pueden usarse sobre operandos booleanos y tienen una importante diferencia respecto a los operadores & y. Esta es, que puede que no se evalúe el operando de la derecha si no es necesario, con lo que se gana en eficiencia. Si, por ejemplo, se efectúa la operación "exp a exp b" y exp a vale true, entonces la expresión exp b no es evaluada, puesto que da lo mismo que esta sea true o sea false debido a que el valor devuelto va a ser invariablemente true. Esto mejora la mucho eficiencia en el caso de que exp b sea una expresión booleana compleja, por ejemplo, formada de cientos de operandos Operador Ternario También llamado Operador Condicional, es quizás el operador más complejo de todos, pero es bastante útil. Toma tres operandos, una condición booleana y dos expresiones. El primer operando es una condición booleana y si su valor es true se evalúa el segundo operando(la primera expresión), pero si es false se evalúa el tercer operando(la segunda expresión). Se verá un ejemplo para ver su funcionamiento: boolean condicion = true ; int resultado = (condicion)? 13: -13; La variable resultado se inicializa con el valor 13. Sin embargo: boolean condicion = false ; int resultado = (condicion)? 13: -13; La variable resultado se inicializa con el valor Operadores de Asignación Operador de Asignación = Asigna un valor a la expresión a la izquierda del operador. Puede operar sobre todo tipo de datos. int b = -123; int a = b; La variable a se inicializa con el valor Operadores de Asignación *=, /=, %=, +=, &=, ˆ=, =, <<=, >>= y >>>= Además de realizar la asignación, primero se realiza la operación indicada. Por ejemplo: int val = 6; val *= 4; La variable val tiene ahora el valor Precedencia de Operadores Cuando se necesita construir una expresión compleja con varios operandos y operadores, es importante saber cuales de estos se resuelven primero, puesto que esto puede variar el resultado final de la expresión. Por ejemplo en la expresión: int x=2+4/2; Michael Moossen Departamento de Informática. U.T.F.S.M. 23

36 2.4. OPERADORES Si se resuelve la expresión sumando 2 más 4 y el resultado se divide entre 2 se obtiene en x el valor 3. Sin embargo, si primero se divide 4 entre 2 y al resultado se le suma 2 se obtiene en x el valor 4. Java sigue unas estrictas pero sencillas reglas a la hora de evaluar estas expresiones. El análisis de la expresión se realiza siempre de derecha a izquierda, pero aún no está resuelto el problema, porque aún siguiendo este orden, pueden producirse resultados inconsistentes. Para resolverlo Java dá un nivel de prioridad distinto a cada operador. Según la tabla siguiente: Precedencia Operadores Mayor. [ ] ( ) ++ --! * / % + - << >> >>> < > <= >= ==!= & ˆ &&? : Menor = En la tabla los elementos de la misma fila tienen la misma precedencia. De los elementos de la misma columna tienen mayor precedencia los que están más arriba. Se verá con un ejemplo, como resolvería el compilador de Java una expresión compleja: int res = ( ) * 12 / 2 - (- 23); El operador de mayor precedencia es el paréntesis (), sin embargo, hay dos paréntesis. Se resuelve primero el de la derecha. Quedaría la expresión: int res = ( ) * 12 / ; A continuación el paréntesis de la izquierda: int res = 5 * 12 / ; Los operadores de división y de multiplicación tienen superior precedencia al operador suma, pero la misma precedencia entre ellos, luego se resuelve primero el operador de la derecha. La división: int res = 5 * ; El operador multiplicación tiene mayor precedencia que el operador suma. int res = ; Y finalmente: res = 53; Es una buena práctica, NO valerse de la precedencia de los operadores, sino que siempre incluir la cantidad suficiente de paréntesis, de tal forma que no quede ninguna duda del sentido de la expresión. 24 Departamento de Informática. U.T.F.S.M. Michael Moossen

37 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Operaciones con Objetos El operador == A pesar que dos objetos pueden compararse usando el operador ==, esto generalmente, no generará el resultado esperado, ya que como Java no soporta sobrecarga de operadores, este operador compara las referencias de los objetos, es decir, si ocupan el mismo espacio de memoria, y no su contenido. Sin embargo, esto puede producir confusión, como se ve en el siguiente código: String S1 = "a"; String S2 = "a"; if (S1==S2) { System.out.println("Que pasó aquí?"); Lo que sucede es que, a pesar de que S1 y S2 se definieron por separado, el compilador optimiza el código, reconociendo la repetición del literal "a", guardándolo en sólo una dirección de memoria, a la que luego apuntan tanto S1, como S2. Por lo tanto, para comparar objetos, siempre usar el método compareto() si lo implementan, o usar el método equals() que se verá más adelante, en la sección 2.9. La Interfaz Comparable Todas los wrappers de tipos de datos númericos básicos, implementan una interfaz que provee el siguiente método: int compareto(object o) Esto debido a que Java no soporta sobrecarga de operadores, por lo que cualquier intento de usar operadores de comparación con objetos, resultará en un error de compilación. Así, x.compareto(y) retorna un entero negativo si x<y, cero si x=y y un entero positivo si se cumple que x>y. Con lo cual, se puede escribir código como el siguiente ejemplo: Double PI = new Double(3.1415); Double E = new Double(2.7183); if (E.compareTo(PI)<0) { System.out.println("E <PI"); Que es equivalente a: if (E.doubleValue()<PI.doubleValue()) { System.out.println("E <PI"); El Operador instanceof En ocasiones, puede ser necesario identificar en tiempo de ejecución el tipo de objeto con el que se está tratando. Debido a las capacidades de polimorfismo de Java, cuando se tiene una referencia a un objeto, puede que sólo se conozca que dicho objeto es una instancia de una clase o de cualquiera de sus derivadas. Y en ocasiones, puede ser necesario saber de qué clase específica es dicho objeto. Java provee de una manera sencilla para identificar el tipo de un objeto: public class Identificacion { public static void main(string[] args) { Object o = new String("Hola"); Michael Moossen Departamento de Informática. U.T.F.S.M. 25

38 2.5. CONTROL DE FLUJO if (o instanceof String) System.out.println("Es una cadena") else System.out.println("No es una cadena"); Al ejecutar el código: efectivamente es una cadena. Si se inicializa a cualquier cosa que no fuera una cadena diría que no lo es Control de Flujo En Java existen varias sentencias de control de flujo, las mismas que provee C o C La Sentencia if El más básico, sencillo y utilizado es el if -else. Dado que else formas: o if (expresión booleana) { sentencias if (expresión booleana) { sentencias else { sentencias es opcional, se puede escribir de dos Esta construcción permite ejecutar la primera sentencia si la expresión lógica es verdadera. En caso de que sea falsa (y de que el if -else esté en la segunda forma) se ejecutará la segunda sentencia La Sentencia switch Para bifurcar también existe el switch : switch (expresión) { case valor1: sentencia; break ; case valor2: sentencia; break ; case valor3: sentencia; break ;... default : sentencia; break ; 26 Departamento de Informática. U.T.F.S.M. Michael Moossen

39 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA La estructura switch compara el valor contenido en la expresión, con cada uno de los valores situados después del case, ejecutando en caso de que exista coincidencia, el código situado a la derecha de los dos puntos. Ese código no se engloba mediante llaves. Si no existe coincidencia, se ejecuta la sentencia optativa default. Cada case debería terminar con una sentencia break, de no ser así se ejecuta el código del case siguiente Ciclos Ahora se verá las distintas estructuras que ofrece Java para realizar iteraciones La Sentencia while y Las más genéricas son while y do -while : while (expresión booleana) { sentencia do { sentencia while (expresión booleana); En ambos casos se ejecutará la sentencia, mientras se cumpla la expresión booleana. La diferencia consiste en que en el primer caso, esa expresión se comprueba antes de iterar (ejecutar la sentencia) y en el segundo después. Por tanto, en ese último caso, la sentencia se ejecutará siempre al menos una vez La Sentencia for Otra iteración interesante es el bucle for: for (inicialización; expresión booleana; paso) { sentencias En este caso lo primero que hará la JVM, será ejecutar las sentencias (separadas por comas) contenidas en inicialización, luego comprobará si es cierta la expresión booleana y ejecutará el código de sentencia. Finalmente ejecutará las sentencias (separadas por comas) contenidas en paso y comprobará de nuevo la expresión booleana Control de Flujo en Ciclos Las capacidades de controlar el flujo de los ciclos, es bastante limitada, con lo que se ha visto hasta el momento. Es por eso que Java, cuenta con un par de sentencias que ayudan a controlar el flujo en ciclos. La Sentencia break Esta sentencia permite abortar un ciclo definitivamente. Por ejemplo: boolean cond = false ; int sum = 0; int prod = 1; for (int i = 1; i <5; i++) { sum += i; if (i==4 &&!cond) { Michael Moossen Departamento de Informática. U.T.F.S.M. 27

40 2.7. MANEJO DE EXCEPCIONES break ; prod *= i; Que no es lo mismo que: for (int i = 1; (i <5 &&!(i==4 &&!cond)); i++) { sum += i; prod *= i; Ya que, en la segunda, no se ejecuta la última suma. La Sentencia continue La sentencia continue permite abortar la iteración actual, pero siguiendo la ejecución en la próxima iteración normalmente. Por ejemplo: int sum = 0; int prod = 1; for (int i = 1; i <5; i++) { sum += i; if (i==2) { continue ; prod *= i; Con lo que se obtiene que, sum = y prod = 1*3* Manejo de Excepciones Las excepciones son la manera que ofrece Java de manejar los errores en tiempo de ejecución. Muchos lenguajes imperativos, cuando tenían un error de esta clase, lo que hacían era detener la ejecución del programa. Las excepciones permiten escribir código que permita manejar ese error y continuar (si se estima conveniente) con la ejecución del programa. El error en ejecución más clásico en Java, es el de desbordamiento. Es decir, el intento de acceso a una posición de un arreglo que no existe. Por ejemplo: public class Desbordamiento { static String mensajes[] = {"Primero", "Segundo", "Tercero"; public static void main(string[] args) { for (int i=0; i<=3; i++) System.out.println(mensajes[i]); System.out.println("Ha finalizado la ejecución"); Este programa tendrá un serio problema cuando intente acceder a mensajes[3], pues no existe dicho valor. Al ejecutarlo dirá lo siguiente (o algo parecido): Primero Segundo 28 Departamento de Informática. U.T.F.S.M. Michael Moossen

41 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA Tercero Exception in thread "main"java.lang.arrayindexoutofboundsexception at Desbordamiento.main(Desbordamiento.java, Compiled Code) Dá un error de ejecución (en esta terminología se diría que se lanzó una excepción) al intentar acceder a dicho valor inexistente. Se ve que, por ahora, el comportamiento del código, es el mismo que en los lenguajes imperativos. Cuando encuentra el error, se detiene la ejecución. Ahora se verá como evitar esto try...catch...finally Existe una estructura que permite capturar excepciones, es decir, reaccionar ante un error de ejecución. De este modo se pueden imprimir mensajes de error a la medida y continuar con la ejecución del programa si se considera que el error no es demasiado grave. Para ver como funciona, se modificará el ejemplo anterior, pero ahora capturando las excepciones: public class PrimerCatch { static String mensajes[] = {"Primero", "Segundo", "Tercero"; public static void main(string[] args) { try { for (int i=0; i<=3; i++) System.out.println(mensajes[i]); catch (ArrayIndexOutOfBoundsException e) { System.out.println("El asunto se ha desbordado"); finally { System.out.println("Ha finalizado la ejecución"); Dentro del bloque que sigue a try, se coloca el código a ejecutar. Es como si se intentara ejecutar dicho código para ver que sucederá. Después de try, se debe colocar al menos un bloque catch o un bloque finally, pudiendo tener ambos e incluso más de un bloque catch. En el bloque finally, se pone el código que se ejecutará siempre, tanto si se lanza la excepción como si no. Su utilidad no es mucha, ya que si se permite continuar con la ejecución del programa, basta con poner el código después del bloque try...catch. En el ejemplo se podría haber puesto lo siguiente: try { for (int i=0; i<=3; i++) System.out.println(mensajes[i]); catch (ArrayIndexOutOfBoundsException e) { System.out.println("El asunto se ha desbordado"); System.out.println("Ha finalizado la ejecución"); Y habría funcionado exactamente igual. Lo importante está en los bloques catch La Clase Exception Cuando se lanza una excepción, en el mundo orientado a objetos, lo que se hace es lanzar una instancia de Exception o de una clase derivada de ella. Normalmente, las clases derivadas no añaden mucha funcionalidad Michael Moossen Departamento de Informática. U.T.F.S.M. 29

42 2.7. MANEJO DE EXCEPCIONES (muchas veces ninguna en absoluto), pero al ser diferentes, permiten distinguir entre los distintos tipos de excepciones. En el programa anterior, por ejemplo, en el bloque catch se captura una excepción del tipo ArrayIndexOutOfBoundsException, ignorando cualquier otro tipo de excepción. Esta clase tiene dos constructores y dos métodos interesantes (tiene más métodos pero sólo se verán estos): Exception e = new Exception(); Crea una excepción sin ningún mensaje específico. Exception e = new Exception(String msg); Crea una excepción con un mensaje que detalla el tipo de excepción. String e.getmessage(); Devuelve el mensaje detallado si existe, o null en caso contrario. void e.printstacktrace(); Escribe por la salida de error estándar, una traza que permite localizar donde se generó el error. Es muy útil para depurar, pero no es conveniente que los usuarios finales vea estos mensajes Capturar Excepciones Ahora ya se tienen los conceptos suficientes como para entender cómo funcionan los catch. Entre paréntesis, como parámetro se pone la declaración de una excepción, es decir, el nombre de una clase derivada de Exception (o la misma Exception) seguido por el nombre de la variable. Si se lanza una excepción, que es la que se desea capturar o una derivada de la misma, se ejecutará el código que contiene el bloque. Así, por ejemplo: catch (Exception e) {... Se ejecutará siempre que se produzca una excepción del tipo que sea, ya que todas las excepciones se derivan de Exception. No es recomendable utilizar algo así ya que se captura cualquier tipo de excepciones sin saber si eso afectará a la ejecución del programa o no. Se pueden colocar varios bloques catch. En ese caso, se comprobará en orden, si la excepción lanzada es la que se desea capturar y si no, pasa a comprobar la siguiente. Eso sí, sólo se ejecuta un bloque catch. En cuanto se captura la excepción, deja de comprobar los demás bloques. Por eso, lo siguiente: catch (Exception e) {... catch (DerivadaDeException e) {... Daría error, ya que el segundo bloque no podría ejecutarse nunca Lanzar Excepciones Bajo ciertas circunstancias puede ser deseable generar o lanzar una excepción para que sea capturada por la sentencia catch correspondiente. Esto es posible mediante la sentencia throw que recibe como parámetro un objeto que implemente la interfaz Throwable, en general, cualquier excepción. Por ejemplo, 30 Departamento de Informática. U.T.F.S.M. Michael Moossen

43 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA try { int i = 200; byte b = (byte )i; if (i!=b) { throw (new Exception("Error de Conversión de Tipos")); catch (Exception E) { System.out.println(E.getMessage()); Aquí, Java no genera una excepción por sí mismo, debido a que la conversión de estos tipos de dato se realiza con el módulo, pero puede ser que en una aplicación real, se necesite hacer este tipo de conversiones, sin perder los valores originales. La única forma de lograr esto es validar manualmente, y eventualmente lanzar una excepción en caso de error Declaración de Excepciones en Métodos Todo método en el que se puede producir uno o más tipos de excepciones (y que no utiliza directamente los bloques try /catch /finally para tratarlos) debe declararlas en el encabezado de la función por medio de la palabra throws. Si un método puede lanzar varias excepciones, se colocan después de throws separadas por comas, como por ejemplo: public void leerarchivo(string fich) throws EOFException, FileNotFoundException {... Se puede declarar sólo una superclase de excepciones para indicar que se pueden lanzar excepciones de cualquiera de sus clases derivadas. El caso anterior sería equivalente a: public void leerarchivo(string fich) throws IOException {... Las excepciones pueden ser lanzadas directamente por leerarchivo() o por alguno de los métodos llamados por él Recursividad La recursividad es una característica presente en casi todos los lenguajes de programación, que trata simplemente de permitir que un método se invoque a sí mismo. Java también lo permite, pero es necesario tener presente ciertos detalles. Primero, a través de la resursividad, se pueden abstraer problemas reales, de forma más clara y sencilla, como se verá en el ejemplo. Sin embargo, implica tiempos de ejecución mucho mayores, debido principalmente a la sobrecarga de guardar las variables locales y parámetros en el stack Números de Fibonacci A continuación se mostrará el ejemplo clásico de los números de Fibonacci. Estos números forman una serie que cumple las siguiente condiciones: F 1 = 1 F 2 = 1 F n = F n 1 + F n 2 Una función que calcule un elemento arbitrario de esta serie, se puede escribir como una función típica usando algún iterador, en este ejemplo se usa for : Michael Moossen Departamento de Informática. U.T.F.S.M. 31

44 2.9. LA CLASE OBJECT long fibonacci(int num) { long sum = 0L; if (num == 1 num == 2) { sum = 1; else { long f1 = 1L; long f2 = 1L; for (int i = 3; i <= num; i++) { sum = f1 + f2; f1 = f2; f2 = sum; return sum; Así, al ejecutar fibonacci(30) se obtendrá como resultado. Esto mismo se puede lograr con un método recursivo: long fibonacci(int num) { long sum = 0L; if (num == 1 num == 2) { sum = 1; else { sum = fibonacci(num-1) + fibonacci(num-2); return sum; donde, se ve más claramente la lógica del problema. A pesar que, en general, los métodos recursivos son más claros, siempre es preferible usar alguna adaptación iterativa del mismo, ya que su desempeño es peor y en métodos largos y complejos, la depuración se vuelve también más compleja. Para fijar una idea de eficiencia, el primer ejemplo da el resultado prácticamente de inmediato para F (aunque incorrecto, debido a la precisión numérica. Ver sección 4.3.3) y la versión recursiva, ya para calcular F 45 demora casi 5 minutos. Sin embargo, pueden haber casos en los que es muy complicado diseñar un algoritmo iterativo para un problema claramente recursivo. En estos casos, se pueden mantener métodos recursivos, siempre y cuando la profundidad de la recursión esté bien acotada. Por ejemplo, no más allá de un par de decenas de niveles La Clase Object Esta clase rara vez se utiliza directamente, pero por ser la clase de la que heredan todas las demás en Java, existen varios métodos que se pueden y, a veces, se deben sobreescribir para lograr que las clases realicen ciertas tareas correctamente. Los más usados, entre otros, son: boolean equals(object O) Comprueba si el objeto dado como parámetro es igual al objeto actual. Esta implementación comprueba 32 Departamento de Informática. U.T.F.S.M. Michael Moossen

45 CAPÍTULO 2. PRINCIPIOS DE PROGRAMACIÓN CON JAVA si ambas referencias son iguales por lo que no resulta muy útil, ya que es equivalente al operador ==. Las distintas clases de Java suelen sobreescribirlo para comprobar si los contenidos de la instancia son los mismos. Por ejemplo, cuando String sobreescribe este método, compara si las cadenas contenidas en ambas instancias son iguales. Es una buena práctica, sobrecargar este operador en las clases propias. String tostring() Devuelve una representación del objeto en formato de cadena. Es llamado automáticamente por Java cuando necesita convertir el objeto en cadena de caracteres. Esta implementación devuelve una cadena que contiene el nombre de la clase del objeto, seguido de una arroba y del código Hash del mismo, lo cual no es muy útil, pero sí lo es, sobreescribirlo para obtener una verdadera representación en String de la clase. Existen otros métodos, pero estos son los más importantes por el momento. Michael Moossen Departamento de Informática. U.T.F.S.M. 33

46 2.9. LA CLASE OBJECT 34 Departamento de Informática. U.T.F.S.M. Michael Moossen

47 Capítulo 3 Programación Orientada a Objetos La tecnología orientada a objetos se define como una metodología de diseño de software, que modela las características de objetos reales o abstractos, por medio del uso de clases y objetos. Hoy en día, la orientación a objetos es fundamental en el desarrollo de software, sin embargo, esta tecnología no es nueva, sus orígenes se remontan a la década de los años sesenta. De hecho, Simula, uno de los lenguajes de programación orientados a objetos más antiguos, fue desarrollado en Taxonomía de Lenguajes Orientados a Objetos Una clasificación para los lenguajes de programación con respecto a la orientación a objetos fue creada por Wegner. Dicha clasificación se constituye de la siguiente forma: Lenguajes Basados en Objetos: Si la sintaxis y semántica del lenguaje soportan las características de objetos. Por ejemplo, Turbo Pascal 5 (por el soporte de módulos). Lenguajes Basados en Clases: Si un lenguaje está basado en objetos y además soporta clases, se dice que esta basado en clases. Por ejemplo, Visual Basic 5 (por el soporte de módulos de clase, pero no da soporte a herencia, por ejemplo). Lenguajes Orientados a Objetos: Si un lenguaje de programación soporta objetos, clases y además permite la jerarquía de dichas clases, entonces se dice que es un lenguaje de programación orientado a objetos. Por ejemplo, Java. Esta clasificación es la que prevalece actualmente. Aquí se establece, claramente, que para que un lenguaje de programación sea considerado orientado a objetos, este debe soportar la creación de clases y la herencia. Java es actualmente uno de los lenguajes de programación que cumple perfectamente con los requisitos de la orientación a objetos, Delphi, Simula, Smalltalk, son también considerados lenguajes orientados a objetos. Java puede ser visto como el resultado de una mezcla de C++ y Smalltalk, tiene la sintaxis de C++, lo que lo hace relativamente fácil de aprender, si se está familiarizado con C++.Sin embargo, una de las características que lo hace más robusto que C++, es que no tiene punteros, que son causa común de errores en el desarrollo de aplicaciones con este lenguaje. Como en el caso de Smalltalk, Java tiene recolección de basura, una capacidad que libera al programador de definir funciones de reservación y liberación de estructuras en memoria Ventajas de la Tecnología Orientada a Objetos Flexibilidad: Se parte del hecho de que mediante la definición de clases se establecen módulos independientes, a partir de los cuales se pueden definir nuevas clases, entonces se puede pensar en estos módulos como bloques con los cuales se puede construir diferentes programas. 35

48 3.3. DESVENTAJAS DE LA TECNOLOGÍA ORIENTADA A OBJETOS Reusabilidad: Una vez que se ha definido a la entidad Persona para utilizarla en una aplicación de negocios, por mencionar un ejemplo, y se desea construir a continuación una aplicación, por ejemplo de deportes, en donde se requiere definir a la misma entidad Persona, no es deseable volver a escribir la definición para la entidad Persona. Por medio de la reusabilidad se puede utilizar una clase definida previamente en las aplicaciones que sea conveniente. Es claro que la flexibilidad con la que se definió la clase va a ser fundamental para su reutilización. Mantenibilidad: Las clases que conforman una aplicación, vistas como módulos independientes entre sí, son fáciles de mantener sin afectar a los demás componentes de la aplicación. Extensibilidad: Gracias a la modularidad y a la herencia, una aplicación diseñada bajo el paradigma de la orientación a objetos, puede ser fácilmente extensible para cubrir necesidades de crecimiento de la aplicación Desventajas de la Tecnología Orientada a Objetos A pesar de que las ventajas de la programación orientada a objetos superan a las limitaciones de la misma, se pueden encontrar algunas características no deseables en esta. Limitaciones para el Programador: No obstante que la tecnología orientada a objetos no es nueva, un gran porcentaje de programadores no están familiarizados con los conceptos de dicha tecnología. En otras palabras, la lógica de la programación estructurada sigue siendo predominante en la mayoría de los desarrolladores de software. Después de haber revisado de forma breve los principios de la programación orientada a objetos, es claro que en esta se requiere una lógica de pensamiento totalmente diferente a la lógica comúnmente utilizada para la programación estructurada. Tamaño Excesivo en las Aplicaciones Resultantes: La gran mayoría de los computadores actuales cuentan con capacidades tanto de almacenamiento como de memoria lo suficientemente buena como para ejecutar la mayoría de las aplicaciones que puedan desarrollarse con la tecnología orientada a objetos. Sin embargo, existen casos en los que lo anterior no se cumple. Una de las desventajas de la programación orientada a objetos es que cuando se heredan clases a partir de clases existentes, se heredan de forma implícita todos los miembros de dicha clase aún cuando no todos se necesiten, lo que produce aplicaciones muy grandes que no siempre encajan en los sistemas disponibles. Velocidad de Ejecución: Esto se relaciona, en cierto modo, con el punto anterior, una aplicación innecesariamente pesada, en muchas ocasiones, es más lenta de ejecutar que una aplicación formada únicamente por los módulos necesarios Características de la Tecnología Orientada a Objetos Abstracción La abstracción es el proceso en el cual se separan las propiedades más importantes de un objeto, de las que no lo son. Es decir, por medio de la abstracción se definen las características esenciales de un objeto del mundo real, los atributos y comportamientos que lo definen como tal, para después modelarlo en un objeto de software. En el proceso de abstracción no debe preocupar la implementación de cada método o atributo, solamente se debe definir en forma general. Por ejemplo, se puede suponer que se desea escribir un programa para representar el sistema solar. Por medio de la abstracción, se puede ver a este sistema como un conjunto de objetos, algunos de estos objetos son los planetas, que tienen un estado, dado por sus características físicas, como: tamaño, masa, entre otras. Estos objetos tendrán también comportamientos: translación y rotación, pueden mencionarse como los más evidentes. Visualizar las entidades que se desean trasladar a programas, en términos abstractos, resulta de gran utilidad para 36 Departamento de Informática. U.T.F.S.M. Michael Moossen

49 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS un diseño óptimo de software, ya que permite comprender más fácilmente la programación requerida. En la tecnología orientada a objetos la herramienta principal para soportar la abstracción es la clase. Se puede definir a una clase como una descripción genérica de un grupo de objetos que comparten características comunes. Dichas características son especificadas en sus atributos y comportamientos Modularidad Dentro de la programación orientada a objetos, la modularidad juega un papel importante. Una vez que se ha representado una situación del mundo real en un programa, se tiene regularmente como resultado, un conjunto de objetos de software que constituyen la aplicación. La modularidad, permite modificar las características de la clase que definen a un objeto, de forma independiente de las demás clases en la aplicación. En otras palabras, si la aplicación puede dividirse en módulos separados, normalmente clases, y estos módulos pueden compilarse y modificarse sin afectar a los demás, entonces dicha aplicación ha sido implementada en un lenguaje de programación que soporta la modularidad. La tecnología orientada a objetos brinda esta propiedad para hacer uso de ella en el proceso de desarrollo de software Encapsulamiento También referido como ocultamiento de la información, el encapsulamiento es la propiedad de la orientación a objetos que permite asegurar que la información de un objeto sea desconocida para los demás objetos de la aplicación. Es muy frecuente referirse a los objetos de software como cajas negras, lo que se debe principalmente a que no se necesita, dentro de la programación orientada a objetos, saber como está instrumentado un objeto para que interactúe con los demás objetos. Generalmente, una clase se define en dos partes, una interfaz por medio de la cual los objetos que son instanciados de la misma interactúan con los demás objetos en la aplicación, y la implementación de los miembros de dicha clase (métodos y atributos). Se desea realizar una aplicación en donde deben interactuar el objeto Porsche, instancia de la clase Automóvil y el objeto Juan Pérez, instancia de la clase Persona. Ambos objetos pueden ser vistos como cajas negras, las cuales se comunican por medio de una interfaz. El objeto Porsche no sabe como está implementado el objeto Juan Pérez y viceversa. Concretamente, el encapsulamiento permite a un objeto ocultar información al mundo exterior, o bien restringir el acceso a la misma. Una aplicación orientada a objetos está constituída, como se mencionó anteriormente, por módulos. Estos módulos se implementan mediante clases, las cuales representan, generalmente, abstracciones de objetos del mundo real. Es por medio del encapsulamiento que se pueden definir los atributos y los métodos de una clase, para que los objetos que se instancian de esta trabajen como unidades independientes de los demás objetos con los que interactúan. En otras palabras, con el encapsulamiento se gana en modularidad, y además se protege a los objetos de ser manipulados de forma inadecuada por objetos externos Jerarquía Una vez que se ha definido una clase, por ejemplo la clase Persona, con sus atributos y métodos, tal vez se necesite definir una nueva clase específica para los Fútbolistas. Obviamente, esta nueva clase compartirá elementos en común con la clase Persona, es decir, la clase Fútbolista será un subconjunto de la clase Persona. La tecnología orientada a objetos permite definir jerarquías entre clases y jerarquías entre objetos. Las dos jerarquías más importantes que existen, son la jerarquía es un que precisa la generalización y especialización entre clases, y la jerarquía es parte de en la que se delimita la agregación de objetos. Comúnmente, a la jerarquía es un se le conoce como herencia. La herencia simple es la propiedad que permite definir una clase nueva en términos de una clase ya existente. Regresando al ejemplo, si se puede decir que un Fútbolista es una Persona, entonces se puede definir a la clase Fútbolista a partir de la clase Persona. Existirán casos en los cuales se necesite definir una clase a partir de dos o más clases preexistentes, en este caso se hablará de herencia múltiple. Por ejemplo, un Gato es un Mamífero y a la vez es un Felino. Java no permite herencia múltiple, pero provee interfaces que pueden ser usadas para emularla. Utilizando herencia, se puede crear una clase general que defina los rasgos comunes de un conjunto de términos Michael Moossen Departamento de Informática. U.T.F.S.M. 37

50 3.4. CARACTERÍSTICAS DE LA TECNOLOGÍA ORIENTADA A OBJETOS relacionados. Esta clase, puede entonces ser heredada por otras clases más específicas, añadiendo cada una sólo aquellos atributos o métodos que sean particulares a ellas, pero conservando las propiedades generales determinadas por la clase general de la que se hereda. En Java, la clase de la que se hereda, se denomina superclase y la clase heredera subclase y esta herencia se transmite a través de la palabra reservada extends. También se dice que una subclase es una versión especializada de su superclase. Esta hereda todas las variables y métodos definidos por su superclase. Luego, agregará sus propios miembros y/o podrá redefinir las variables y métodos heredados. Todas las clases de Java creadas por el programador tienen una superclase. Cuando no se indica explícitamente una superclase con la palabra extends, la clase deriva de Object, que es la clase raíz de toda la jerarquía de clases de Java. Como consecuencia, todas las clases tienen algunos métodos que han heredado de Object. Ejemplo básico de herencia, usando la clase Persona definida anteriormente: public class EjemploHerencia { public static void main(string[] args) { Fútbolista JuanPerez = new Fútbolista(); JuanPerez.edad= new Integer(35); JuanPerez.sexo= "Masculino"; JuanPerez.nombre= "Juan Pérez"; JuanPerez.equipo= "ABC"; JuanPerez.posicion= new Integer(12); JuanPerez.jugar(); JuanPerez.imprimir(); class Fútbolista extends Persona { String equipo; Integer posicion; void jugar() { estado= "Jugando"; En cuanto a la jerarquía de agregación, también conocida como inclusión, se puede decir que se trata del agrupamiento lógico de objetos relacionados entre sí dentro de una clase. Suponga que se quiere definir la clase Automóvil, pero además se ha definido ya la clase Volante. Si se puede expresar la oración, un Volante es parte de un Automóvil, entonces se puede instanciar un objeto de la clase Volante, para definir a la clase Automóvil. En este caso, se dice que Automóvil es una agregación y Volante es un agregado de Automóvil. public class EjemploAgregacion { public static void main(string[] args) { Automóvil Porsche = new Automóvil(); Porsche.color= "Rojo"; Porsche.miVolante= new Volante(); Porsche.miVolante.color= "Negro"; Porsche.miVolante.diametro= new Double(30); Porsche.virar(); class Automóvil { String color; 38 Departamento de Informática. U.T.F.S.M. Michael Moossen

51 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS Volante mivolante; void virar() { mivolante.virar(); class Volante { String color; Double diametro; void virar() { System.out.println("Virando volante de color "+ color) System.out.println("y diámetro de "+ diametro+ "cm."); Polimorfismo Muchas veces es necesario que un mismo comportamiento o acción se realice de diferentes maneras, por ejemplo, suponga que se desea implementar a la clase Mamífero, suponga también que uno de los métodos que se desea implementar para esta clase, es el que permita a tales mamíferos desplazarse de forma natural. Entonces se tendrá que para algunos mamíferos el desplazamiento se realizará por medio de caminar, como es el caso de las personas, para otros el desplazamiento natural será nadar, como el caso de los delfines mientras que para otros, el desplazamiento se logra por medio de volar, como sucede con los murciélagos. En otras palabras, un mismo comportamiento, en este caso el desplazamiento, puede tomar diferentes formas. Dentro de la programación orientada a objetos puede modelarse esta situación del mundo real, en objetos de software, gracias al polimorfismo. El polimorfismo es la propiedad por la cual una entidad puede tomar diferentes formas. Generalmente, esta entidad es una clase, y la forma en que se consigue que tome diferentes formas es por medio de nombrar a los métodos de dicha clase con un mismo nombre pero con diferentes implementaciones. En Java, esto se logra a través de la: Sobrecarga de Métodos Esto permite definir dos o más métodos dentro de la misma clase con el mismo nombre, siempre que la declaración de sus parámetros sea diferente. En este caso, se dice que el método está sobrecargado y el proceso de definir un método así, se conoce como sobrecarga del método. La sobrecarga de métodos es una de las maneras en que Java implementa el polimorfismo. Cuando se llama a un método sobrecargado, el compilador actúa justo sobre la versión cuyo tipo de parámetros coincida con los de la llamada. Así, se podría definir la siguiente clase Desplazamiento que permitirá desplazarse a distintos objetos: class Desplazamiento { void desplazar(ave A) { System.out.println("Las aves vuelan"); void desplazar(persona P) { System.out.println("Las personas caminan");... Michael Moossen Departamento de Informática. U.T.F.S.M. 39

52 3.5. CLASES NOTA: No se debe confundir la sobrecarga de los métodos con la redefinición de estos. En el primer caso, sobrecargar un método consiste en definir dos o más métodos dentro de la misma clase con el mismo nombre, mientras que redefinir (override ) un método, es darle una nueva definición. En este caso el método debe tener exactamente los mismos argumentos en tipo y número que el método redefinido y sólo puede ocurrir en el caso de una clase heredada. No existe una regla exacta para saber si un método se debe sobrecargar o no. Realmente, la idea es aprovechar las ventajas que ofrece esta forma de polimorfismo, así que lo normal es sobrecargar aquellos métodos que estén intrínsecamente relacionados, como es el caso del ejemplo anterior, pero no se debe confundir. Por ejemplo, el método sqrt(), aunque se llama igual, calcula de forma totalmente diferente la raíz cuadrada de un número entero que la de uno en punto flotante. Aquí, aunque se aplique sobrecarga al método, realmente no se respetaría el propósito para el que se creó el polimorfismo Características Adicionales de los Lenguajes Orientados a Objetos Además de las características que se mencionaron anteriormente como esenciales de los lenguajes de programación orientados a objetos, es deseable que estos cumplan también con las siguientes: Tipificación Fuerte: Esto es, que durante la fase de diseño e implementación, se declare que tipo de datos soportará cada variable. Manejo de Excepciones: Dentro de la misma definición del lenguaje se deberá establecer la forma de detectar y manipular excepciones(errores) que puedan surgir durante la ejecución de un programa. Generalidad: Se refiere principalmente a que las clases se definan lo más generalizadas posible, para que sean fácilmente reusables. Para generar este tipo de clases, normalmente se definen parámetros formales, que son instanciados por parámetros reales. Multitarea: Es conveniente que el lenguaje permita la creación de procesos que se ejecuten de forma simultánea independientemente del sistema operativo. Persistencia: Los objetos deben poder permanecer, si así se desea, después de la ejecución de un programa. Datos Compartidos: Los objetos pueden necesitar referirse a la misma localidad de memoria (memoria compartida) o bien comunicarse mediante mensajes Clases Clase: Es un molde o bien prototipo en donde se definen los atributos (variables) y las acciones (métodos) comunes de una entidad. Este es el paradigma que propone la programación orientada a objetos, la abstracción de los elementos que constituyen a un objeto del mundo físico, esto es, atributos y comportamiento, y la representación de estos elementos por medio de objetos de software, formados por variables y métodos, que permitan la manipulación de tales variables. Cabe mencionar, que además de representar los objetos físicos del mundo real por medio de objetos, también se pueden modelar objetos abstractos, por ejemplo las acciones de un usuario al interactuar con la máquina. Se puede definir a una clase como una descripción genérica de un grupo de objetos que comparten características comunes. Dichas características son especificadas en sus atributos y comportamientos. En otras palabras, una clase es un molde o modelo en donde se especifican las características que definen a un objeto de manera general. A partir de una clase se pueden definir objetos particulares. En programación orientada a objetos, se dice que un objeto es una instancia de la clase, es decir, un objeto es un caso particular del conjunto de objetos que comparten características similares, definidas en la clase. Como ejemplo se define la clase Persona, esta clase tendrá ciertos atributos, por ejemplo edad, sexo y nombre 40 Departamento de Informática. U.T.F.S.M. Michael Moossen

53 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS entre otros. Las acciones o comportamientos que se pueden definir para esta clase son, por ejemplo, respirar, caminar, comer, etcétera. Estas son, en este caso, las características que definen a la clase Persona. Un objeto instanciado de esta clase podría ser Juan Pérez, de sexo masculino y de 35 años de edad, quién se encuentra caminando. Estos atributos son conocidos formalmente en programación orientada a objetos como variables de instancia por que contienen el estado de un objeto particular en un momento dado, en este caso Juan Pérez. Así mismo, los comportamientos son llamados métodos de instancia, porque muestran el comportamiento de un objeto particular de la clase. Esto se podría escribir en Java como: public class EjemploClase { public static void main(string[] args) { Persona JuanPerez = new Persona(); JuanPerez.edad= new Integer(35); JuanPerez.sexo= "Masculino"; JuanPerez.nombre= "Juan Pérez"; JuanPerez.caminar(); JuanPerez.imprimir(); class Persona { Integer edad; String sexo; // Masculino o Femenino String nombre; String estado; void comer() { estado= "Comiendo"; void caminar() { estado= "Caminando"; void imprimir() { System.out.println("Este es "+nombre+ "una persona de sexo "+ sexo+ ","); System.out.println("de "+ edad+ "años y que ahora está "+ estado); Hay que tener cuidado de no confundir un objeto con una clase, una gelatina de frutilla y una gelatina de limón son objetos de la misma clase (o del mismo molde), pero con atributos (o sabores) diferentes Atributos Son los datos y variables, que al definirse dentro de la clase se llaman variables instancia de la clase. Se llaman así porque cada instancia de la clase, es decir, cada objeto particular que se cree, contiene su propia copia de estas variables (generalmente) Métodos Son las funciones de la clase que actuan sobre las variables de la instancia de ella y definen su comportamiento. Otra de las ventajas de Java es la flexibilidad y potencia que les proporciona. Michael Moossen Departamento de Informática. U.T.F.S.M. 41

54 3.5. CLASES La expresión general de la declaración de un método es la siguiente: acceso tipo nombremetodo(parámetros) { //cuerpo del método donde acceso indica el tipo de protección asignado a este método (public, protected o private ), tipo es el valor que devuelve el método, puede ser un tipo primitivo o un tipo de clase o void en caso de que no devuelva ninguno. Los parámetros, cuando los hay, se especifican por la pareja tipo - identificador. Esta línea es lo que se conoce como el header, signatura o firma del método. Java no soporta parámetros opcionales. Salvo los métodos estáticos(ver sección 3.7.1) o de clase, se aplican siempre a un objeto de la clase por medio del operador punto. Dicho objeto es su argumento implícito. Los métodos tienen visibilidad directa de las variables miembro del objeto que es su argumento implícito, es decir, pueden acceder a ellas sin cualificarlas con un nombre de objeto y el operador punto. El valor de retorno puede ser un valor de un tipo primitivo o una referencia. En cualquier caso, no puede haber más que un único valor de retorno (que puede ser un objeto o un arreglo). Se puede devolver también una referencia a un objeto por medio de un nombre de interfaz. El objeto devuelto debe pertenecer a una clase que implemente esa interfaz. Se puede devolver como valor de retorno un objeto de la misma clase que el método o de una subclase, pero nunca de una superclase. Los métodos pueden definir variables locales. Su visibilidad llega desde la definición hasta el final del bloque en el que han sido definidas. No hace falta inicializarlas en el punto que se definen, sin embargo, el compilador no permite utilizarlas sin haberlas inicializado. A diferencia de las variables miembro, las variables locales no se inicializan por defecto. Si en el header del método se incluye la palabra native no hay que incluir el código o implementación del método. Este código deberá estar en una librería dinámica (DLL). Estas librerías son archivos de funciones compiladas normalmente en lenguajes distintos a Java. Esta es la forma de poder utilizar conjuntamente funciones realizadas en otros lenguajes desde código escrito en Java. Paso de Parámetros a Métodos En Java los parámetros de los tipos primitivos se pasan siempre por valor. El método recibe una copia del parámetro actual. Si se modifica esta copia, el parámetro original que se incluyó en la llamada no es modificado. La forma de modificar dentro de un método una variable de un tipo primitivo, es incluirla como variable miembro en una clase y pasar como parámetro una referencia a un objeto de dicha clase. Las referencias se pasan también por valor, pero a través de ellas se pueden modificar los objetos referenciados. En Java no se pueden pasar métodos como parámetros a otros métodos (en C/C++ se pueden pasar punteros a función como parámetros). Lo que se puede hacer en Java es pasar una referencia a un objeto y dentro de la función utilizar los métodos de ese objeto. Si un método devuelve this (ver sección 3.6.2), es decir, un objeto de la clase, o una referencia a otro objeto, ese objeto puede concatenarse con otra llamada a otro método de la misma o de diferente clase y así sucesivamente. En este caso aparecerán varios métodos en la misma sentencia unidos por el operador punto (.). Por ejemplo, la siguiente sentencia: String s = "Curso Java ".trim().substring(1,4); es correcta porque trim() devuelve String y substring() también. Así, se evita escribir: String s1 = "Curso Java "; String s2 = s1.trim(); String s = s2.substring(1,4); La secuencia de ejecución, cuando se concatenan varias llamadas a métodos, es de izquierda a derecha. 42 Departamento de Informática. U.T.F.S.M. Michael Moossen

55 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS El Operador return Ya se comentó anteriormente que la llamada a un método podría acabar con la devolución de un valor por parte de esta. Este valor puede ser de un tipo primitivo, o una referencia a un objeto. En cualquier caso, de la devolución de este valor se encarga el operador return. Ejemplo: return retvalue; Constructores Un punto clave de la Programación Orientada a Objetos(POO) es el evitar información incorrecta por no haber sido correctamente inicializadas las variables. Java no permite que haya variables miembro que no estén inicializadas. Asimismo, si se crean varias instancias de la misma clase, puede acabar resultando muy pesado el tener que inicializar todas las variables miembro para cada instancia. Por todo ello, Java ofrece el uso de un método especial llamado el constructor de la clase. Un constructor es un método que se llama automáticamente, cada vez que se crea un objeto de una clase y su principal misión es la de reservar espacio de memoria al objeto y la de inicializar todas las variables miembro. Aunque se ha dicho que el constructor es como un método, este tiene características específicas como que se tiene que llamar igual que la clase y que no devuelve ningún valor (ni siquiera void ). Esto se debe a que el tipo que devuelve el constructor es su argumento implícito, es decir, la clase en sí misma. Pero, sí se le puede dotar de un parámetro de restricción, aunque si se declara como private ninguna otra clase podrá crear una instancia de esta clase. Un constructor puede llamar a otro constructor previamente definido en la misma clase, por medio de la palabra this. En este contexto, la palabra this sólo puede aparecer en la primera sentencia de un constructor. El constructor de una subclase puede llamar al constructor de su superclase por medio de la palabra super, seguida de los parámetros apropiados entre paréntesis. De esta forma, un constructor sólo tiene que inicializar por sí mismo las variables no heredadas. Sobrecarga de Constructores Es aquí donde realmente se aprecian los beneficios del polimorfismo. El constructor de una clase es el que inicializa los valores que el programador crea conveniente cuando esta se instancia. Pues bien, sobrecargando el constructor, se consigue dotar a la clase de flexibilidad. Por ejemplo, como mínimo, se debería tener en cuenta que podría no pasársele parámetros al constructor, cuando este lo espera, debido a un fallo en alguna otra parte de la aplicación (se refiere a cualquier otra clase que llame a esta). Es por ello que siempre es recomendable definir al menos dos constructores: el específico de la aplicación que se esté diseñando y el estándar. El siguiente ejemplo lo dejará mucho más claro: class Box{ double width; double height; double depth; // El siguiente es el constructor específico Box(double w, double h, double d) { width= w; height= h; depth= d; // pero podría ser que no le llegaran parámetros // por fallar la otra clase (método) que lo invoque Box() { width= height= depth= -1; //-1 indica volumen no existente Michael Moossen Departamento de Informática. U.T.F.S.M. 43

56 3.5. CLASES // e incluso se puede pensar que se quiere construir un cubo, // entonces, por qué introducir 3 valores? ;) Box(double valor) { width= height= depth= valor; double volume() { return width* height* depth; Como se ve, de cara a flexibilizar una clase es fundamental el polimorfismo a la hora de implementar los constructores Destructores Aunque los objetos son dinámicamente asignados mediante el operador new (ver sección 3.6.1), en Java no hay destructores específicos como en C++. El sistema se ocupa automáticamente de liberar la memoria de los objetos que ya han perdido la referencia, esto es, objetos que ya no tienen ningún nombre que permita acceder a ellos, por ejemplo, por haber llegado al final del bloque en el que habían sido definidos, porque a la referencia se le ha asignado el valor null o porque a la referencia se le ha asignado la dirección de otro objeto. A esta característica de Java se le llama garbage collection (recolección de basura). En Java, es normal que varias variables de tipo referencia apunten al mismo objeto. Java lleva internamente un contador de cuántas referencias hay sobre cada objeto. El objeto podrá ser borrado cuando el número de referencias sea cero. Nunca se sabe cuando se activará el recolector de basura (depende mucho de la implementación del sistema de ejecución de Java) pero mientras no falte memoria, es muy probable que nunca se active. Si se quiere llamar explícitamente al garbage collector, se puede usar el método System.gc(), aunque esto sólo es tomado por la JVM como una sugerencia. El Método finalize() Algunas veces un objeto necesitará realizar algunas acciones antes de que sea destruido y que no sean las propias de liberalización de memoria, sino de liberar recursos como el manejador de un archivo, una conexión de red o memoria reservada para funciones nativas. Hay que tener en cuenta, que el recolector de basura sólo libera la memoria reservada con new. Si por ejemplo, se ha reservado memoria con funciones nativas en C (por ejemplo, utilizando la función malloc()), esta memoria hay que liberarla explícitamente utilizando el método finalize(). Un finalizador es un método, sin valor de retorno (void ), ni argumentos y que siempre se llama finalize(). Los finalizadores se llaman de modo automático siempre que hayan sido definidos por el programador de la clase. Para realizar su tarea correctamente, un finalizador debería terminar siempre llamando al finalizador de su superclase. Es importante saber, que lo único seguro, es que el método finalize() se llama justo antes del recolector de basura pero no hay manera de saber cuando, o incluso si es llamado, por lo que lo más conveniente es que el programador realice de manera explícita la liberación de recursos críticos mediante otros métodos que el mismo llame. El método System.runFinalization() sugiere a la JVM, que ejecute los finalizadores de los objetos pendientes, los que han perdido su referencia. 44 Departamento de Informática. U.T.F.S.M. Michael Moossen

57 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS 3.6. Objetos Objeto: Unidad de software conformada por atributos y métodos específicos. El objeto, es el concepto principal sobre el cual se fundamenta la tecnología orientada a objetos. Un objeto puede ser visto como una entidad que posee atributos y efectúa acciones. En el mundo real, podemos encontrar cientos de ejemplos que cumplen con esta definición, algunos de ellos son: una bicicleta, un automóvil, una persona, una computadora, etcétera. Estos objetos son casos particulares de entidades llamadas clases, en donde se definen las características comunes de tales objetos. Un caso particular es el ejemplo del objeto automóvil, se pueden mencionar como atributos de este: el modelo, el color, la marca, el número de placas, entre otros. Algunas acciones que es capaz de realizar un automóvil son: ir en reversa, virar, frenar, acelerar y cambiar la velocidad. Este objeto automóvil, es una instancia particular de la entidad automóvil. En términos de la programación orientada a objetos, se dice que todo objeto tiene un estado (atributos) y un comportamiento (acciones). La programación orientada a objetos permite modelar estos objetos del mundo real en objetos de software de forma eficaz. Un objeto de software mantiene sus atributos o estado en variables e implementa las acciones o comportamientos por medio de métodos o funciones. Suponga que se quiere desarrollar una aplicación que simule un vehículo. Se tendría entonces un objeto vehículo constítuido por variables en las cuales se podría almacenar número de serie, color, velocidad actual, etcétera. Junto a esto, se tendría un conjunto de funciones que implementarán las acciones para frenar, virar, cambiar la velocidad, etcétera Operador new Para obtener una instancia o ejemplar de la clase se siguen dos pasos: Se declara una variable del tipo de la clase. Esta será la que posea la referencia del objeto, pero en este punto, el valor de la variable es null y cualquier intento de acceder a ella producirá un error. Por ejemplo, String Doce; Doce = String.valueOf(12); Obtener una copia física, real de la clase, es decir, crear el objeto. Para ello, se emplea el operador new. Dicho operador asigna memoria de manera dinámica, esto es, en tiempo de ejecución, al objeto y devuelve una referencia de él, que se guardará en la variable que se crea en el paso anterior. Por ejemplo, String Doce = new String("12"); El espacio de memoria que se reserva, se calcula a partir de la descripción de la clase y luego se llama al constructor de esta o a un constructor por defecto de Java si la clase carece de él Objeto this Normalmente, dentro del cuerpo de un método de un objeto, se puede acceder directamente a las variables miembros del objeto. Sin embargo, algunas veces no se querrá tener ambigüedad sobre el nombre de la variable miembro y uno de los argumentos del método que tengan el mismo nombre. Por ejemplo, el siguiente constructor de la clase HSBColor inicializa alguna variable miembro de un objeto de acuerdo a los argumentos pasados al constructor. Cada argumento del constructor tiene el mismo nombre que la variable del objeto cuyo valor contiene el argumento. class HSBColor { int luminosidad; int saturacion; int brillo; HSBColor (int luminosidad, int saturacion, int brillo) { this.luminosidad= luminosidad; Michael Moossen Departamento de Informática. U.T.F.S.M. 45

58 3.6. OBJETOS this.saturacion= saturacion; this.brillo= brillo; Se debe utilizar this en este constructor para evitar la ambigüedad entre el argumento luminosidad y la variable miembro luminosidad (y así con el resto de los argumentos). Escribir luminosidad = luminosidad; no tendría sentido. Los nombres de argumentos tienen mayor precedencia y ocultan a los nombres de las variables miembro con el mismo nombre. Para referirse a la variable miembro, se debe hacer explícitamente a través del objeto actual this. También se puede utilizar this para llamar a uno de los métodos del objeto actual. Esto sólo es necesario si existe alguna ambigüedad con el nombre del método y se utiliza para intentar hacer el código más claro. El objeto this, también puede ser usado como valor de retorno de un método para, por ejemplo, permitir la concatenación de llamadas a métodos. Ejemplo: class A { A hacer() {... return this ; this se utiliza también para llamar a un constructor desde otro (ver sección 3.5.3) Objeto super Hay veces que se quiere crear una superclase que mantenga el detalle de su implementación para ella sola, es decir, se quiere que sus datos miembros sean privados. En este caso, no habría manera de que una subclase accediera o inicializara directamente estas variables por su cuenta. Puesto que la encapsulación es un atributo primordial de la POO, Java ha tenido en cuenta una manera de proporcionar una solución a este problema. Cuando quiera que sea que una clase necesite referirse a su superclase inmediata, lo podrá hacer mediante el empleo de super. super, en este contexto, se puede emplear de dos formas: Llamar al constructor de su superclase. class B extends A { B() { super (); // llama al constructor de A Acceder a un miembro de la superclase que haya sido escondido por un miembro de una subclase. class A { int a; class B extends A { int a; void ejecutar() { a= 2; // Modifica el atributo a propio super.a= 3; // Modifica el atributo a del padre 46 Departamento de Informática. U.T.F.S.M. Michael Moossen

59 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS Llamada al Constructor de su Superclase Ya se comentó que un constructor de una clase puede llamar por medio de la palabra this a otro constructor previamente definido en la misma clase. En este contexto, la palabra this sólo puede aparecer en la primera sentencia de un constructor. Como ejemplo de esto, se cita el ejemplo de la sobrecarga de constructores de la sección 3.5.3, en donde el constructor sin parámetros se podría reemplazar por una llamada al constructor de un parámetro, como sigue: Box() { this (-1); // llama al constructor de un parámetro. De forma análoga, el constructor de una clase derivada puede llamar al constructor de su superclase por medio de super(). Entre paréntesis van los argumentos apropiados para uno de los constructores de la superclase. De esta forma, un constructor sólo tiene que inicializar directamente las variables no heredadas. La llamada al constructor de la superclase debe ser la primera sentencia del constructor, excepto si se llama a otro constructor de la misma clase con this(). Si el programador no la incluye, Java incluye automáticamentre una llamada al constructor por defecto de la superclase, super(). Esta llamada en cadena a los constructores de las superclases llega hasta el origen de la jerarquía de clases, esto es, al constructor de Object. Acceso a los Miembros de su Superclase En este caso, super actúa algo así como this, excepto que siempre se refiere a la superclase de la subclase en la que se usa. En este caso, su empleo se justifica para solucionar el problema de que la clase derivada pise un miembro (variable o método) de su superclase por llamarlo de la misma manera. Por ejemplo: class Madre { int i; class Hija extends Madre { int i; //esta variable esconde la de su superclase Hija(int a, int b) { super.i= a; //es la variable de la clase Madre i= b; //variable de Hija void show() { imprimir("i en Madre: "+ super.i); imprimir("i en Hija: "+ i); class UsoDeSuper { public static void main(string args[]) { Hija subob = new Hija(1,2); subob.show(); //muestra: i en Madre: 1 //i en Hija: 2 Ejemplo del uso de super (): Michael Moossen Departamento de Informática. U.T.F.S.M. 47

60 3.7. MODIFICADORES DE ATRIBUTOS Y MÉTODOS class Madre { int i,j; Madre(int a, int b) { i=a; j=b; void show() { imprimir("i y j: "+ i+ + j); class Hija extends Madre { int k; Hija(int a, int b, int c) { super (a,b); k=c; void show() { // llamada al método show() de Madre // esto es la manera de diferenciar // entre un método de una superclase // y uno redefinido por su hija super.show(); //i y j: 1 2 imprimir("k: "+ k); //k: 3 class Redefine { public static void main(string args[]) { Hija subob = new Hija(1,2,3); subob.show();//redefine show() de Madre Por último, para concluir este tema, queda aclarar cuando se llaman los construtores si se tiene una jerarquía de clases derivadas. La respuesta, es que los constructores se llaman en orden de derivación, desde la superclase hacia la subclase Modificadores de Atributos y Métodos El Modificador static Hay veces que se desea definir un miembro de una clase para ser usado independientemente de cualquier objeto de esa clase. Normalmente, a un miembro de una clase se accede sólo si se ha instanciado un objeto de dicha clase. No obstante, es posible crear un miembro que pueda ser usado por sí mismo, sin necesidad de referenciar a una instancia específica. Para crear tales tipos de miembros se emplea el modificador static. Cuando un miembro se declara con esta palabra reservada, se puede acceder a él antes de que cualquier objeto de su clase sea creado, y sin referenciar a ningún objeto. Se pueden declarar estáticos tanto métodos como variables. 48 Departamento de Informática. U.T.F.S.M. Michael Moossen

61 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS El ejemplo más claro de un miembro estático es el main(). Se declara de esta manera porque se debe llamar antes de que cualquier objeto sea declarado. Atributos Estáticos Los atributos de instancia declarados como estáticos (también llamados de clase ) son, esencialmente, variables globales. Cuando se crean objetos específicos de esa clase no se hace ninguna copia de los atributos estáticos. Lo que ocurre, es que todas las instancias de esa clase comparten el mismo atributo. Estos atributos tienen sentido, principalmente, cuando varias instancias de la misma clase necesitan llevar el control o estado del valor de un dato. También se podría utilizar para definir constantes. Para llamar a este tipo de atributos se suele utilizar el nombre de la clase (no es imprescindible, pues se puede utilizar también el nombre de cualquier objeto de esa clase), porque de esta forma queda más claro, seguida de un. y el atributo. Por ejemplo: class Constantes { static double PI = ; class UsoStatic { public static void main(string args[]) { System.out.println("Pi := "+ Constantes.PI); Los atributos miembro estáticos se crean en el momento en que pueden ser necesarios: cuando se va a crear el primer objeto de la clase, en cuanto se llama a un método estático o en cuanto se utiliza un atributo estático de dicha clase. Lo importante es que las atributos miembro estáticos se inicializan siempre antes que cualquier objeto de la clase. Métodos Estáticos También llamados de clase, pueden recibir objetos de su clase como argumentos explícitos, pero no tienen argumento implícito ni pueden utilizar la referencia this. Para llamar a estos métodos se suele emplear el nombre de la clase, en vez del nombre de un objeto de la clase. Los métodos y variables de clase son lo más parecido que Java tiene a las funciones y variables globales de C/C++. Las restricciones que tiene un método estático son: 1. Sólo pueden llamar otro método estático 2. Sólo deben acceder a datos estático 3. No se pueden referir a this o super de ninguna manera Si se necesita hacer algún tipo de computación para inicializar los atributos estáticos, se puede declarar también un bloque estático, el cual se ejecutará sólo una vez, cuando se carga. class Estatica { static int a= 3; static int b; static void show(int x) { System.out.println("x = "+ x); System.out.println("a = "+ a); System.out.println("b = "+ b); Michael Moossen Departamento de Informática. U.T.F.S.M. 49

62 3.7. MODIFICADORES DE ATRIBUTOS Y MÉTODOS static { System.out.println("Bloque estático inicializado"); b= a* 4; //se ve que, aunque declarada como static //b se inicializa en tiempo de ejecución public static void main(string args[]) { show(15); Tan pronto como Estatica se carga, todas las sentencias del bloque estático se ejecutan. Primero, a se inicializa a 3, luego se ejecuta el bloque estático y, por último, b se inicializa al valor asignado El Modificador final Un atributo que se declara como final, previene que su contenido sea modificado. Esto significa, que se debe inicializar un atributo final cuando se declara. No obstante, Java permite separar la definición de la inicialización. Esta última, se puede hacer más tarde, en tiempo de ejecución, llamando a métodos o en función de otros datos. El atributo final así definido es constante, pero no tiene porqué tener el mismo valor en todas las ejecuciones del programa, pues depende de como haya sido inicializado. Esto es lo más parecido a las constantes de otros lenguajes de programación. Por ejemplo: final float PI = ; Aunque básicamente este operador se emplea para crear constantes en Java, también se puede definir una clase o un método como final. En el primer caso, una clase final no puede tener clases derivadas. Esto se hace por motivos de seguridad y de eficiencia, porque cuando el compilador sabe que los métodos no van a ser redefinidos puede hacer optimizaciones adicionales. En el segundo, un método declarado como final, no puede ser redefinido por una clase que derive de su propia clase El Modificador abstract Es posible que con la herencia se cree una familia de clases con una interfaz común. En esos casos, es posible, y hasta probable, que la clase raíz de las demás no sea una clase útil, y que hasta se desee que el usuario nunca haga instancias de ella, porque su utilidad es inexistente. No se quieren implementar sus métodos, sólo declararlos, para crear una interfaz común. Entonces, se declaran sus métodos como abstractos: public abstract void mi metodo(); Como se ve, se declara el método pero no se implementa, ya que se sustituye el código que debería ir entre llaves por un punto y coma. Cuando existe un método abstracto se debe declarar la clase abstracta, o el compilador dará un error. Al declarar como abstracta una clase se asegura que el usuario no pueda crear instancias de ella: abstract class Mamífero { String especie, color; public abstract void mover(); class Gato extends Mamífero { int numero patas; public void mover() { System.out.println("El gato es el que se mueve"); 50 Departamento de Informática. U.T.F.S.M. Michael Moossen

63 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS public class Abstractos { public static void main(string[] args) { Gato bisho = new Gato(); bisho.mover(); En este ejemplo de herencia, parece absurdo pensar que se vaya a crear instancias de Mamífero, sino de alguna de sus clases derivadas. Por eso, se decidió declararlo abstracto El Modificador transient Por defecto, las variables miembro son una parte persistente del estado de un objeto. Las variables que forman parte persistente del estado del objeto deben guardarse cuando el objeto es serializado (ver sección 4.5.5). Se puede utilizar la palabra clave transient para indicar a la JVM que la variable indicada no es una parte persistente del objeto. Al igual que otros modificadors de variables en el sistema Java, se puede utilizar transient en una clase o declaración de variable de ejemplar como esta. class TransientExample { transient int var;... Este ejemplo declara una variable entera llamada var que no es una parte persistente del estado de la clase TransientExample El Modificador volatile Si una clase contiene una variable miembro que es modificada de forma asíncrona, mediante la ejecución de hilos concurrentes, se puede utilizar la palabra clave volatile de Java para notificar esto al sistema Java. La siguiente declaración de variable es un ejemplo de como declarar que una variable va a ser modificada de forma asíncrona por hilos concurrentes. class VolatileExample { volatile int contador; Clases Internas Una clase interna, es una clase definida dentro de otra clase, conocida como clase contenedora, en alguna variante de la siguiente forma general: class ClaseContenedora {... class ClaseInterna { Michael Moossen Departamento de Informática. U.T.F.S.M. 51

64 3.8. CLASES INTERNAS El alcance de una clase interna está limitado por el alcance de la clase que la contiene. Así, si una clase B está englobada dentro de una clase A, entonces B es vista por A, pero no fuera de esta última. Una clase interna tiene acceso a los miembros (atributos o métodos), incluyendo los privados, de la clase que la contiene. No obstante, la clase contenedora no tiene acceso a los miembros de la clase interna. Por ejemplo: public class Contenedora { private int a= 2; public Contenedora() { System.out.println("a dentro de Contenedora vale: "+ a); Inner inner = new Inner(); class Inner { public Inner() { a= a*a; System.out.println("a en inner class: "+ a); public static void main(string args[]) { Contenedora cont = new Contenedora(); Además de su utilidad en sí, las clases internas se utilizan mucho en el modelo de eventos introducido en la versión de Java 1.1 y que se verá en la sección 6.3. Hay que señalar que, la máquina virtual de java no sabe de la existencia de clases internas. Por ello, el compilador convierte estas clases en clases globales, contenidas en archivos.class cuyo nombre es del estilo ClaseContenedora$ClaseInterna.class. Esta conversión inserta variables ocultas, métodos y argumentos en los constructores. Hay cuatro tipos de clases internas: Clases internas estáticas Clases internas miembro Clases internas locales Clases anónimas Clases Internas Estáticas Se conocen también con el nombre de clases anidadas (nested classes). Las clases internas estáticas sólo pueden ser creadas dentro de otra clase al máximo nivel, es decir, directamente en el bloque de definición de la clase contenedora y no en un bloque más interno. Este tipo de clases internas, se definen utilizando la palabra static. Debido a su caracter estático, estas clases no pueden acceder a los miembros de su clase contenedora directamente sino a través de un objeto. En cierta forma, las clases internas estáticas se comportan como clases normales. Para utilizar su nombre desde fuera de la clase contenedora, hay que precederlo por el nombre de la clase contenedora y el operador punto (.). Este tipo de relación entre clases, se puede utilizar para agrupar varias clases dentro de una clase más general. Las clases internas estáticas pueden ver y utlizar los miembros estáticos de la clase contenedora. No se necesitan objetos de la clase contenedora para crear objetos de la clase interna estática. Los métodos de la clase interna 52 Departamento de Informática. U.T.F.S.M. Michael Moossen

65 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS estática, no pueden acceder directamente a los objetos de la clase contenedora. En caso de que los haya, deben disponer de una referencia a dichos objetos, como cualquier otra clase. El hecho de que las clases contenedoras no puedan acceder directamente a sus clases internas estáticas, hace que estas últimas se empleen rara vez Clases Internas Miembro Llamadas también simplemente clases internas, son clases definidas al máximo nivel de la clase contenedora, sin la palabra static. Este tipo de clase interna, no puede tener variables miembro estáticas. Tienen una nueva sintaxis para las palabras reservadas this, new y super, que se verá más adelante. La característica principal de estas clases internas, es que cada objeto de la clase interna, existe siempre dentro de uno y sólo un objeto de la clase contenedora. Un objeto de la clase contenedora, puede estar relacionado con uno o más objetos de la clase interna. Tener esto presente es muy importante para entender las características que se explican a continuación: Los métodos de la clase interna ven directamente las variables miembro del objeto de la clase contenedora, sin necesidad de cualificarlos, cosa que no ocurre al revés. Esto ocurre porque existe una relación uno a varios que existe entre los objetos de la clase contenedora y los de la clase interna. Otras clases diferentes de las clases contenedora e interna, pueden utilizar directamente los objetos de la clase interna, sin cualificarlos, con el objeto o el nombre de la clase contenedora. De hecho, se puede seguir accediendo a los objetos de la clase interna aunque se pierda la referencia al objeto de la clase contenedora con el que están asociados. Una clase interna miembro puede contener otra clase interna miembro, hasta el nivel que se desee (aunque no se considera buena técnica de programación utilizar muchos niveles). En la clase interna, la palabra this, se refiere al objeto de la propia clase interna. Para acceder al objeto de la clase contenedora se utiliza ClaseContenedora.this. Para crear un nuevo objeto de la clase interna, se puede utilizar new, precedido por la referencia al objeto de la clase contenedora que contendrá el nuevo objeto: unobjeto.new (). El tipo del objeto es el nombre de la clase contenedora seguido del nombre de la clase interna, como por ejemplo: Contenedora.Interna objetointerna = objetocontenedora.new Interna(...); Imaginemos ahora que B es una clase interna de A y que C es una clase interna de B. La creación de objetos de las tres clases se puede hacer del siguiente modo: A a = new A(); //se crea un objeto de la clase A A.B b = a.new B(); //b es un objeto de la clase interna B //dentro de a A.B.C c = b.new C(); //c es un objeto de la clase interna C //dentro de b Nunca se puede crear un objeto de la clase interna sin una referencia a un objeto de la clase contenedora. Los constructores de la clase interna tienen, como argumento oculto, una referencia al objeto de la clase contenedora. El nuevo significado de la palabra super es algo complicado: Si una clase deriva de una clase interna, su constructor no puede llamar a super() directamente. Ello hace que el compilador no pueda crear un constructor por defecto. Al constructor hay que pasarle una referencia a la clase contenedora de la clase interna superclase, y con esa referencia llamar a ref.super(). Las clases internas pueden derivar de otras clases diferentes de la clase contenedora. En este caso, conviene tener en cuenta lo siguiente. Las clases internas constituyen como una segunda jerarquía de clases en Java: por una parte están en la clase contenedora y ven sus variables; por otra parte pueden derivar de otra clase que no tenga nada que ver con la clase contenedora. Es muy importante evitar conflictos con los nombres. En caso de conflicto entre un nombre heredado y un nombre en la clase contenedora, el nombre heredado tiene prioridad. En caso de conflicto de nombres, Java obliga a utilizar la referencia this con un nuevo significado: para referirse al atributo o método miembro Michael Moossen Departamento de Informática. U.T.F.S.M. 53

66 3.8. CLASES INTERNAS heredado se utiliza this.name, mientras que se utiliza ClaseContenedora.this.name para el miembro de la clase contenedora. Si una clase contenedora deriva de una super-clase que tiene una clase interna, la clase interna de la subclase puede a su vez derivar de la clase interna de la super-clase y redefinir todos los métodos que necesite. Todo esto expuesto aquí, son todas las posibilidades que puede tener el uso de una clase interna, pero como en todo, esto es sólo cultura general. Como restricciones de estas clases internas miembro se tiene: Ejemplo: No pueden tener el mismo nombre que la clase contenedora. Tampoco pueden tener miembros estáticos, ni atributos, ni métodos, ni clases. //clase contenedora class A { int i=1; //variable miembro public A(int i) { this.i=i; //constructor //los métodos de la clase contenedora necesitan una //referencia a los objetos de la clase interna public void printa(b objetob) { System.out.println("i="+i+", objetob.j="+objetob.j);//s ı lo acepta //la clase interna puede tener cualquier visibilidad. Con private //da error porque main() no puede acceder a la clase interna protected class B { int j=2; public B(int j) { this.j=j; //constructor public void printb() { System.out.println("i="+i+ ", j"+ j); ı//s //fin clase B //fin clase contenedora A sabe que es j class ClaseInterna { public static void main(string args[]) { A a1 = new A(11); A a2 = new A(12); println("a1.i="+a1.i+ ", a2.i="+ a2.i); //forma de crear objetos de la clase interna //asociados a un objeto de la clase contenedora A.B b1 = a1.new B(-10), b2 = a1.new B(-20); //referencia directa a los objetos b1 y b2 (sin cualificar). println("b1.j="+b1.j+ ", b2.j="+ b2.j); //los métodos de la clase interna pueden acceder directamente 54 Departamento de Informática. U.T.F.S.M. Michael Moossen

67 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS //a las variables miembro del objeto de la clase contenedora b1.printb(); //escribe: i=11 j=-10 b2.printb(); //escribe: i=11 j=-20 //los métodos de la clase contenedora deben recibir referencias //a los propios objetos de la clase interna, para que puedan //identificarlos. a1.printa(b1); a1.printa(b2); A a3 = new A(13); A.B b3 = a3.new B(-30); println("b3.j="+ b3.j); a3 = null ; b3.printb(); //escribe: i=13 j=-30 a3 = new A(14); //se crea un nuevo objeto asociado a la //referencia a3 //b3 sigue asociado al anterior objeto de la clase contenedora b3.printb(); //escribe: i=13 j=-30 //fin main() public static void println(string str) { System.out.println(str); //fin clase ClaseInterna Clases Internas Locales Estas clases no se declaran dentro de otra clase al máximo nivel, sino dentro de un bloque de código, normalmente en un método, aunque también se pueden crear en un inicializador estático o en un constructor. Las principales características de las clases locales son las siguientes: Como las variables locales, las clases locales sólo son visibles y utilizables en el bloque de código en el que están definidas. Los objetos de la clase local deben ser creados en el mismo bloque en que dicha clase ha sido definida. De esta forma se puede acercar la definición al uso de la clase. Las clases internas locales tienen acceso a todas las variables miembro y métodos de la clase contenedora. Pueden ver también los miembros heredados, tanto por la clase interna local como por la clase contenedora. Las clases locales pueden utilizar las variables locales y argumentos de métodos visibles en ese bloque de código, pero sólo si son final (en realidad la clase local trabaja con sus copias de las variables locales y por eso se exige que sean final y no puedan cambiar). Un objeto de una clase interna local, sólo puede existir en relación con un objeto de la clase contenedora, que debe existir previamente. La palabra this se puede utilizar en la misma forma que en las clases internas miembro, pero no las palabras new y super Clases Anónimas Las clases anónimas son muy similares a las clases internas locales, pero sin nombre. En las clases internas locales, primero se define la clase y luego se crean uno o más objetos. En las clases anónimas se unen estos dos pasos: como la clase no tiene nombre sólo se puede crear un único objeto, ya que las clases anónimas no pueden definir constructores. Estas clases se utilizan con mucha frecuencia para definir clases y objetos que gestionen los Michael Moossen Departamento de Informática. U.T.F.S.M. 55

68 3.9. INTERFACES eventos de los distintos componentes de la interfaz de usuario como se verá en la sección 6.3. Existen tres maneras de definir una clase anónima: Requieren una extensión de la palabra clave new. Se definen en una expresión de Java, incluida en una asignación o en la llamada a un método. Se incluye la palabra new seguida de la definición de la clase anónima, entre llaves... Otra forma de definirlas es mediante la palabra new seguida del nombre de la clase de la que hereda (sin extends ) y la definición de la clase anónima entre llaves... El nombre de la super-clase puede ir seguido de argumentos para su constructor (entre paréntesis, que con mucha frecuencia estarán vacíos pues se utilizará un constructor por defecto). Una tercera forma de definirlas es con la palabra new seguida del nombre de la interface que implementa (sin implements ) y la definición de la clase anónima entre llaves... En este caso la clase anónima deriva de Object. El nombre de la interface va seguido por paréntesis vacíos, pues el constructor de Object no tiene argumentos. Para las clases anónimas, el compilador produce archivos con un nombre del tipo ClaseContenedora$1.class, asignando un número correlativo a cada una de las clases anónimas. En este tipo de clase, se deben respetar los aspectos tipográficos, pues al no tener nombre dichas clases, suelen resultar difíciles de leer e interpretar. Por lo tanto, cuando se usen, seguir los siguientes consejos: Poner la palabra new en la misma línea que el resto de la expresión. Las llaves se abren en la misma línea que new, después del cierre del paréntesis de los argumentos del constructor. El cuerpo de la clase anónima se debe sangrar respecto a las líneas anteriores de código para que resulte claramente distinguible. El cierre de las llaves va seguido por el resto de la expresión en la que se ha definido la clase anónima. Ejemplo de definición de una clase anónima derivada de ActionListener: objeto.addactionlistener(new ActionListener() { public void actionperformed(actionevent ae) {... ); 3.9. Interfaces La primera y más conocida función del uso de interfaces, es la de resolver el problema de la herencia múltiple presente en otros lenguajes, como por ejemplo C++. Las interfaces de Java, al separar la forma del como (la declaración del código que lo implementa) permiten la herencia múltiple sin los problemas típicos que se pueden plantear, para decidir que implementación usar, cuando una clase hereda de dos que tienen implementaciones distintas para la misma signatura(o header) de un método. Esta claro que tampoco es una solución mágica (simplemente no se hereda el código, así que no hay que decidir cual se coge), pero es una solución. Pero esta no podría ser la única razón para el uso de interfaces, ya que tampoco es la más usada (en realidad se usa bastante poco si se exceptúan los componentes gráficos que implementan varias interfaces de oyentes de eventos). Además, los manuales de la buena programación dicen que las interfaces son para modelar las relaciones tiene un, no es un, aunque esto no siempre se así. Otra función, es cuando se necesita cierto polimorfismo, es decir, cuando se necesita que una clase pudiera tener con los mismos métodos o atributos distintos comportamientos, por ejemplo, una clase que pudiera almacenarse en una base de datos o en un archivo de texto, sólo cambiando el objeto que la hace persistente. Siempre que 56 Departamento de Informática. U.T.F.S.M. Michael Moossen

69 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS todos implementen la misma interfaz, la clase puede almacenarse en cualquier tipo de dispositivo sin cambiar los métodos necesarios. Una tercera razón, quizás menos relacionada con la programación en primera instancia, pero que es de enorme importancia para el diseño (para el buen diseño) de aplicaciones, es que las interfaces de Java son (o pueden ser) el contrato entre distintas partes de una aplicación, o entre los distintos programadores de una aplicación. Si se discuten y diseñan buenas interfaces entre los distintos componentes de la aplicación en la primera parte del desarrollo, los diferentes grupos de trabajo pueden trabajar en paralelo, sin tener que preocuparse de lo que hacen o ya han hecho el resto, y para los tests, es bastante fácil escribir un par de clases de prueba que implementen esas interfaces. De esta forma, la integración entre las distintas partes de la aplicación ya esta hecha desde un principio, sin tener que andar consultando sobre que métodos se dispone para llevar a cabo una labor, y los grupos de desarrollo sólo tienen que preocuparse de la implementación de su parte. Además, el uso de interfaces, debido a la propia naturaleza de Java, hace que sea tremendamente fácil encontrar las clases que se ven afectadas por un cambio brusco en alguna parte de la aplicación, ya que lo hará el compilador sólo Declaración de una Interfaz Como mínimo, una declaración de interfaz contiene la palabra clave interface que se va a crear. interface Contable {... y el nombre de la interfaz Nota: Por convención, los nombres de interfaces empiezan con una letra mayúscula al igual que las clases. Frecuentemente los nombres de interfaces terminan en able o ible. Una declaración de interfaz puede tener otros dos componentes: el especificador de acceso public y una lista de superinterfaces. Una interfaz puede extender otras interfaces, como una clase puede extender o subclasificar otra clase. Sin embargo, mientras que una clase sólo puede extender una superclase, las interfaces pueden extender a cualquier número de interfaces. Así, una declaración completa de interfaz se parecería a esto. [public ] interface NombreDeInterface [extends ListaDeSuperInterfaces] {... El especificador de acceso public, indica que la interfaz puede ser utilizada por todas las clases en cualquier paquete. Si la interfaz no se especifica como pública, sólo será accesible para las clases definidas en el mismo paquete que la interfaz. La cláusula extends es similar a la utilizada en la declaración de una clase, sin embargo, una interfaz puede extender varias interfaces (mientras una clase sólo puede extender una), y una interfaz no puede extender clases. Esta lista de superinterfaces es un lista delimitada por comas de todas las interfaces extendidas por la nueva interfaz. Una interfaz hereda todas las constantes y métodos de sus superinterfaces, a menos que la interfaz oculte una constante con el mismo nombre o redeclare un método con una nueva declaración. El Cuerpo de una Interfaz El cuerpo de la interfaz contiene las declaraciones de métodos para los métodos definidos en la interfaz. Además de las declaraciones de los métodos, una interfaz puede contener declaraciones de constantes. Nota: Las declaraciones de miembros en una interfaz no permiten el uso de algunos modificadores y desaconsejan el uso de otros. No se podrán utilizar transient, volatile, o synchronized en una declaración de miembro en una interfaz. Tampoco se podrá utilizar los especificadores private y protected cuando se declaren miembros de una interfaz. Todos los valores constantes definidos en una interfaz son implícitamente públicos, estáticos y finales. El uso de Michael Moossen Departamento de Informática. U.T.F.S.M. 57

70 3.9. INTERFACES estos modificadores en una declaración de constante en una interfaz está desaconsejado por falta de estilo. Similarmente, todos los métodos declarados en una interfaz son implícitamente públicos y abstractos. Este código define una interfaz llamada Colección que contiene un valor constante y tres declaraciones de métodos. interface Colección { int MAXIMO = 500; void agregar(object obj); void borrar(object obj); Object buscar(object obj); int contadoractual(); La interfaz anterior puede ser implementada por cualquier clase que represente una colección de objetos como pueden ser pilas, vectores, etc... Se observa que cada declaración de método está seguida por un punto y coma (;), porque una interfaz no proporciona implementación para los métodos declarados dentro de él Uso de una Interfaz Para utilizar una interfaz se debe escribir una clase que lo implemente. Una clase declara todas las interfaces que implementa en su declaración de clase. Para declarar que una clase implementa una o más interfaces, se utiliza la palabra clave implements seguida por una lista delimitada por comas con las interfaces implementadas por la clase. Por ejemplo, considere la interfaz Colección presentado en la sección anterior. Ahora, suponga que quiere escribir una clase que implemente una pila FIFO (primero en entrar, primero en salir). Como una pila FIFO contiene otros objetos, tiene sentido que implemente la interfaz Colección. La clase PilaFIFO declara que implementa la interfaz Colección de esta forma. class PilaFIFO implements Colección { void agregar(object obj) {... void borrar(object obj) {... Object buscar(object obj) {... int contadoractual() {... Así se garantiza que proporciona implementación para los métodos agregar(), borrar(), buscar() y contadoractual(). Por convención, la cláusula implements sigue a la cláusula extends si es que esta existe. Observe que las firmas de los métodos de la interfaz Colección implementados en la clase PilaFIFO deben corresponder exactamente con las firmas de los métodos declarados en la interfaz Colección, sino se produce un error de compilación. 58 Departamento de Informática. U.T.F.S.M. Michael Moossen

71 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS Paquetes En Java, es posible agrupar varias clases en una estructura llamada paquete. Un paquete no es más que un conjunto de clases, generalmente relacionadas entre sí de alguna manera. Es habitual diseñar una aplicación, distribuyendo su funcionalidad entre varios paquetes, cuyas clases se comunican entre sí a través de interfaces bien definidas. El uso de paquetes aporta varias ventajas frente a la programación sin paquetes. En primer lugar, permite encapsular funcionalidad, en unidades con un cierto grado de independencia, ocultando los detalles de implementación. De esta forma, se pueden conseguir diseños (e implementaciones) más limpios y elegantes. Por otra parte, se potencia la reutilización de las clases desarrolladas. Es posible definir interfaces de uso de cada paquete, para que otros paquetes o aplicaciones puedan utilizar la funcionalidad implementada. Además, el uso de paquetes permite la reutilización de los nombres de las clases, ya que el espacio de nombres de un paquete, es independiente del de otros. El lenguage Java impone la restricción de que una clase debe tener un nombre único, dentro del paquete al cual pertenece. Sin embargo, es posible que dos clases tengan el mismo nombre, siempre y cuando pertenezcan a paquetes distintos Uso de Paquetes Para incluir una clase dentro de un paquete, únicamente hay que utilizar una sentencia package del archivo fuente. Por ejemplo: al principio package prueba; public class Prueba {... O con subpaquetes: package prueba.prueba2; public class Prueba { Nombre de Paquetes y Clases El nombre completo de la clase ( fully qualified name ) está compuesto por el nombre del paquete al cual pertenece la clase, además del nombre de la propia clase. Los nombres completos de las clases del ejemplo anterior son prueba.prueba y prueba.prueba2.prueba. Dado que prueba y prueba.prueba2 son paquetes distintos (aunque prueba.prueba2 sea un subpaquete de prueba), no hay colisión de nombres entre prueba.prueba y prueba.prueba2.prueba. Sin embargo, aunque el compilador permita usar el mismo nombre de esta forma, no es una práctica aconsejable, porque el código tiende a ser confuso y propenso a errores. En cuanto al nombre de los paquetes, Sun propuso en su momento un convenio para facilitar el uso de paquetes de terceros. Este convenio no es en absoluto obligatorio, aunque sí es aconsejable seguir alguno, para mantener mínimamente organizadas las clases. Según el convenio propuesto por Sun, el nombre de los paquetes desarrollados por una compañía, se forma a partir del nombre del dominio DNS que esa compañía tiene registrado, invirtiendo el orden de sus miembros. Por ejemplo, un paquete de gestión de stocks llamado manager desarrollado por IBM, podría llamarse com.ibm.stocks.manager. Michael Moossen Departamento de Informática. U.T.F.S.M. 59

72 3.10. PAQUETES Uso de Otros Paquetes Para usar clases de otros paquetes, no hay más que utilizar sentencias import clase. Por ejemplo:, antes de la declaración de la import java.lang.*; import java.util.vector; Con el primer import, se le indica al compilador que se importan todas las clases del subpaquete java.lang. (Este paquete contiene clases esenciales en la mayoría de aplicaciones Java, por lo que el compilador lo importa automáticamente, aunque no se incluya una línea como en este caso). Con el segundo import, se indica que sólo se quiere importar la clase Vector del subpaquete java.util. Es posible utilizar una clase sin que esté incluída en un import, siempre y cuando se utilice su nombre completo. Por ejemplo: java.util.vector vector = new java.util.vector(100, 10); La ventaja de utilizar una sentencia import, es que la línea anterior se puede reducir a: Vector vector = new Vector(100, 10); Estructura Jerárquica de Directorios Los archivos.class pertenecientes a paquetes y subpaquetes deben estar organizados de forma adecuada para que tanto el compilador como la máquina virtual puedan utilizarlos. Para ello, cada paquete se ubica en un directorio. Cada subpaquete se ubica en un directorio de nivel inferior, y así sucesivamente. Además, el nombre de cada directorio debe ser el del propio paquete. Por ejemplo, las clases del paquete prueba, deben estar situadas en un directorio llamado prueba. Dentro de este, debe haber un subdirectorio llamado prueba2, en el que se deben situar las clases del paquete prueba.prueba2. Así, cada (sub)paquete se corresponde con un (sub)directorio, y viceversa. No existen restricciones en cuanto a donde debe estar situado el primer nivel, pero una vez situado en algún directorio, los niveles descendientes deben respetar la jerarquía. En el ejemplo anterior, se podría situar el directorio prueba, en el directorio raíz del árbol de directorios (por ejemplo C:/), en un directorio de primer nivel (por ejemplo, C:/java), en uno de segundo, etc. En algunos tutoriales, libros y manuales, a este primer nivel donde están situados los directorios se le llama codebase. Si nuestro codebase es C:/java/codebase, debe haber un directorio C:/java/codebase/prueba y otro C:/java/codebase/prueba/prueba Ejecución de las Clases A la hora de ejecutar una aplicación Java, tener en cuenta algunas cosas. En primer lugar, hay que indicar el nombre completo de la clase a ejecutar. Por ejemplo, si se tiene una clase Manager que pertenece al paquete com.ibm.manager, la orden para ejecutarla sería: java com.ibm.manager.manager Para que el anterior comando funcione, es necesario que el directorio actual desde el que se lanza la orden, sea el codebase (por ejemplo, C:/java/codebase, si la clase Manager está en C:/java/codebase/com/ibm/manager/Manager.class). Si se esta en otro directorio, es posible evitar el tener que cambiar de directorio, indicando a la máquina virtual qué directorio debe tomar como codebase, con una orden como: java -classpath C:/java/codebase com.ibm.manager.manager 60 Departamento de Informática. U.T.F.S.M. Michael Moossen

73 CAPÍTULO 3. PROGRAMACIÓN ORIENTADA A OBJETOS Puede ser que en el árbol de directorios, se tengan todas las clases juntas a partir de un único directorio codebase, o que se tengan separadas en varios. Puede ser que se tengan unas clases a partir de C:/java/codebase y otras a partir de D:/clases, por ejemplo. Para facilitar la ejecución de las clases y evitar el tener que incluir parámetros demasiado largos en las invocaciones a la máquina virtual, es posible definir una variable de entorno llamada CLASSPATH que contenga todos los directorios codebase del sistema. Utilizando esta variable, es posible invocar y utilizar clases que estén en cualquier parte, sin tener que pasarle un parámetro -classpath a la máquina virtual. Por ejemplo, con CLASSPATH = C:/java/codebase;D:/clases es posible utilizar clases que estén situadas en esos dos directorios. En el ejemplo anterior, ya no es necesario el parámetro -classpath, por lo que el comando: java com.ibm.manager.manager puede ser ejecutado desde cualquier directorio. (No es posible suprimir com, com.ibm o com.ibm.manager, puesto que forman parte del nombre completo de la clase). Michael Moossen Departamento de Informática. U.T.F.S.M. 61

74 3.10. PAQUETES 62 Departamento de Informática. U.T.F.S.M. Michael Moossen

75 Capítulo 4 Programación con la API Estándar En este capítulo se verán varios aspectos importantes de la programación con Java, como la manipulación eficiente de cadenas de caracteres con la clase StringBuffer, manejo de fechas y funciones matemáticas, y también temas específicos como la conectividad a bases de datos y el manejo de archivos Manejo de Strings En esta sección, se verán algunos detalles del manejo de cadenas de caracteres o strings. Se verán primero algunas características de la clase String, luego la funcionalidad de la clase StringBuffer, que permite aumentar el rendimiento en el manejo de strings, y finalmente la clase StringTokenizer que es una herramienta clave para descomponer strings La Clase String La clase String representa cadenas de caracteres. Todos los literales de cadenas de caracteres, como "abc", son implementados como instancias de esta clase. Los objetos de tipo String son constantes, sus valores no pueden ser modificados una vez creados. La clase StringBuffer soporta strings mutables. Ejemplos de un string: String str = "abc"; Que es equivalente a: char data[] = { a, b, c ; String str = new String(data); A continuación, algunos ejemplos del uso de strings: System.out.println("abc"); String cde = "cde"; System.out.println("abc"+ cde); String c = "abc".substring(2,3); String d = cde.substring(1, 2); La clase String incluye métodos para examinar los caracteres individuales de la secuencia, para comparar strings, para buscar strings, para extraer substrings, y para crear copias en mayúsculas o minúsculas. A continuación, una breve descripción de los principales métodos de la clase, para realizar estas funcionalidades: char charat(int index) Retorna el caracter en la posición especifícada por index. index debe ser un valor entre cero y length()-1. 63

76 4.1. MANEJO DE STRINGS int compareto(string anotherstring) Compara el string actual con otro en forma lexicográfica. El resultado es un entero negativo si este string precede al parámetro, un número positivo si el parámetro precede este string o cero si son iguales. int indexof(string str, int fromindex) Retorna la posición de la primera ocurrencia del parámetro en el string actual, a partir de la posición indicada por fromindex. Si no se encuentra ninguna ocurrencia se devuelve -1. int length() Retorna el largo del string en caracteres. String replace(char oldchar, char newchar) Retorna un nuevo string que resulta de reemplazar todas las ocurrencias de oldchar por newchar en el string actual. String substring(int beginindex, int endindex) Retorna un nuevo string formado por los caracteres del string original desde la posición beginindex inclusive hasta la posición endindex exclusive. Si beginindex es negativo, o endindex es mayor que length(), o si beginindex es mayor que endindex se produce un IndexOutOfBoundsException. String tolowercase() Convierte todos los caracteres de este string a caracteres en minúscula. String touppercase() Convierte todos los caracteres de este string a caracteres en mayúscula. String trim() Remueve espacios en blanco(y caracteres de control) tanto del principio como del final del string La Clase StringBuffer Un StringBuffer implementa una secuencia de caracteres mutables, es como un string, pero que puede ser modificado. En cualquier instante del tiempo, su contenido es una secuencia de caracteres bien definida, pero su largo y contenido pueden ser cambiados a través de la invocación de algunos métodos. StringBuffer es usado por el compilador, para implementar el operador de concatenación binario +. Por ejemplo, el código: String x = "a"+ 4 + "c"; Se compila como si fuese: String x = new StringBuffer().append("a").append(4).append("c").toString(); Lo cual crea, un nuevo StringBuffer inicialmente vacío, luego se le concatena la representación en string de cada operando uno a uno, y finalmente se convierte el StringBuffer en un String. Las principales operaciones que pueden ser invocadas por un StringBuffer, son los métodos insert() y append(), los cuales están sobrecargados para permitir la manipulación de prácticamente cualquier tipo de dato. El método append() siempre agrega su parámetro al final del buffer, y el método insert() lo hace en la posición especifícada. Por ejemplo, si z es un StringBuffer cuyo contenido actual es "start", entonces la llamada z.append("le") dejaría el contenido de z en "startle". Así también, la llamada z.insert(4, "le") produciría "starlet". En general, si sb es una instancia de un StringBuffer, entonces sb.append(x) tiene el mismo efecto que sb.insert(sb.length(), x). Cada StringBuffer tiene una capacidad. Mientras el largo de la cadena de caracteres que almacene no exceda esta capacidad, no es necesario reservar más espacio de memoria. Si en cambio, esta capacidad se sobrepasa, automaticamente se ajustará la memoria reservada por el StringBuffer. 64 Departamento de Informática. U.T.F.S.M. Michael Moossen

77 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR La Clase StringTokenizer La clase StringTokenizer permite separar un string en varios tokens. El conjunto de delimitadores (los caracteres que separan los tokens) deben ser especificados al momento de la creación. Una instancia de un StringTokenizer se puede comportar de dos maneras, dependiendo si fue creado con el modificador returndelims con el valor verdadero o falso. Si es falso, los caracteres delimitadores sirven para separar los tokens. Un token es una secuencia máxima de caracteres consecutivos que no son delimitadores. Si es verdadero, los caracteres delimitadores son también considerados tokens. por lo tanto, un token es un caracter delimitador o una secuencia máxima de caracteres consecutivos que no son delimitadores. Un objeto StringTokenizer mantiene internamente la posición del string ya procesada. Así, algunas operaciones permiten seguir procesando el string y cambiar este estado interno. El siguiente ejemplo demuestra el uso de un StringTokenizer: StringTokenizer st = new StringTokenizer("this is a test"); while (st.hasmoretokens()) { System.out.println(st.nextToken()); Se obtiene como salida: this is a test 4.2. Utilidades Iteradores Existe una interfaz llamada Iterator. Un objeto que implementa esta interfaz, genera una serie de elementos, de a uno a la vez. Llamadas sucesivas al método next() retorna elementos sucesivos de la serie. El método hasnext() indica si aún quedan elementos en la serie. Por ejemplo, para iterar por sobre todos los elementos de un vector: for (Iterator it = V.iterator(); it.hasnext(); ) { System.out.println(it.next()); Java provee los mecanismos para iterar sobre los elementos de un Vector, las claves y valores de un Hashtable, entre otros Vectores La clase Vector implementa un arreglo de objetos mutable. Como un arreglo, contiene componentes que pueden ser accesados usando un índice entero. Sin embargo, el tamaño de un vector puede variar según se necesite para acomodar los objetos agregados o removidos después que el vector fue creado. Cada vector trata de optimizar la administración de memoria, manteniendo variables de capacidad e incremento de capacidad. La capacidad es siempre, por lo menos del tamaño del vector, siendo usualmente un poco mayor, ya que cada vez que se debe aumentar la capacidad del vector, esta lo hace en bloques de tamaño dado por la variable de incremento de capacidad. Una aplicación, también podría aumentar explícitamente la capacidad de un vector, antes de agregar una gran cantidad de objetos, para así diminuir la sobrecarga de la administración de memoria. A continuación, una breve descripción de los métodos más importantes de esta clase: Michael Moossen Departamento de Informática. U.T.F.S.M. 65

78 4.2. UTILIDADES void addelement(object obj) Agrega el objeto especifícado al final del vector, incrementando su tamaño en uno. Su capacidad también es incrementada si es necesario. boolean removeelement(object obj) Elimina la ocurrencia del objeto(con menor índice) en el vector. Todos los elementos posteriores al elemento eliminado, quedan con su índice disminuído en uno. String tostring() Retorna la representación de este vector, conteniendo la representación en string de cada elemento. boolean isempty() Prueba si este vector tiene elementos. Iterator iterator() Retorna un iterador, para iterar sobre los elementos del vector. int indexof(object elem, int index) Retorna el índice del objeto buscado a partir del índice indicado. Si no se encuentra, retorna -1. Object elementat(int index) Retorna el elemento en la posición indicada. void setelementat(object obj, int index) Reemplaza el elemento en la posición indicada La Clase Hashtable Esta clase implementa una interfaz, que mapea nombres clave a valores. Cualquier objeto distinto de null puede ser usado como clave o valor. Para guardar y obtener objetos exitosamente de un Hashtable, los objetos usados como clave, deben implementar los métodos hashcode() y equals(). Una instancia de Hashtable tiene dos parámetros que afectan su rendimiento: la capacidad inicial y el factor de carga. La capacidad es el número de posiciones disponibles en la tabla, y el factor de carga, es una medida de que tan llena puede estar la tabla antes de que incremente su capacidad automaticamente. Cuando la cantidad de elementos en la tabla excede el producto entre el factor de carga y la capacidad actual, la capacidad es incrementada llamando al método rehash(). Generalmente, el factor de carga por defecto (0.75) ofrece un buen compromiso entre costos de tiempo y espacio. Valores mayores reducen la pérdida de espacio, pero incrementan el costo de tiempo de accceso a los elementos. La capacidad inicial controla el compromiso entre espacio perdido y la necesidad de reestructurar la tabla. No será necesario reestructurar la tabla, si la capacidad inicial es mayor que el número de elementos máximo que tendrá la tabla, dividido por su factor de carga. Sin embargo, al asignar a la capacidad inicial un valor muy grande, se puede perder mucho espacio. El siguiente ejemplo crea un Hashtable de números, usando los nombres de los números como clave: Hashtable numbers = new Hashtable(); numbers.put("one", new Integer(1)); numbers.put("two", new Integer(2)); numbers.put("three", new Integer(3)); Para obtener el número, se usa el siguiente código: Integer n = (Integer)numbers.get("two"); if (n!= null ) { System.out.println("two = "+ n); 66 Departamento de Informática. U.T.F.S.M. Michael Moossen

79 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR A continuación, una descripción breve de los métodos más importantes de esta clase: Object put(object key, Object value) Mapea la clave especificada al valor especificado en la tabla. Ni la clave ni el valor pueden ser null. El valor puede ser obtenido llamando al método get() con la clave original. Este método retorna el valor previo especificado por la clave en la tabla, o null si no existía. Object get(object key) Retorna el valor al cual está mapeada la clave especificada en la tabla, o null a ningún valor de la tabla. si la clave no está mapeada Object remove(object key) Realiza la misma operación que get(), pero además remueve la clave y su valor correspondiente de la tabla. String tostring() Retorna una representación de string del objeto, en la forma de un conjunto de entradas de la forma "clave = valor", usando el método tostring() de cada objeto La Clase System La clase System contiene diversos atributos y métodos útiles. Es una clase estática, que no puede ser instanciada. Algunas de las funcionalidades que provee esta clase son: Flujos de datos de entrada, salida y error estándares; y acceso a propiedades del sistema, entre otras. Los flujos de datos estándares se definen como: static final InputStream in; static final PrintStream out, err; Cada uno de estos flujos de datos está siempre abierto y listo para recibir y enviar datos. Tipicamente estos flujos de datos corresponden a la entrada a través del teclado y la salida a través de pantalla. Ejemplo: System.out.println(data) Estos son los métodos más importantes de esta clase: static String getproperty(string key) Retorna la propiedad del sistema indicada por la clave especificada. El apéndice B muestra un listado de propiedades válidas en la J2SE 1.4. static String setproperty(string key, String value) Establece la propiedad del sistema indicada, al valor especificado. Retorna el valor de la propiedad del sistema, o null si no existe ninguna, con la clave especificada. static void setin(inputstream in) static void setout(printstream out) static void seterr(printstream err) Estos métodos reasignan los flujos estándares. Por ejemplo, para redireccionar la salida estándar a un archivo, basta esta línea: System.setOut(new PrintStream(new FileOutputStream(new File("c:/out.log")))); Así, el método System.out.println() escribe en el archivo "c:/out.log". static void exit(int status) Este método termina la ejecución de la JVM. El argumento sirve como código del estado de la ejecución; por convención, un valor distinto de cero indica un error de ejecución. Michael Moossen Departamento de Informática. U.T.F.S.M. 67

80 4.3. FUNCIONES MATEMÁTICAS 4.3. Funciones Matemáticas Java también provee de un sin número de funciones matemáticas, distribuídas en varias clases: La Clase Abstracta Number Esta clase abstracta es la superclase de todas las clases encapsuladora de números como Byte y Long. Las subclases de Number deben proveer métodos para convertir el valor númerico representado a byte, double, float, int, long y short. Por ejemplo, int intvalue() Debe retornar el número especificado como un int. Notar que esto puede producir aproximaciones y truncamientos. Además, todas las clases encapsuladoras de números, como Integer o Float, proveen los siguientes métodos (se muestran los métodos de la clase Double): static double parsedouble(string s) throws NumberFormatException Retorna un nuevo double inicializado al valor representado por el string especificado. static Double valueof(string s) throws NumberFormatException Retorna un nuevo objeto Double inicializado al valor representado por el string especificado. El string es interpretado como una representación de un valor punto flotante. Espacio en blanco al inicio o final del string son ignorados. Por otra parte, las clases encapsuladoras de números decimales, como Float y Double, proveen las siguientes constantes(se muestran los métodos de la clase Double): static final double NaN Esta constante representa un valor no númerico(not-a-number) de tipo Double, y es igual al valor calculado como: Double.longBitsToDouble(0x7ff L) static final double POSITIVE INFINITY Esta constante representa el valor + de tipo Double, y es igual al valor calculado como: Double.longBitsToDouble(0x7ff L) static final double NEGATIVE INFINITY Esta constante representa el valor de tipo Double, y es igual al valor calculado como: Double.longBitsToDouble(0xfff L) La Clase Math Esta clase contiene métodos para realizar operaciones númericas básicas, como por ejemplo funciones trigonométricas, logaritmo, exponencial y raíz cuadrada. A diferencia de la clase StrictMath, las funciones de esta clase no definen un resultado bit-a-bit concordante con el estándar. Esta relajación permite implementaciones de mejor rendimiento cuando no se requiere reproducción estricta. Por defecto, muchas de las funciones de esta clase simplemente delegan la funcionalidad a las funciones equivalentes en StrictMath. Para ayudar a asegurar la portabilidad de los programas Java, la definición de muchas de las funciones de StrictMath, requiere que produzcan el mismo resultado que ciertos algoritmos públicos. Estos algoritmos están publicados en netlib, bajo el paquete llamado Freely Distributable Math Library(fdlibm). Esta librería puede 68 Departamento de Informática. U.T.F.S.M. Michael Moossen

81 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR ser encontrada en Las funciones matemáticas de Java fueron definidas con respecto a la versión del 4 de Enero de Esta clase contiene las siguientes constantes matemáticas: static final double E El valor de tipo double más cercano que cualquier otro al valor de e, la base del algoritmo natural. static final double PI El valor de tipo double más cercano que cualquier otro al valor de π, la razón entre la circunferencia de un circulo y su diámetro. A continuación, una breve descripción de los métodos más importantes de esta clase: static double sin(double a) Retorna el seno trigonométrico de un ángulo. También existen las funciones tirgonométricas cos(), tan(), asin(), acos() y atan(). static double abs(double a) Retorna el valor absoluto de un valor double. El calculo exacto es: Double.longBitsToDouble((Double.doubleToLongBits(a)<<1)>>>1) También existen versiones de este método para float, int y long. static int max(int a, int b) Retorna el mayor de los dos valores enteros. También existen versiones de este método para float, double y long. static long min(long a, long b) Retorna el menor de los dos valores. También existen versiones de este método para float, double int. static double random() Retorna un valor mayor o igual a cero y menor que uno. Los valores retornados son escogidos pseudoaleatoriamente con una distrubción (aproximadamente) uniforme en este rango. static double floor(double a) Retorna el mayor valor double que no es mayor que el argumento y que es igual a un entero matemático. static double ceil(double a) Retorna el menor valor double que no es menor que el argumento y que es igual a un entero matemático. Notar que Math.ceil(x) es exactamente lo mismo que Math.floor(-x). static int round(float a) Retorna el entero más cercano al argumento. El resultado es igual al valor de la expresión: (int )Math.floor(a + 0.5f) static double sqrt(double a) Retorna la raíz cuadrada positiva de un valor double. static double log(double a) Retorna el algoritmo natural (base e) de un valor double. static double exp(double a) Retorna el número exponencial (e) elevado a la potencia indicada. static double pow(double a, double b) Retorna el valor del primer argumento elevado a la potencia indicada por el segundo. Michael Moossen Departamento de Informática. U.T.F.S.M. 69 e

82 4.4. MANEJO DE FECHAS El paquete java.math Este paquete provee clases para realizar aritmética entera(biginteger) y decimal(bigdecimal) de precisión arbitraria. La clase BigInteger es análoga al tipo de dato int, excepto que provee precisión arbitraria, por lo que no se pueden producir errores de desbordamiento o perdida de precisión. Además de las operaciones aritméticas estándares, BigInteger provee de aritmética modular, calculo del máximo común denominador, prueba de primalidad, generación de números primos, manipulación de bits, etc. El siguiente ejemplo muestra nuevamente los números de Fibonacci, ahora usando precisión arbitraria, lo que permite obtener resultados precisos. BigInteger fibonacci(long num) { BigInteger sum = BigInteger.ZERO; if (num == 1 num == 2) { sum = BigInteger.ONE; else { BigInteger f1 = BigInteger.ONE; BigInteger f2 = BigInteger.ONE; for (long i = 3; i <= num; i++) { sum = f1.add(f2); f1 = f2; f2 = sum; return sum; Así, se puede obtener el valor real de F 20000, y comprobar que tiene 4180 dígitos!!. La clase BigDecimal, además de proveer las operaciones aritméticas básicas, permite el control absoluto sobre el comportamiento al redondear, permitiendo eligir entre ocho modos diferentes. El siguiente ejemplo, muestra el valor de π/e, con 2000 decimales de precisión: BigDecimal a = new BigDecimal(Math.PI); a = a.divide(new BigDecimal(Math.E), 2000, BigDecimal.ROUND DOWN); System.out.println(a); 4.4. Manejo de Fechas Antes de la aparición del JDK 1.1, la clase Date tenía dos funcionalidades adicionales a las actuales. Permitía la interpretación de fechas como valores de año, mes, día, hora, minuto y segundo. También permitía formatear fechas y convertir strings en fechas. Desafortunadamente esta API no soportaba las nuevas funcionalidades de internacionalización incorporada en el JDK 1.1, por lo que desde entonces, se debe usar la clase Calendar para hacer conversiones entre fechas y campos, y la clase DateFormat para formatear fechas y convertir strings en fechas La Clase Date La clase Date representa un instante específico en el tiempo, con precisión a la milésima de segundo. Aunque la clase Date intenta reflejar la Hora Universal(UTC), puede que no sea exacta, dependiendo del entorno del computador en que se ejecute la JVM. Casi cualquier sistema operativo moderno asume que un día es igual a 70 Departamento de Informática. U.T.F.S.M. Michael Moossen

83 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR = segundos. En la UTC, sin embargo, cerca de cada año par o dos, existe un segundo adicional, llamado leap second, el cual se agrega como último segundo del día, siempre un 31 de Diciembre o un 30 de Junio. Por ejemplo, el último minuto del año 1995 tuvo 61 segundos. A continuación, una breve descripción de los constructores más útiles de esta clase: Date() Crea un nuevo objeto que se inicializa de tal forma que represente la hora a la cual fue creado. Date(long date) Crea un nuevo objeto que se inicializa de tal forma que represente el número especificado de milisegundos desde la hora base estándar, llamada también época(epoch), actualmente Enero 1, 1970, 00:00:00 GMT. Y una breve descripción de los métodos más importantes: int compareto(date anotherdate) Compara dos fechas. Retorna cero si las fechas son iguales, un valor menor que cero si esta fecha es menor que el argumento; y un valor mayor que cero si esta fecha es mayor que el parámetro. long gettime() Retorna el número de milésimas de segundo desde Enero 1, 1970, 00:00:00 GMT representados por esta fecha. void settime(long time) Fija esta fecha de tal forma que represente el número de milésimas de segundo desde Enero 1, 1970, 00:00:00 GMT. String tostring() Convierte esta fecha a un string de la forma: dow mon dd hh:mm:ss zzz yyyy. Ver más detalles de los formatos en la sección La Clase GregorianCalendar Esta clase es una subclase concreta de Calendar y provee el calendario estándar usado en casi todo el mundo. Esta implementación considera una única discontinuidad, que corresponde a la fecha en que se instituyó el calendario Gregoriano, en la mayoría de los países el 15 de octubre de Así, antes del 4 de Octubre de 1582, se usa el calendario Juliano y al día siguiente, el 15 de Octubre, se comienza a usar el calendario Gregoriano que incorpora los años bisiestos(los años divisibles por cuatro, excepto los años divisibles por 100 y no divisibles por 400, tienen un día más: el 29 de Febrero). Sin embargo, las fechas anteriores al año 45 Antes de Cristo pueden no ser exactas debido a que no existía un calendario estándar. También, se pueden producir problemas con años anteriores a 1583, ya que el año nuevo se celebraba el 25 de Marzo, así para evitar confusiones, este calendario siempre usa el 1 de Enero como año nuevo. A continuación, un ejemplo ilustrativo de los principales usos de este calendario: System.out.println("Current Time"); // create a Chile/Continental Time time zone SimpleTimeZone pdt = new SimpleTimeZone(-4 * 60 * 60 * 1000, "Chile/Continental"); // create a GregorianCalendar with the Chile/Continental time zone // and the current date and time Calendar calendar = new GregorianCalendar(pdt); calendar.settime(new Date()); // print out a bunch of interesting things System.out.println("ERA: "+ calendar.get(calendar.era)); System.out.println("YEAR: "+ calendar.get(calendar.year)); System.out.println("MONTH: "+ calendar.get(calendar.month)); Michael Moossen Departamento de Informática. U.T.F.S.M. 71

84 4.4. MANEJO DE FECHAS System.out.println("WEEK OF YEAR: "+ calendar.get(calendar.week OF YEAR)); System.out.println("WEEK OF MONTH: "+ calendar.get(calendar.week OF MONTH)); System.out.println("DATE: "+ calendar.get(calendar.date)); System.out.println("DAY OF MONTH: "+ calendar.get(calendar.day OF MONTH)); System.out.println("DAY OF YEAR: "+ calendar.get(calendar.day OF YEAR)); System.out.println("DAY OF WEEK: "+ calendar.get(calendar.day OF WEEK)); System.out.println("DAY OF WEEK IN MONTH: "+ calendar.get( Calendar.DAY OF WEEK IN MONTH)); System.out.println("AM PM: "+ calendar.get(calendar.am PM)); System.out.println("HOUR: "+ calendar.get(calendar.hour)); System.out.println("HOUR OF DAY: "+ calendar.get(calendar.hour OF DAY)); System.out.println("MINUTE: "+ calendar.get(calendar.minute)); System.out.println("SECOND: "+ calendar.get(calendar.second)); System.out.println("MILLISECOND: "+ calendar.get(calendar.millisecond)); System.out.println("ZONE OFFSET: "+ (calendar.get( Calendar.ZONE OFFSET)/(60*60*1000))); System.out.println("DST OFFSET: "+ (calendar.get( Calendar.DST OFFSET)/(60*60*1000))); System.out.println(calendar.getTime()); System.out.println("Current Time, plus 1day 5h32m"); calendar.add(calendar.date, 1); calendar.add(calendar.hour, 5); calendar.add(calendar.minute, 32); System.out.println(calendar.getTime()); La Clase SimpleDateFormat Esta clase es una clase concreta para formatear(convertir una fecha en un string) y parsear(convertir un string en una fecha) fechas. Sintaxis de Formatos de Fecha Para especificar el formato de fecha se usa un patrón de fecha. Este patrón, se puede formar por los siguientes símbolos: Símbolo Descripción Presentación Ejemplo G Descriptor de era Texto AD y Año Número 1996 M Mes del año Texto ó Número July ó 07 d Día del mes Número 10 h Hora en am/pm (1-12) Número 12 H Hora del día (0-23) Número 0 m Minuto de hora Número 30 s Segundo de minuto Número 55 S Milésima de segundo Número 978 E Día de la semana Texto Martes D Día del año Número 189 F Día de la semana del mes Número 2 (Segundo Lunes de Julio) w Semana del año Número 27 W Semana del mes Número 2 a Descriptor de am/pm Texto PM k Hora del día (1-24) Número Departamento de Informática. U.T.F.S.M. Michael Moossen

85 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR Símbolo Descripción Presentación Ejemplo K Hora en am/pm (0-11) Número 0 z Zona horaria Texto Hora Estándar del Pacífico Escape para texto Delimitador dd de MMMM de yyyy Comilla simple Literal la cantidad de símbolos en el patrón determinan el formato: Texto: Cuatro o más símbolos significa descripción completa, menos de cuatro, usa la forma corta si existe una. Número: La cantidad de símbolos indica el número mínimo de dígitos. Números menores serán completados con ceros a la izquierda. Sólo el año es manejado en forma especial, si la cantidad de y es 2, el año se trunca a dos dígitos. Texto ó Número: Tres símbolos o más, significa texto, sino número. Ejemplos: Código de ejemplo: Patrón de Formato Resultado "yyyy.mm.dd G at hh:mm:ss z" AD at 15:08:56 PDT "EEE, MMM d, yy" Wed, July 10, 96 "h:mm a" 12:08 PM "hh o clock a, zzzz" 12 o clock PM, Pacific Daylight Time "K:mm a, z" 0:00 PM, PST "yyyyy.mmmmm.dd GGG hh:mm aaa" 1996.July.10 AD 12:08 PM SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm"); Date currenttime1 = new Date(); String datestring = formatter.format(currenttime1); // Parse the previous string back into a Date. ParsePosition pos = new ParsePosition(0); Date currenttime2 = formatter.parse(datestring, pos); En el ejemplo, la fecha currenttime2 es igual a la fecha currenttime La Clase SimpleTimeZone Esta clase es una subclase concreta de TimeZone, que representa una zona horaria para ser usada con el calendario Gregoriano. Su principal funcionalidad es el siguiente constructor: SimpleTimeZone(int rawoffset, String ID); Que construye un SimpleTimeZone con el desplazamiento indicado en milésimas de segundo, de la Hora Media en Greenwich(GMT) e identificador de la zona horaria. Todos los identificadores disponibles pueden ser obtenidos con el método TimeZone.getAvailableIDs(). Normalmente, se debería usar el método TimeZone.getDefault() para crear un TimeZone. Ejemplo, SimpleTimeZone pdt = new SimpleTimeZone(-4 * 60 * 60 * 1000, "Chile/Continental"); Michael Moossen Departamento de Informática. U.T.F.S.M. 73

86 4.5. ENTRADA Y SALIDA DE DATOS 4.5. Entrada y Salida de Datos Archivos y Directorios Un objeto de la clase File puede representar un archivo o un directorio. Tiene los siguientes constructores: File(String name) File(String dir, String name) File(File dir, String name) Para crear un objeto que encapsule un archivo, se puede dar el nombre de un archivo, el nombre y el directorio, o sólo el directorio, como path absoluto y como path relativo al directorio actual. Ejemplos: File f1 = new File("c:/windows/notepad.exe"); File f2 = new File("c:/windows"); // Un directorio File f3 = new File(f2, "notepad.exe"); // Es igual a f1 Notar que Java interpreta el slash(/) en forma correcta, es decir, como un backslash. Para saber si el archivo existe se puede llamar al método exists(). Si File representa un archivo que existe, los siguientes métodos dan información acerca de él: boolean isfile() Retorna true si el archivo existe y no es un directorio. long length() Retorna el tamaño del archivo en bytes. long lastmodified() Retorna la fecha de la última modificación del archivo. boolean canread() Retorna true si el archivo se puede leer. boolean canwrite() Retorna true si el archivo se puede escribir. boolean delete() Intenta borrar el archivo, si tiene éxito, retorna true. boolean renameto(file newfile) Intenta renombrar el archivo actual por el parámetro, retorna true si tiene éxito. Si File representa un directorio, se pueden utilizar los siguientes métodos: boolean isdirectory() Retorna true si existe el directorio. boolean mkdir() Intenta crear el directorio, si tiene éxito retorna true. boolean delete() Intenta borrar el directorio, si tiene éxito retorna true. String[] list() Devuelve una lista de archivos que se encuentran en el directorio. Por último, los siguientes métodos devuelven el path del archivo de distintas maneras. 74 Departamento de Informática. U.T.F.S.M. Michael Moossen

87 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR String getpath() Devuelve el path que contiene el objeto File. String getname() Devuelve el nombre del archivo. String getabsolutepath() Devuelve el path absoluto (concatenando el relativo al actual). String getparent() Devuelve el directorio padre. El siguiente código de ejemplo, muestra todas las funcionalidades de la clase File: import java.io.file; import java.util.date; class FileExample { public static void main(string[] args) { File InFile = new File(args[0]); if (!InFile.exists()) { System.out.println("Archivo "+ InFile.getAbsolutePath() + "no Existe."); System.exit(1); if (InFile.isFile()) { showfile(infile); System.exit(0); if (InFile.isDirectory()) { showdir(infile); System.exit(0); System.out.println("Error Indeterminado:"); System.out.println(InFile.getAbsolutePath()); System.out.println("No es ni Archivo, ni Directorio."); System.exit(2); public static void showfile(file F) { System.out.println("Directorio Padre: "+ F.getParent()); System.out.println("Nombre: "+ F.getName()); System.out.println("Tamaño: "+ F.length() + " Bytes"); System.out.println("Fecha Modificación: "+ (new Date(F.lastModified()))); System.out.println("Puede ser leído: "+ F.canRead()); System.out.println("Puede ser escrito: "+ F.canWrite()); public static void showdir(file F) { System.out.println("Directorio Padre: "+ F.getParent()); System.out.println("Nombre: "+ F.getName()); System.out.println("Contiene "+ F.list().length + " Entradas."); for (int i=0; i<f.list().length; i++) { System.out.println(F.list()[i]); Michael Moossen Departamento de Informática. U.T.F.S.M. 75

88 4.5. ENTRADA Y SALIDA DE DATOS Lectura y Escritura en Archivos de Texto Existen las clases FileReader y FileWriter (extendiendo las clases Reader y Writer) que permiten leer y escribir caracteres desde archivos de texto. Se puede construir un objeto de cualquiera de estas dos clases a partir de un string que contenga el nombre del archivo, o un objeto de la clase File que represente dicho archivo. Por ejemplo, el código: FileReader fr1 = new FileReader("archivo.txt"); es equivalente a: File f = new File("archivo.txt"); FileReader fr2 = new FileReader(f); Si no se encuentra el archivo indicado, los constructores de FileReader pueden lanzar una excepción java.io.filenotfoundexception. Los constructores de FileWriter pueden lanzar java.io.ioexception. Si no encuentran el archivo indicado, crean uno nuevo. Por defecto, esta clase comienza a escribir al comienzo del archivo, reemplazando su contenido anterior. Para escribir detrás de lo que ya existe en el archivo ( append ), se utiliza un segundo argumento de tipo boolean con valor true : FileWriter fw = new FileWriter("archivo.txt", true ); Para terminar, un ejemplo que copia el contenido del archivo "a1.txt" al archivo "a2.txt": FileReader in = new FileReader("a1.txt"); FileWriter out = new FileWriter("a2.txt"); int c; while ((c = in.read())!= -1) out.write(c); in.close(); out.close(); Esto está bien si se desea trabajar con caracteres, pero es bastante poco amigable si se desean leer strings o números, por lo tanto se introducen otras clases que permiten un manejo más fácil y eficiente de archivos de texto. Se puede crear un objeto BufferedReader para leer de un archivo de texto de la siguiente manera: BufferedReader br = new BufferedReader(new FileReader("archivo.txt")); Utilizando el objeto de tipo BufferedReader con el método readline() se puede leer el archivo de línea en línea: String Linea = br.readline(); Así, ya se ha leído una línea del archivo. Y qué hacer con una línea entera? En eso ayuda la clase StringTokenizer vista en la sección que da la posibilidad de separar la línea en las palabras (tokens) que la forman, y cuando sea preciso, se pueden convertir las palabras en números. En el caso de archivos, es muy importante utilizar un buffer, puesto que la tarea de escribir en disco es muy lenta respecto a los procesos del programa y realizar las operaciones de lectura de golpe y no de una en una, hace mucho más eficiente el acceso. Por ejemplo, a continuación se lee un archivo completo que se guarda en la variable texto: 76 Departamento de Informática. U.T.F.S.M. Michael Moossen

89 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR String texto = new String(); try { BufferedReader entrada = new BufferedReader(new FileReader("archivo.txt")); String s; while ((s = entrada.readline())!= null ) texto += s; entrada.close(); catch (java.io.filenotfoundexception fnfex) { System.out.println("Archivo no encontrado: "+ fnfex); En cambio, La clase PrintWriter es más práctica para escribir un archivo de texto, porque posee los métodos print() y println(), al igual que System.out. Un objeto PrintWriter se puede crear a partir de un BufferedWriter (para disponer de buffer), que se crea a partir del FileWriter al que se le pasa el nombre del archivo. Después, escribir en el archivo es tan fácil como en pantalla. El siguiente ejemplo, ilustra lo anterior: try { BufferedWriter bw = new BufferedWriter(new FileWriter("archivo.txt")); PrintWriter salida = new PrintWriter(bw); salida.println("hola, soy la primera línea"); salida.close(); // Modo append bw = new BufferedWriter(new FileWriter("archivo.txt", true )); salida = new PrintWriter(bw); salida.print("y yo soy la segunda."); double b = ; salida.println(b); salida.close(); catch (java.io.ioexception ioex) { ioex.printstacktrace(); Lectura y Escritura en Archivos Binarios Para la manipulación de archivos binarios, Java provee las clases FileInputStream y FileOutputStream para leer y escribir bytes. Su uso es idéntico al de las clases FileReader y FileWriter, por lo tanto a continuación, sólo un ejemplo de su uso para copiar archivos. FileInputStream in = new FileInputStream(new File("a1.txt")); FileOutputStream out = new FileOutputStream(new File("a2.txt")); int c; while ((c = in.read())!= -1) out.write(c); in.close(); out.close(); Michael Moossen Departamento de Informática. U.T.F.S.M. 77

90 4.5. ENTRADA Y SALIDA DE DATOS Archivos de Acceso Aleatorio La clase RandomAccessFile implementa las interfaces DataInput y DataOutput y por lo tanto puede usarse para leer y escribir. Se puede crear con un nombre de archivo o un objeto File. Cuando se crea un RandomAccessFile, se debe indicar si sólo se quiere leer o también se quiere escribir en el archivo (se tiene que poder leer un archivo para poder escribirlo). La siguiente línea de código crea un RandomAccessFile que lee el archivo llamado "archivo.txt": new RandomAccessFile("archivo.txt", "r"); Y esta abre el mismo archivo, tanto para lectura como para escritura: new RandomAccessFile("archivo.txt", "rw"); Después de haber abierto el archivo, se pueden usar los métodos comunes read() o write() para realizar operaciones de entrada y/o salida sobre el archivo. RandomAccessFile soporta la noción de puntero de archivo. Este puntero indica la posición actual en el archivo, cuando el archivo se crea por primera vez, el puntero de archivo es cero, indicando el principio del archivo. Las llamadas a los métodos read() y write() ajustan la posición del puntero de archivo según el número de bytes leídos o escritos. Además de los métodos de entrada y salida normales que implícitamente mueven el puntero de archivo cuando ocurre una operación, RandomAccessFile contiene tres métodos que manipulan explícitamente el puntero de archivo: int skipbytes(int n) throws IOException Mueve el puntero hacia adelante el número de bytes especificado. void seek(long pos) Mueve el puntero de archivo a la posición anterior al byte especificado. long getfilepointer() throws IOException Devuelve la posición actual (byte) del puntero de archivo Serialización de Objetos El paquete java.io tiene otros dos streams de bytes ObjectInputStream y ObjectOutputStream que funcionan como los otros streams de entrada y salida. Sin embargo, son especiales porque pueden leer y escribir objetos. La clave para escribir objetos es representar su estado de una forma suficientemente serializada para reconstruir el objeto cuando es leído. Por eso, leer y escribir objetos es un proceso llamado serialización de objetos. La serialización de objetos, es esencial para construir todo, excepto aplicaciones temporales. Se Puede usar la serialización de objetos de las siguientes formas: Invocación Remota de Métodos(RMI): Comunicación de objetos mediante sockets. Persistencia de Peso Ligero: El archivo de un objeto para una invocación posterior en el mismo programa. Como programador Java, se necesita conocer la serialización de objetos desde dos puntos de vista. Primero, saber como serializar objetos, escribiéndolos en un ObjectOutputStream y luego, leerlos usando un ObjectInputStream. Segundo, saber como escribir una clase para que sus objetos puedan ser serializados. Escribir en un ObjectOutputStream Escribir objetos a un stream es un proceso sencillo. Por ejemplo, 78 Departamento de Informática. U.T.F.S.M. Michael Moossen

91 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR FileOutputStream out = new FileOutputStream("theTime"); ObjectOutputStream s = new ObjectOutputStream(out); s.writeobject("today"); s.writeobject(new Date()); s.flush(); ObjectOutputStream es un stream de proceso, por eso debe construirse sobre otro stream. Este código construye un ObjectOutputStream sobre un FileOutputStream, para serializar el objeto a un archivo llamado "thetime". Luego, el string Today y un objeto Date se escriben en el stream con el método writeobject() de ObjectOutputStream. Si un objeto se refiere a otro objeto, entonces todos los objetos que son alcanzables desde el primero, son escritos al mismo tiempo, para mantener la relación entre ellos. Así, el método writeobject() al serializar el objeto especificado, sigue sus referencias a otros objetos recursivamente, y los escribe todos. El stream ObjectOutputStream implementa la interfaz DataOutput, que define muchos métodos para escribir tipos de datos primitivos, como writeint(), writefloat(), o writeutf(). Por lo tanto, se pueden usar estos métodos para escribir tipos de datos primitivos a un ObjectOutputStream. El método writeobject() lanza una excepción NotSerializableException, si el objeto dado no es serializable. Un objeto es serializable sólo si la clase implementa la interfaz Serializable. Leer desde un ObjectInputStream Una vez que se han escrito objetos y tipos de datos primitivos en un stream, se deseará leerlos y reconstruir los objetos. Esto también es sencillo. Aquí está el código que lee el string y el objeto Date, que se escribieron en el archivo llamado "thetime" del último ejemplo. FileInputStream in = new FileInputStream("theTime"); ObjectInputStream s = new ObjectInputStream(in); String today = (String)s.readObject(); Date date = (Date)s.readObject(); Como ObjectOutputStream, ObjectInputStream debe construirse sobre otro stream. En este ejemplo, los objetos fueron serializados en un archivo, por eso el código construye un ObjectInputStream sobre un FileInputStream. Luego, el código usa el método readobject() de ObjectInputStream, para leer el string y el objeto Date desde el archivo. Los objetos deben ser leídos desde el stream en el mismo orden en que se esribieron. Obsérvese que el valor de retorno de readobject() es un objeto, que es convertido y asignado a un tipo específico. El método readobject() deserializa el siguiente objeto en el stream y revisa sus referencias a otros objetos recursivamente, para deserializar todos los objetos que son alcanzables desde él. De esta forma, se mantiene la relación entre los objetos. El stream ObjectInputStream implementa la interfaz DataInput, que define métodos para leer tipos de datos primitivos. Los métodos de DataInput son comparables a los definidos en DataOutput para escribir tipos de datos primitivos. Entre ellos se incluyen readint(), readfloat(), y readutf(). Estos métodos se usan para leer tipos de datos primitivos desde un ObjectInputStream. Proporcionar Serialización de Objetos Propios Un objeto es serializable sólo si su clase implementa la interfaz Serializable. Así, si se quiere serializar un ejemplar de una clase propia, la clase debe implementar esta interfaz. Esta es una interfaz vacía. Es decir, no contiene ninguna declaración de método; su propósito es simplemente identificar las clases cuyos objetos son serializables. No se tiene que escribir ningún método. La serialización de un objeto de esta clase la maneja el método Michael Moossen Departamento de Informática. U.T.F.S.M. 79

92 4.6. CONECTIVIDAD A BASE DE DATOS (JDBC) defaultwriteobject() de ObjectOutputStream. Este método, escribe cualquier cosa necesaria para reconstruir el objeto de la clase, incluyendo lo siguiente: La clase del Objeto. Los valores para todos los miembros no-transient refieren a otros objetos. y no-static, incluyendo los miembros que se 4.6. Conectividad a Base de Datos (JDBC) A continuación, se presentan los diferentes conceptos implicados en el establecimiento y control de una conexión con bases de datos dentro de una aplicación Java usando Java Database Connection (JDBC). Para que Java pueda conectarse a una Base de datos, se necesita un driver JDBC para el RDBMS usado. Como la API JDBC está predominantemente compuesta por interfaces, se necesita obtener una implementación de un driver JDBC real, para poder conectarse con una base de datos usando JDBC. Si la base de datos no permite el uso de JDBC, siempre se puede usar el driver puente JDBC-ODBC para conectarse con cualquier base de datos (o fuente de datos) que soporte el protocolo ODBC Introducción a los Drivers JDBC Una inspección casual del API JDBC muestra rápidamente la dominación de las interfaces dentro de la API, lo que podría llevar al usuario a preguntarse dónde se realiza el trabajo. Realmente esta es sólo una aproximación que tocan los desarrolladores JDBC, porque la implementación real es la proporcionada por los vendedores de Drivers JDBC, que a su vez proporcionan las clases que implementan las interfaces necesarias. Con todos los drivers disponibles, elegir uno puede ser díficil. Afortunadamente, Sun Microsystems mantiene una base de datos con más de 150 drivers JDBC de una amplia variedad de vendedores. Esta debería ser la primera parada después de seleccionar una base de datos. Ver Desde una perspectiva de programación, hay dos clases principales responsables para el establecimiento de una conexión con una base de datos. La primera clase es DriverManager, que es una de las clases que realmente proprociona la API JDBC. DriverManager, es responsable de manejar un almacén de drivers registrados, esencialmente abstrayendo los detalles del uso de un driver, para que el programador no tenga que tratar con ellos directamente. La segunda clase es la real del Driver JDBC. Estas son proporcionadas por vendedores independientes. La clase Driver de JDBC, es la responsable de establecer la conexión y de manejar todas las comunicaciones con la base de datos. Los drivers JDBC vienen en cuatro tipos diferentes: Drivers de Tipo 1 Los drivers del tipo uno tienen algo en común: todos usan el puente JDBC-ODBC, que está incluido como parte estándar del JDK. Los drivers del tipo uno son diferentes al driver ODBC (Open DataBase Connectivity) adjunto al puente JDBC-ODBC. Para conectar con una fuente de datos diferente, simplemente se tiene que registrar una fuente de datos ODBC diferente, usando el Administrador ODBC, al nombre de la fuente de datos apropiada. Como ODBC se ha estado utilizando desde hace bastante tiempo (más que el lenguaje Java), los drivers ODBC son abundantes. Esto hace de este tipo de drivers una buena elección para aprender a conectar programas Java a bases de datos. De hecho, hay drivers ODBC que permiten asignar fuentes de datos ODBC a una aplicación Microsoft Excel o archivos de texto plano. Sin embargo, el nivel extra de indirección, puede resultar en una pérdida de rendimiento, ya que el JDBC es transferido dentro de ODBC, que luego es transferido en el protocolo específico de la base de datos. Otro problema potencial de los drivers del tipo uno, es su utilización en aplicaciones distribuidas. Como el propio puente no soporta comunicación distribuida, la única forma de que los drivers del tipo uno puedan trabajar a través de la red es, si el propio driver ODBC soporta interacción remota. Para drivers ODBC sencillos, esta no es una opción, y mientras que las grandes bases de datos tienen drivers ODBC que pueden trabajar de forma remota, no pueden competir con el mejor rendimiento de los drivers JDBC puro Java. 80 Departamento de Informática. U.T.F.S.M. Michael Moossen

93 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR El nombre de clase para el driver puente JDBC-ODBC es sun.jdbc.odbc.jdbcodbcdriver y la URL JDBC toma la forma jdbc:odbc:dsn, donde dsn es el Data Source Name (nombre de la fuente de datos) usado para registrar la base de datos con el Administrador ODBC. Drivers de Tipo 2 Los drivers del tipo dos también son conocidos como drivers Java parciales, porque traducen directamente el API JDBC en un API específico de la base de datos. El computador que está ejecutando la JVM debe tener las librerías cliente apropiadas para la base de datos, que podrían incluir código binario nativo, instalado y ejecutándose. Para una aplicación distribuida, este requerimietno puede introducir problemas extra con las licencias, así como posibles pesadillas con los problemas de distribución de código. Por ejemplo, usar un modelo del tipo dos, restringe a los desarrolladores a utilizar plataformas y sistemas operativos soportados por la librería cliente de la base de datos. Sin embargo, este modelo puede funcionar eficientemente, cuando la base cliente está fuertemente controlada. Esto ocurre típicamente en LANs corporativas. Drivers de Tipo 3 Los dos últimos tipos de drivers, son drivers puro Java. El beneficio de los drivers puro Java es su fácil despliegue en entornos altamente distribuidos. Los drivers del tipo tres son drivers puro Java que transforman el API JDBC en un protocolo independiente de la base de datos. El driver JDBC no comunica directamente con la base de datos; comunica con un servidor de capa media, que a su vez comunica con la base de datos. Este nivel extra de indirección proporciona flexibilidad, ya que se puede acceder a diferentes bases de datos desde el mismo código, porque el servidor de la capa media oculta los detalles a la aplicación Java. Para cambiar a una base de datos diferente, sólo se necesita cambiar los parámetros en el servidor de la capa media. (Un punto a tener en cuenta es que, el formato de la base de datos a la que se está accediendo, debe ser soportado por el servidor de la capa media). El lado negativo de los drivers del tipo tres, es que el nivel extra de indirección puede perjudicar el rendimiento general del sistema. Por otro lado, si una aplicación necesita interactuar con una variedad de formatos de bases de datos, un driver del tipo tres, es una aproximación adecuada debido al hecho de que se usa el mismo driver JDBC, sin importar la base de datos subyacente. Drivers de Tipo 4 Los drivers del tipo cuatro, son drivers puro Java que se comunican directamente con la base de datos. Muchos programadores consideran éste el mejor tipo de driver, ya que normalmente proporciona un rendimiento óptimo y permite al desarrollador utilizar las funcionalidades específicas de la base de datos. Por supuesto este acoplamiento puede reducir la flexibilidad, especialmente si se necesita cambiar la base de datos subyacente en una aplicación. Este tipo de driver se usa frecuentemente en applets y otras aplicaciones altamente distribuidas Registrar un Driver JDBC: La Clase DriverManager El primer paso en el proceso de crear una conexión entre una aplicación Java y una base de datos es el registro de un driver JDBC con la JVM en la que se está ejecutando la aplicación Java. En el mecanismo de conexión tradicional, la conexión y toda la comunicación con la base de datos, es controlada por un objeto DriverManager. Para establecer una conexión, se debe registrar un driver JDBC adecuado para la base de datos objetivo, con el objeto DriverManager. La especificación JDBC, dice que los drivers JDBC se registran automáticamente a sí mismos con el objeto DriverManager cuando se cargan en la JVM. Registrar un driver es tan simple, como cargar la clase del driver en la JVM, lo que puede hacerse de muchas maneras. Una forma es, con el ClassLoader: Class.forName(), es la más conocida y la que se usará en este documento. Ejemplo: Michael Moossen Departamento de Informática. U.T.F.S.M. 81

94 4.6. CONECTIVIDAD A BASE DE DATOS (JDBC) try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver").newInstance(); catch (ClassNotFoundException e) { System.err.println(e.getMessage()); Esta alternativa debería funcionar bien en todas las JVMs. Es necesario manejar flujos alternativos, ya que Class.forName() puede lanzar una excepción ClassNotFoundException, por eso se debe envolver el código de registro en un manejador de excepción apropiado La Conexión: La Clase Connection y URLs de Conexión Una vez que un Driver JDBC se ha registrado con el DriverManager, puede usarse para establecer una conexión a una base de datos. Pero cómo selecciona DriverManager el driver correcto, dado que puede haberse registrado más de un driver? (Además, una sóla JVM puede soportar múltiples aplicaciones concurrentes, que podrían conectarse con diferentes bases de datos con diferentes drivers). La técnica es bastante simple: cada driver JDBC usa una URL JDBC específica (que tiene un formato parecido al de una dirección Web) como un significado de auto-identificación. El formato de la URL es específico del driver JDBC. Pero todos incluyen un localizador específico del driver para especificar de forma única la base de datos con la que una aplicación quiere interactúar. Dependiendo del tipo de driver, este localizador incluye un nombre de host, un puerto, y un nombre de sistema de base de datos. Cuando se encuentra con una URL específica, el DriverManager itera sobre la colección de drivers registrados hasta que uno de ellos reconoce la URL específicada. Si no se encuentra ningún driver adecuado, se lanza una SQLException. La siguiente lista presenta varios ejemplos específicos de URLs JDBC reales: jdbc:odbc:dsn jdbc:db2:database Muchos drivers, incluyendo el driver puente JDBC-ODBC, acepta parámetros adicionales al final de la URL como un nombre de usuario y una password. El método para obtener una conexión a una base de datos, dando una URL JDBC específica, es llamar a getconnection() sobre el objeto DriverManager. Este método tiene varias formas: DriverManager.getConnection(url); DriverManager.getConnection(url, username, password); DriverManager.getConnection(url, dbproperties); Aquí, url es un objeto String que es la URL JDBC, username y password son objetos String que representan el nombre de usuario y la password que la aplicación JDBC debería usar para conectarse con la fuente de datos; y dbproperties es un objeto Properties de Java, que encapsula todos los parámetros (posiblemente incluyendo nombre de usuario y la password) que requiere un driver JDBC para hacer una conexión con éxito. Así, se puede crear una conexión concreta a la Base de Datos con el siguiente código: Connection con = null ; try { con = DriverManager.getConnection("jdbc:odbc:dsn"); catch (SQLException e) { System.err.println(e.getMessage()); El método getconnection() puede lanzar una SQLException, si se produce algún error al conectarse a la base de datos. 82 Departamento de Informática. U.T.F.S.M. Michael Moossen

95 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR Ejecutar una Instrucción SQL: La Clase Statement Un objeto Statement es el que envía sentencias SQL al controlador de la base de datos. Simplemente, se crea un objeto Statement y se ejecuta, suministrando el método SQL apropiado con la sentencia SQL que se quiere enviar. Para una sentencia que retorne registros, el método a ejecutar es executequery(). Para sentencias que crean o modifican estructuras, el método a utilizar es executeupdate(). Se toma un ejemplar de una conexión activa para crear un objeto Statement. En el siguiente ejemplo, se utiliza el objeto con, de la sección anterior, para crear el objeto stmt. Statement stmt = null ; try { stmt = con.createstatement(); catch (SQLException e) { System.err.println(e.getMessage()); En este momento stmt existe, pero no tiene ninguna sentencia SQL que pasarle al controlador de la base de datos. Se necesita ejecutar un método que ejecute código SQL. Por ejemplo, en el siguiente fragmento de código, se utiliza executeupdate() con una sentencia SQL, que crea una nueva tabla: try { stmt.executeupdate("create TABLE COFFEES "+ "(COF NAME VARCHAR(32), SUP ID INTEGER, PRICE FLOAT, "+ "SALES INTEGER, TOTAL INTEGER)"); catch (SQLException e) { System.err.println(e.getMessage()); Se utilizó el método executeupdate() porque la sentencia SQL es una sentencia DDL (Data Definition Language). Las sentencias que crean, modifican o eliminan estructuras son todas ejemplos de sentencias DDL y se ejecutan con el método executeupdate(). Como se podría esperar de su nombre, el método executeupdate() también se utiliza para ejecutar sentencias SQL que actualizan registros en una tabla, como INSERT y UPDATE. El siguiente código inserta una fila de datos. Se utilizará el mismo objeto stmt que se creó en el ejemplo anterior. Observe que se utilizan comillas simples alrededor del nombre del café, porque está anidado dentro de las comillas dobles. Para la mayoría de los controladores de bases de datos, la regla general es alternar comillas dobles y simples. try { stmt.executeupdate( "INSERT INTO COFFEES "+ "VALUES ( Colombian, 101, 7.99, 0, 0)"); catch (SQLException e) { System.err.println(e.getMessage()); Si no se desea seguir usando un objeto Statement, es necesario ejecutar el método close() para liberar los recursos Obtención de Resultados: La Interfaz ResultSet El método más utilizado para ejecutar sentencias SQL es executequery(). Este método se utiliza, principalmente para ejecutar sentencias SELECT, que comprenden la amplia mayoría de las sentencias SQL. JDBC devuelve los resultados en un objeto ResultSet, por eso se necesita declarar un ejemplar de la clase ResultSet para contener los resultados. El siguiente código presenta el objeto ResultSet, al que se le asigna el resultado de una consulta. Michael Moossen Departamento de Informática. U.T.F.S.M. 83

96 4.6. CONECTIVIDAD A BASE DE DATOS (JDBC) ResultSet rs = null ; try { rs = stmt.executequery("select COF NAME, PRICE FROM COFFEES"); catch (SQLException e) { System.err.println(e.getMessage()); La variable rs, que es un ejemplar de ResultSet, contiene las filas de cafés y sus precios. Para acceder a los nombres y los precios, se recorren las filas y se recuperan los valores de acuerdo con sus tipos. El método next() mueve un cursor a la siguiente fila y hace que esa sea la actual. Como el cursor inicialmente se posiciona justo antes de la primera fila de un objeto ResultSet, primero se debe llamar al método next() para mover el cursor a la primera fila y convertirla en la fila actual. Sucesivas invocaciones del método next(), moverán el cursor de fila en fila. La interfaz ResultSet provee de una serie de métodos getxxx() del tipo apropiado, que se utilizan para recuperar el valor de cada columna. Por ejemplo, la primera columna de cada fila de rs es COF NAME, que almacena un valor del tipo VARCHAR de SQL. El método para recuperar un valor VARCHAR es getstring(). La segunda columna de cada fila almacena un valor del tipo FLOAT de SQL, y el método para recuperar valores de ese tipo es getfloat(). El siguiente código accede a los valores almacenados en la fila actual de rs, e imprime una línea con el nombre seguido por tres espacios y el precio. Cada vez que se llama al método next(), la siguiente fila se convierte en la actual, y el bucle continúa hasta que no haya más filas por visitar en rs. String query = "SELECT COF NAME, PRICE FROM COFFEES"; try { ResultSet rs = stmt.executequery(query); while (rs.next()) { String s = rs.getstring("cof NAME"); float n = rs.getfloat("price"); System.out.println(s + " " + n); catch (SQLException e) { System.err.println(e.getMessage()); La salida se parecerá a esto. Colombian 7.99 French Roast 8.99 Espresso 9.99 Colombian Decaf 8.99 French Roast Decaf 9.99 El método getstring() es invocado sobre el objeto rs, por eso getstring() recuperará el valor almacenado en la columna COF NAME de la fila actual de rs. El valor recuperado por getstring() se ha convertido desde un VARCHAR de SQL a un String de Java y se ha asignado al objeto s. La situación es similar con el método getfloat() excepto en que recupera el valor almacenado en la columna PRICE, que es un FLOAT de SQL, y lo convierte a un float de Java antes de asignarlo a la variable n. JDBC ofrece dos formas para identificar la columna de la que un método getxxx() obtiene un valor. Una forma es dar el nombre de la columna, como se ha hecho anteriormente. La segunda forma, es dar el índice de la columna (el número de columna), con un 1 significando la primera columna, un 2 para la segunda, etc. Si se utilizara el número de columna, en vez del nombre de columna el código anterior quedaría así: String s = rs.getstring(1); float n = rs.getfloat(2); 84 Departamento de Informática. U.T.F.S.M. Michael Moossen

97 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR Tener en cuenta que el número de columna se refiere al número de columna en la hoja de resultados, y no en la tabla original. En suma, JDBC permite utilizar tanto el nombre como el número de la columna como argumento a un método getxxx(). Utilizar el número de columna es un poco más eficiente, y hay algunos casos donde es necesario utilizarlo. La siguiente tabla muestra que método getxxx() se recomienda usar para cada tipo de dato SQL. Tipo de dato SQL TINYINT SMALLINT INTEGER BIGINT REAL FLOAT DOUBLE DECIMAL NUMERIC BIT CHAR LONGCARCHAR BINARY VARBINARY LONGVARBINARY DATE TIME TIMESTAMP VARCHAR Método Recomendado getbyte() getshort() getint() getlong() getfloat() getdouble() getdouble() getbigdecimal() getbigdecimal() getboolean() getstring() getstring() getasciistream() getbytes() getbytes() getdate() gettime() gettimestamp() getstring() Si no se desea seguir usando un objeto ResultSet, es necesario ejecutar el método close() para liberar los recursos Manejo de Transacciones Hay veces en que no se quiere que una sentencia tenga efecto, a menos que otra también suceda. Por ejemplo, cuando se actualiza la cantidad de café vendida semanalmente, también se deseará actualizar la cantidad total vendida hasta la fecha. Sin embargo, no se deseará actualizar una, sin actualizar la otra; de otro modo, los datos serían inconsistentes. La forma para asegurarse que ocurren las dos acciones o que no ocurre ninguna, es utilizar una transacción. Una transacción es un conjunto de una o más sentencias que se ejecutan como una unidad, por eso, o se ejecutan todas o no se ejecuta ninguna. Cuando se crea una conexión, está en modo de auto-entrega. Esto significa que cada sentencia SQL individual es tratada como una transacción y será automáticamente entregada justo después de ser ejecutada. La forma de permitir que dos o más sentencia sean agrupadas en una transacción, es desactivar el modo de autoentrega. Esto se demuestra en el siguiente código, donde con es una conexión activa. con.setautocommit(false ); Una vez que se ha desactivado la auto-entrega, no se entregará ninguna sentencia SQL hasta que se llame explícitamente al método commit(). Todas las sentencias ejecutadas después de la llamada al método setautocommit(false ) y antes de la llamada al método commit(), serán incluidas en la transacción actual y serán entregadas juntas como una unidad. El siguiente código, en el que con es una conexión activa, ilustra una transacción. con.setautocommit(false ); Statement updatesales = con.createstatement(); Michael Moossen Departamento de Informática. U.T.F.S.M. 85

98 4.6. CONECTIVIDAD A BASE DE DATOS (JDBC) updatesales.executeupdate("update COFFEES SET SALES = 50 "+ "WHERE COF NAME LIKE Colombian "); Statement updatetotal = con.createstatement("update COFFEES "+ "SET TOTAL = TOTAL + 50 WHERE COF NAME LIKE Colombian "); updatetotal.executeupdate(); con.commit(); con.setautocommit(true ); En este ejemplo, el modo auto-entrega se desactiva para la conexión con, lo que significa que las dos sentencias updatesales y updatetotal serán entregadas juntas cuando se llame al método commit(). Siempre que se llame al método commit() (bien automáticamente, cuando está activado el modo auto-commit o explícitamente cuando está desactivado), todos los cambios resultantes de las sentencias de la transacción serán permanentes. En este caso, significa que las columnas SALES y TOTAL para el café Colombian han sido cambiadas a 50 (si TOTAL ha sido 0 anteriormente) y mantendrá este valor hasta que se cambie con otra sentencia de actualización. La línea final del ejemplo anterior, activa el modo auto-commit, lo que significa que cada sentencia será de nuevo entregada automáticamente cuando esté completa. Se vuelve, por lo tanto al estado por defecto, en el que no se tiene que llamar al método commit(). Es conveniente desactivar el modo auto-commit, sólo mientras se quiera estar en modo de transacción. De esta forma, se evita bloquear la base de datos durante varias sentencias. Además de agrupar las sentencias para ejecutarlas como una unidad, las transaciones pueden ayudar a preservar la integridad de los datos de una tabla. Por ejemplo, suponga que un empleado se ha propuesto introducir los nuevos precios de los cafés en la tabla COFFEES, pero se retrasa algunos días. Mientras tanto, los precios han subido, y hoy el propietario está introduciendo los nuevos precios. Finalmente, el empleado empieza a introducir los precios, ahora desfasados, al mismo tiempo que el propietario intenta actualizar la tabla. Después de insertar los precios desfasados, el empleado se da cuenta de que ya no son válidos y llama el método rollback() de la conexión para deshacer sus efectos. El método rollback() aborta la transacción y restaura los valores que había antes de intentar la actualziación. Al mismo tiempo, el propietario está ejecutando una sentencia SELECT e imprime los nuevos precios. En esta situación, es posible que el propietario imprima los precios que más tarde serían devueltos a sus valores anteriores, haciendo que los precios impresos sean incorrectos. Esta clase de situaciones puede evitarse utilizando transaciones. Si un controlador de base de datos soporta transaciones, y casi todos lo hacen, proporcionará algún nivel de protección contra conflictos, que pueden surgir cuando dos usuarios acceden a los datos a la misma vez. Para evitar conflictos durante una transacción, un controlador de base de datos utiliza bloqueos, mecanismos para bloquear el acceso de otros a los datos que están siendo accedidos por una transacción. Una vez activado, el bloqueo permanece hasta que la transacción sea entregada o anulada. Por ejemplo, un controlador de base de datos podría bloquear una fila de una tabla, hasta que la actualización se haya entregado. El efecto de este bloqueo es evitar que un usuario obtenga una lectura sucia, esto es, que lea un valor antes de que sea permanente (acceder a un valor actualizado que no haya sido entregado, se considera una lectura sucia, porque es posible que el valor sea devuelto a su valor anterior). La forma en que se configuran los bloqueos, está determinado por lo que se llama, nivel de aislamiento de transacción, que puede variar desde no soportar transaciones en absoluto, a soportar todas las transaciones que fuerzan unas reglas de acceso estrictas. Un ejemplo de nivel de aislamiento de transacción, es TRANSACTION READ COMMITTED, que no permite que se acceda a un valor hasta que haya sido entregado. En otras palabras, si el nivel de aislamiento de transacción se selecciona a TRANSACTION READ COMMITTED, el controlador de la base de datos no permitirá que ocurran lecturas sucias. La interfaz Connection incluye cinco valores que representan los niveles de aislamiento de transacción que se pueden utilizar en JDBC. Normalmente, no se necesita cambiar el nivel de aislamiento de transacción; se puede utilizar el valor por defecto del controlador. JDBC permite averiguar el nivel de aislamiento de transacción del controlador de la base de datos (utilizando el método gettransactionisolation()) y permite configurarlo a otro nivel (utilizando el método settransactionisolation()). Sin embargo, hay que tener en cuenta, que aunque JDBC permite seleccionar un nivel de aislamiento, hacer esto no tendrá ningún efecto, a no ser que el driver del controlador de la base de datos lo soporte. 86 Departamento de Informática. U.T.F.S.M. Michael Moossen

99 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR 4.7. Hilos y Sincronización Los procesadores y los sistemas operativos modernos permiten procesamiento multitarea, es decir, la realización simultánea de dos o más actividades (al menos aparentemente). En la realidad, un computador con una sola CPU no puede realizar dos actividades a la vez. Sin embargo, los sistemas operativos actuales son capaces de ejecutar varios programas simultáneamente aunque sólo se disponga de una CPU: reparten el tiempo entre dos (o más) actividades, o bien utilizan los tiempos muertos de una actividad (por ejemplo, operaciones de lectura de datos desde el teclado) para trabajar en la otra. En computadores con dos o más procesadores la multitarea es real, ya que cada procesador puede ejecutar un hilo o thread diferente. Un proceso es un programa ejecutándose de forma independiente y con un espacio propio de memoria. Un sistema operativo multitarea es capaz de ejecutar más de un proceso simultáneamente. Un thread o hilo es un flujo secuencial simple dentro de un proceso. Un único proceso puede tener varios hilos ejecutándose. Por ejemplo, el programa Netscape puede ser un proceso, mientras que cada una de las ventanas que se pueden tener abiertas simultáneamente mostrando páginas HTML, estaría formada por al menos un hilo. Un sistema multitarea da realmente la impresión de estar haciendo varias cosas a la vez, y eso es una gran ventaja para el usuario. Sin el uso de threads, hay tareas que son prácticamente imposibles de ejecutar, particularmente las que tienen tiempos de espera importantes entre etapas. Los threads o hilos permiten organizar los recursos del computador de forma que pueda haber varios programas actuando en paralelo. Un hilo puede realizar cualquier tarea que pueda realizar un programa normal y corriente. Los threads pueden ser daemon o no daemon. Son daemon aquellos hilos que realizan en background (en un segundo plano) servicios generales, esto es, tareas que no forman parte de la esencia de un programa y que se están ejecutando mientras no finalice una aplicación. Un thread daemon podría ser, por ejemplo aquél que está comprobando permanentemente si el usuario pulsa un botón. Un programa Java finaliza cuando sólo quedan corriendo threads de tipo daemon. Por defecto, y si no se indica lo contrario, los threads son del tipo no daemon Creación de Hilos En Java hay dos formas de crear nuevos threads. La primera de ellas consiste en crear una nueva clase que herede de la clase java.lang.thread y sobrecargar el método run() de dicha clase. El segundo método, consiste en declarar una clase que implemente la interfaz java.lang.runnable, la cual declarará el método run(); posteriormente se crea un objeto de tipo Thread, pasándole como argumento al constructor, el objeto creado de la nueva clase (la que implementa la interfaz Runnable). A continuación se presentan dos ejemplos de creación de threads con cada uno de los dos métodos citados. Creación de Threads especializando la Clase Thread Considérese el siguiente ejemplo de declaración de una nueva clase: public class SimpleThread extends Thread { // constructor public SimpleThread(String str) { super(str); // redefinición del método run() public void run() { for (int i=0;i<10;i++) System.out.println("Este es el thread : "+ getname()); En este caso, se ha creado la clase SimpleThread, que hereda de Thread. En su constructor se utiliza un string (opcional) para poner nombre al nuevo thread creado, y mediante super() se llama al constructor de la super-clase Michael Moossen Departamento de Informática. U.T.F.S.M. 87

100 4.7. HILOS Y SINCRONIZACIÓN Thread. Asimismo, se redefine el método run(), que define la principal actividad del thread, para que escriba 10 veces el nombre del thread creado. Para poner en marcha este nuevo thread, se debe crear un objeto de la clase SimpleThread, y llamar al método start(),heredado de la super-clase Thread, que se encarga de llamar a run(). Por ejemplo: SimpleThread mithread = new SimpleThread("Hilo de prueba"); mithread.start(); Creación de Threads implementando la Interfaz Runnable Esta segunda forma también requiere que se defina el método run(), pero además es necesario crear un objeto de la clase Thread para lanzar la ejecución del nuevo hilo. Al constructor de la clase Thread se le debe pasar una referencia del objeto de la clase que implementa la interface Runnable. Posteriormente, cuando se ejecute el método start() del thread, este llamará al método run() definido en la nueva clase. A continuación, se muestra el mismo estilo de clase que en el ejemplo anterior, implementada mediante la interface Runnable: public class SimpleRunnable implements Runnable { // se crea un nombre String namethread; // constructor public SimpleRunnable(String str) { namethread = str; // definición del método run() public void run() { for (int i=0;i<10;i++) System.out.println("Este es el thread : "+ namethread); El siguiente código crea un nuevo thread y lo ejecuta por este segundo procedimiento: SimpleRunnable p = new SimpleRunnable("Hilo de prueba"); // se crea un objeto de la clase Thread pasándole el objeto Runnable como argumento Thread mithread = new Thread(p); // se arranca el objeto de la clase Thread mithread.start(); Este segundo método cobra especial interés con applets, ya que cualquier applet debe heredar de la clase java.applet.applet, y por lo tanto ya no puede heredar de Thread. La elección de una u otra forma derivar de Thread o implementar Runnable depende del tipo de clase que se vaya a crear. Así, si la clase a utilizar ya hereda de otra clase (por ejemplo un applet), no quedará más remedio que implementar Runnable, aunque normalmente es más sencillo heredar de Thread Ciclo de Vida de un Thread En la sección anterior se ha visto como crear nuevos objetos que permiten incorporar en un programa la posibilidad de realizar varias tareas simultáneamente. Un thread puede presentar cuatro estados distintos: Nuevo (New): El thread ha sido creado pero no inicializado, es decir, no se ha ejecutado todavía el método start(). Se producirá un mensaje de error (IllegalThreadStateException) si se intenta ejecutar cualquier método de la clase Thread distinto de start(). 88 Departamento de Informática. U.T.F.S.M. Michael Moossen

101 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR Ejecutable (Runnable): El thread puede estar ejecutándose, siempre y cuando se le haya asignado un determinado tiempo de CPU. En la práctica, puede no estar siendo ejecutado en un instante determinado, en beneficio de otro thread. Bloqueado (Blocked o Not Runnable): El thread podría estar ejecutándose, pero alguna actividad interna suya se lo impide, como por ejemplo, una espera producida por una operación de escritura o lectura de datos por teclado(e/s). Si un thread está en este estado, no se le asigna tiempo de CPU. Muerto (Dead): La forma habitual de que un thread muera es finalizando el método run(). También puede llamarse al método stop() de la clase Thread, aunque dicho método es considerado peligroso y no se debe utilizar. A continuación se explicarán con mayor detenimiento los puntos anteriores. Ejecución de un Nuevo thread La creación de un nuevo thread no implica necesariamente que se empiece a ejecutar código. Hace falta iniciarlo con el método start(), ya que de otro modo, cuando se intenta ejecutar cualquier método del thread distinto del método start() se obtiene en tiempo de ejecución el error IllegalThreadStateException. El método start() se encarga de llamar al método run() de la clase Thread. Si el nuevo thread se ha creado heredando de la clase Thread, la nueva clase deberá redefinir el método run() heredado. En el caso de utilizar una clase que implemente la interfaz Runnable, el método run() de la clase Thread se ocupa de llamar al método run() de la nueva clase. Una vez que el método start() ha sido llamado, se puede decir que el thread ya está corriendo (running), lo cual no quiere decir que se esté ejecutando en todo momento, pues ese thread tiene que compartir el tiempo de CPU con los demás threads que también estén corriendo. Detener un Thread Temporalmente El sistema operativo se ocupa de asignar tiempos de CPU a los distintos threads que se estén ejecutando simultáneamente. Aún en el caso de disponer de un computador con más de un procesador (2 ó más CPUs), el número de threads simultáneos suele siempre superar el número de CPUs, por lo que se debe repartir el tiempo de forma que parezca que todos los procesos corren a la vez (quizás más lentamente), aún cuando sólo unos pocos pueden estar ejecutándose en un instante de tiempo. Los tiempos de CPU que el sistema continuamente asigna a los distintos threads en estado runnable, se utilizan en ejecutar el método run() de cada thread. Por diversos motivos, un thread puede en un determinado momento renunciar voluntariamente a su tiempo de CPU y otorgárselo al sistema para que se lo asigne a otro thread. Esta renuncia se realiza mediante el método yield(). Es importante que este método sea utilizado por las actividades que tienden a monopolizar la CPU. El método yield() viene a indicar, que en ese momento no es muy importante para ese thread el ejecutarse continuamente, y por lo tanto tener ocupada la CPU. En caso de que ningún thread esté solicitando CPU para una actividad intensiva, el sistema volverá casi de inmediato a asignar nuevo tiempo al thread que fue generoso con los demás. Por ejemplo, en un Pentium II 400 Mhz es posible llegar a más de medio millón de llamadas por segundo al método yield(), dentro del método run(), lo que significa que llamar al método yield() apenas detiene al thread, sino que sólo ofrece el control de la CPU para que el sistema decida si hay alguna otra tarea que tenga mayor prioridad. Si lo que se desea es parar o bloquear temporalmente un thread (pasar al estado Bloqueado), existen varias formas de hacerlo: Ejecutando el método sleep() de la clase Thread. Esto detiene el thread un tiempo preestablecido. Generalmente, el método sleep() se llama desde el método run(). Ejecutando el método wait() heredado de la clase Object, a la espera de que suceda algo que es necesario para poder continuar. El thread volverá nuevamente a la situación de runnable, mediante los métodos notify() o notifyall(), que se deberán ejecutar cuando cesa la condición que tiene detenido al thread. Michael Moossen Departamento de Informática. U.T.F.S.M. 89

102 4.7. HILOS Y SINCRONIZACIÓN Cuando el thread está esperando para realizar operaciones de Entrada/Salida(E/S). Cuando el thread está tratando de llamar a un método synchronized de un objeto, y dicho objeto está bloqueado por otro thread. Un thread pasa automáticamente del estado Bloqueado a Corriendo cuando cesa alguna de las condiciones anteriores o cuando se llama a notify() o notifyall(). La clase Thread dispone también de un método stop(), pero no se debe utilizar, ya que puede provocar bloqueos del programa(deadlock). Hay una última posibilidad para detener un thread, que consiste en ejecutar el método suspend(). El thread volverá a ser ejecutable de nuevo ejecutando el método resume(). Esta última forma tampoco se aconseja, por razones similares a la utilización del método stop(). El método sleep() de la clase Thread recibe como argumento el tiempo en milisegundos que ha de permanecer detenido. Adicionalmente, se puede incluir un número entero con un tiempo adicional en nanosegundos. Las declaraciones de estos métodos son las siguientes: static void sleep(long millis) throws InterruptedException static void sleep(long millis, int nanosecons) throws InterruptedException Considérese el siguiente ejemplo: System.out.println("Contador de segundos"); int count=0; public void run() { try { sleep(1000); System.out.println(count++); catch (InterruptedException e) { Se observa que el método sleep() puede lanzar una InterruptedException que ha de ser capturada. Así, se ha hecho en este ejemplo, aunque luego no se gestiona esa excepción. La forma preferible de detener temporalmente un thread, es la utilización conjunta de los métodos wait() y notifyall(). La principal ventaja del método wait() frente a los métodos anteriormente descritos, es que libera el bloqueo del objeto, por lo que el resto de threads que se encuentran esperando para actuar sobre dicho objeto, pueden llamar a sus métodos. Hay dos formas de llamar a wait(): Indicando el tiempo máximo que debe estar detenido (en milisegundos y con la opción de indicar también nanosegundos), de forma análoga a sleep(). A diferencia del método sleep(), que simplemente detiene el thread el tiempo indicado, el método wait() establece el tiempo máximo que debe estar detenido. Si en ese plazo se ejecutan los métodos notify() o notifyall() que indican la liberación de los objetos bloqueados, el thread continuará sin esperar a concluir el tiempo indicado. Las dos declaraciones del método wait() son como siguen: final void wait(long timeout) throws InterruptedException final void wait(long timeout, int nanos) throws InterruptedException Sin argumentos, en cuyo caso el thread permanece detenido hasta que sea reinicializado explícitamente mediante los métodos notify() o notifyall(). final void wait() throws InterruptedException Las llamadas a los métodos wait() y notify() han de estar incluidas en un método synchronized, ya que de otra forma se obtendrá una excepción del tipo IllegalMonitorStateException, en tiempo de ejecución. El uso típico de wait(), es el de esperar a que se cumpla alguna determinada condición, ajena al propio thread. Cuando esta se cumpla, se utiliza el método notifyall() para avisar a los distintos threads que pueden utilizar el objeto. 90 Departamento de Informática. U.T.F.S.M. Michael Moossen

103 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR Finalizar un Thread Un thread finaliza cuando el método run() devuelve el control, por haber terminado lo que tenía que hacer (por ejemplo, un bucle for que se ejecuta un número determinado de veces) o por haberse dejado de cumplir una condición (por ejemplo, por un bucle while en el método run()). Para saber si un thread está vivo o no, es útil el método isalive() de la clase Thread, que devuelve true si el thread ha sido inicializado y no detenido, y false si el thread es todavía nuevo(no ha sido inicializado) o ha finalizado Sincronización La sincronización nace de la necesidad de evitar que dos o más threads traten de acceder a los mismos recursos al mismo tiempo. Así, por ejemplo, si un thread tratara de escribir en un archivo, y otro thread estuviera al mismo tiempo tratando de borrarlo, se produciría una situación no deseada. Otro caso en el que hay que sincronizar threads, se produce cuando un thread debe esperar a que estén preparados los datos que le debe suministrar otro thread. Para solucionar estos tipos de problemas es importante poder sincronizar los distintos threads. Las secciones de código de un programa que acceden a un mismo recurso(un mismo objeto de una clase, un archivo del disco, etc.) desde dos threads distintos, se denominan secciones críticas. Para sincronizar dos o más threads, hay que utilizar el modificador synchronized en aquellos métodos del objeto-recurso con los que puedan producirse situaciones conflictivas. De esta forma, Java bloquea(asocia un bloqueo o lock) el recurso sincronizado. Por ejemplo: public synchronized void metodosincronizado() { // acceder, por ejemplo, a las variables de un objeto... La sincronización previene las interferencias solamente sobre un tipo de recurso: la memoria reservada para un objeto. Cuando se prevea que unas determinadas variables de una clase pueden tener problemas de sincronización, se deberán declarar como private (o protected ). De esta forma, sólo podrán estar accesibles a través de métodos de la clase, que deberán estar sincronizados. Es importante tener en cuenta que si se sincronizan algunos métodos de un objeto pero otros no, el programa puede no funcionar correctamente. La razón, es que los métodos no sincronizados pueden acceder libremente a las variables miembro, ignorando el bloqueo del objeto. Sólo los métodos sincronizados comprueban si un objeto está bloqueado. Por lo tanto, todos los métodos que accedan a un recurso compartido deben ser declarados synchronized. De esta forma, si algún método accede a un determinado recurso, Java bloquea dicho recurso, de forma que el resto de threads no puedan acceder al mismo hasta que el primero en acceder termine de realizar su tarea. Bloquear un recurso u objeto significa que sobre ese objeto no pueden actuar simultáneamente dos métodos sincronizados. Existen dos niveles de bloqueo de un recurso. El primero, es a nivel de objetos, mientras que el segundo es a nivel de clases. El primero se consigue declarando todos los métodos de una clase como synchronized. Cuando se ejecuta un método synchronized sobre un objeto concreto, el sistema bloquea dicho objeto, de forma que si otro thread intenta ejecutar algún método sincronizado de ese objeto, este segundo método se mantendrá a la espera hasta que finalice el anterior (y desbloquee por lo tanto el objeto). Si existen varios objetos de una misma clase, como los bloqueos se producen a nivel de objeto, es posible tener distintos threads ejecutando métodos sobre diversos objetos de una misma clase. El bloqueo de recursos a nivel de clases se corresponde con los métodos y las variables de clase o estáticas. Si lo que se desea es conseguir que un método bloquee simultáneamente una clase entera, es decir, todos los objetos creados de una clase, es necesario declarar este método como synchronized static. Durante la ejecución de un método declarado de esta segunda forma, ningún método sincronizado tendrá acceso a ningún objeto de la clase bloqueada. La sincronización puede ser problemática y generar errores. Un thread podría bloquear un determinado recurso de Michael Moossen Departamento de Informática. U.T.F.S.M. 91

104 4.7. HILOS Y SINCRONIZACIÓN forma indefinida, impidiendo que el resto de threads accedan al mismo. Para evitar esto último, habrá que utilizar la sincronización sólo donde sea estrictamente necesario. Es conveniente tener presente, que si dentro de un método sincronizado se utiliza el método sleep() de la clase Thread, el objeto bloqueado permanecerá en ese estado durante el tiempo indicado en el argumento de dicho método. Esto implica que otros threads no podrán acceder a ese objeto durante ese tiempo, aunque en realidad no exista peligro de simultaneidad, ya que durante ese tiempo el thread que mantiene bloqueado el objeto no realizará cambios. Para evitarlo es conveniente sustituir sleep() por el método wait() de la clase java.lang.object heredado automáticamente por todas las clases. Cuando se llama al método wait() (siempre debe hacerse desde un método o bloque synchronized ) se libera el bloqueo del objeto y por lo tanto es posible continuar utilizando ese objeto a través de métodos sincronizados. El método wait() detiene el thread hasta que se llame al método notify() o notifyall() del objeto, o finalice el tiempo indicado como argumento del método wait(). El método notify() lanza una señal indicando al sistema que puede activar uno de los threads que se encuentren bloqueados esperando para acceder al objeto. El método notifyall() lanza una señal a todos los threads que están esperando la liberación del objeto. Los métodos notify() y notifyall() deben ser llamados desde el thread que tiene bloqueado el objeto para activar el resto de threads que están esperando la liberación de un objeto. Un thread se convierte en propietario del bloqueo de un objeto, ejecutando un método sincronizado del objeto. Los bloqueos de tipo clase, se consiguen ejecutando un método de clase sincronizado (synchronized static ). Véanse las dos funciones siguientes: put() inserta un dato y get() lo recoge: public synchronized int get() { while (available == false ) { try { // Espera a que put() asigne el valor y lo comunique con notify() wait(); catch (InterruptedException e) { available = false ; // notifica que el valor ha sido leído notifyall(); // devuelve el valor return contents; public synchronized void put(int value) { while (available == true ) { try { // Espera a que get() lea el valor disponible antes de darle otro wait(); catch (InterruptedException e) { // ofrece un nuevo valor y lo declara disponible contents = value; available = true ; // notifica que el valor ha sido cambiado notifyall(); El bucle while de la función get() continúa ejecutándose hasta que el método put() haya suministrado un nuevo valor y lo indique con avalaible = true. En cada iteración del while, la función wait() hace que el hilo que ejecuta el método get() se detenga hasta que se produzca un mensaje de que algo ha sido cambiado (en este caso, con el método notifyall() ejecutado por put()). El método put() funciona de forma similar. Existe también la posibilidad de sincronizar una parte del código de un método sin necesidad de mantener blo- 92 Departamento de Informática. U.T.F.S.M. Michael Moossen

105 CAPÍTULO 4. PROGRAMACIÓN CON LA API ESTÁNDAR queado el objeto desde el comienzo hasta el final del método. Para ello se utiliza la palabra clave synchronized indicando entre paréntesis, el objeto que se desea sincronizar. Por ejemplo, si se desea sincronizar el propio thread en una parte del método run(), el código podría ser: public void run() { while (true ) {... synchronized (this ) { // El objeto a sincronizar es el propio thread... // Código sincronizado try { sleep(500); // Se detiene el thread durante 0.5 segundos pero el objeto // es accesible por otros threads al no estar sincronizado catch (InterruptedException e) { Un thread puede llamar a un método sincronizado de un objeto para el cual ya posee el bloqueo, volviendo a adquirirlo. Por ejemplo: public class VolverAAdquirir { public synchronized void a() { b(); System.out.println("Estoy en a()"); public synchronized void b() { System.out.println("Estoy en b()"); El anterior ejemplo obtendrá como resultado: Estoy en b() Estoy en a() debido a que se ha podido acceder al objeto con el método b() al ser el thread que ejecuta el método a() propietario con anterioridad del bloqueo del objeto. La sincronización es un proceso que lleva bastante tiempo a la CPU, luego se debe minimizar su uso, ya que el programa será más lento Prioridades Con el fin de conseguir una correcta ejecución de un programa, se establecen prioridades en los threads, de forma que se produzca un reparto eficiente de los recursos disponibles. Así, en un determinado momento, interesará que un determinado proceso acabe lo antes posible sus cálculos, de forma que habrá que otorgarle más recursos (más tiempo de CPU). Esto no significa que el resto de los procesos no requieran tiempo de CPU, sino que necesitarán menos. La forma de llevar a cabo esto, es gracias a las prioridades. Cuando se crea un nuevo thread, este hereda la prioridad del thread, desde el cual ha sido inicializado. Las prioridades viene definidas por variables miembro de la clase Thread, que toman valores enteros, que oscilan entre la máxima prioridad MAX PRIORITY (normalmente tiene el valor 10) y la mínima prioridad MIN PRIORITY (valor 1), siendo la prioridad por defecto NORM PRIORITY (valor 5). Para modificar la prioridad de un thread se utiliza el método setpriority(). Se obtiene su valor con getpriority(). El algoritmo de distribución de recursos en Java escoge por norma general, aquel thread que tiene una prioridad Michael Moossen Departamento de Informática. U.T.F.S.M. 93

106 4.7. HILOS Y SINCRONIZACIÓN mayor, aunque no siempre ocurra así, para evitar que algunos procesos queden dormidos. Cuando hay dos o más threads de la misma prioridad (y además, dicha prioridad es la más elevada), el sistema no establecerá prioridades entre los mismos, y los ejecutará alternativamente dependiendo del sistema operativo en el que esté siendo ejecutado. Si dicho sistema operativo soporta el time-slicing (reparto del tiempo de CPU), como por ejemplo lo hace Windows 95/98/NT, los threads serán ejecutados alternativamente. Un thread puede en un determinado momento renunciar a su tiempo de CPU y otorgárselo a otro thread de la misma prioridad, mediante el método yield(), aunque en ningún caso a un thread de prioridad inferior Grupos de Thread Todo hilo de Java debe formar parte de un grupo de hilos (ThreadGroup). Puede pertenecer al grupo por defecto o a uno explícitamente creado por el usuario. Los grupos de threads proporcionan una forma sencilla de manejar múltiples threads como un sólo objeto. Así, por ejemplo, es posible parar varios threads con una sola llamada al método correspondiente. Una vez que un thread ha sido asociado a un grupo, no puede cambiar de grupo. Cuando se arranca un programa, el sistema crea un ThreadGroup llamado main. Si en la creación de un nuevo thread no se especifica a qué grupo pertenece, automáticamente pasa a pertenecer al grupo del thread desde el que ha sido creado. Si en dicho programa no se crea ningún ThreadGroup adicional, todos los threads creados pertenecerán al grupo main (en este grupo se encuentra el método main()). Para conseguir que un thread pertenezca a un grupo concreto, hay que indicarlo al crear el nuevo thread, según uno de los siguientes constructores: Thread(ThreadGroup grupo, Runnable destino) Thread(ThreadGroup grupo, String nombre) Thread(ThreadGroup grupo, Runnable destino, String nombre) A su vez, un ThreadGroup debe pertenecer a otro ThreadGroup. Como ocurría en el caso anterior, si no se especifica ninguno, el nuevo grupo pertenecerá al ThreadGroup desde el que ha sido creado (por defecto al grupo main). La clase ThreadGroup tiene dos posibles constructores: ThreadGroup(ThreadGroup parent, String nombre); ThreadGroup(String name); el segundo de los cuales toma como padre el grupo al cual pertenezca el thread desde el que se crea (Thread.currentThread()). Existen numerosos métodos para trabajar con grupos de threads a disposición del usuario, como por ejemplo, getmaxpriority(), setmaxpriority(), getname(), getparent() y parentof(). En la práctica los ThreadGroups no se suelen utilizar demasiado. Su uso práctico se limita a efectuar determinadas operaciones de forma más simple que de forma individual. Véase el siguiente ejemplo: ThreadGroup mithreadgroup = new ThreadGroup("Mi Grupo de Threads"); Thread mithread = new Thread(miThreadGroup, "un thread para mi grupo"); donde se crea un grupo de threads(mithreadgroup) y un thread que pertenece a dicho grupo(mithread). 94 Departamento de Informática. U.T.F.S.M. Michael Moossen

107 Capítulo 5 Java e Internet 5.1. Trabajo en Red El entorno Java es altamente considerado por su capacidad para escribir programas que utilizan e interactúan con los recursos de Internet y la World Wide Web. De hecho, los navegadores que soportan Java utilizan esta capacidad del entorno Java, hasta el extremo de transportar y ejecutar applets a través de la red Conceptos Básicos Cuando se escriben programas Java que se comunican a través de la red, se está programando en la llamada capa de aplicación. Típicamente, no se necesita trabajar con las capas de transporte, ni los protocolos TCP y UDP; en su lugar se utilizan las clases del paquete java.net. Estas clases porporcionan comunicación de red independiente del sistema. Sin embargo, se necesita entender la diferencia entre TCP y UDP para decidir que clases se deberían utilizar en qué programas. Cuando dos aplicaciones se quieren comunicar una con otra de forma fiable, establecen una conexión y se envían datos a través de la conexión. Esto es parecido a hacer una llamada de teléfono se establece una comunicación cuando se marca el número de teléfono y la otra persona responde. Se envían y reciben datos cuando se habla por el teléfono y se escucha lo que le dice la otra persona. Al igual que la compañia telefónica, TCP garantiza que los datos enviados por una parte de la conexión, realmente llegan a la otra parte y en el mismo orden en el que fueron enviados. Las aplicaciones que requieren fiabilidad, o canales punto a punto para comunicarse, utilizan TCP. Hyper Text Transfer Protocol (HTTP), File Transfer Protocol (ftp), y Telnet (telnet) son ejemplos de aplicaciones que requieren un canal de comunicación fiable. El orden en que los datos son enviados y recibidos a través de la red es crítico para el éxito de estas aplicaciones cuando se utiliza HTTP para leer desde una URL, los datos deben recibirse en el mismo orden en que fueron enviados, de otra forma se podría obtener un archivo HTML revuelto, un archivo Zip corrupto o cualquier otra información no válida. Para muchas aplicaciones, esta garantía de fiabilidad es crítica para el éxito de la transferencia de información desde un punto de la conexión a otro. Sin embargo, otras formas de comunicación, no necesitan que esta comunicación sea tan estricta, y más aún esta la conexión fiable puede entorpecer el servicio. Considere, por ejemplo, un servicio de reloj que envía la hora actual a sus clientes cuando estos lo piden. Si el cliente pierde un paquete, tiene sentido volver a enviar el paquete? No, porque la hora que recibiría el cliente ya no sería exacta. Si el cliente hace dos peticiones y recibe dos paquetes del servidor en distinto orden, realmente no importa porque el cliente puede imaginarse que los paquetes no están en orden y pedir otro. El canal fiable, aquí no es necesario, ya que causa una degradación del rendimiento, y podría entorpecer la utilidad del servicio. Otro ejemplo de servicio que no necesita un canal de fiabilidad garantizada, es el comando ping. El único objetivo del comando ping es comprobar la comunicación entre dos computadores a través de la red. De hecho, ping necesita conocer las caídas o los paquetes fuera de orden para determinar lo buena o mala que es la conexión. Así un canal fiable entorpecería este servicio. 95

108 5.1. TRABAJO EN RED El protocolo UDP proporciona una comunicación no garantizada entre dos aplicaciones en la red. UDP no está basado en la conexión como TCP. UDP envía paquetes de datos, llamados datagramas, de una aplicación a otra. Enviar datagramas, es como enviar una carta a través del servicio de correos: el orden de envío no es importante y no está garantizado, y cada mensaje es independiente de los otros. Puertos Generalmente, un computador tiene una sola conexión física con la red. Todos los datos destinados a un computador en particular, llegan a través de una sola conexión. Sin embargo, los datos podrían ser utilizados por diferentes aplicaciones ejecutándose en el computador. Entonces, cómo sabe el computador a qué aplicación enviarle los datos? A través del uso de puertos. Los datos transmitidos por Internet están acompañados por una información de dirección que identifica el computador y el puerto al que están destinados. El computador está identificado por su dirección IP de 32 bits; esta dirección se utiliza para envíar los datos al computador correcto en la red. Los puertos están identificados por un número de 16 bits, que TCP y UDP utilizan para envíar los datos a la aplicación correcta. En aplicaciones basadas en la conexión, una aplicación establece una conexión con otra aplicación, uniendo un socket a un número de puerto. Esto tiene el efecto de registrar la aplicación con el sistema para recibir todos los datos destinados a ese puerto. Dos aplicaciones no pueden utilizar el mismo puerto: intentar acceder a uno que ya está utilizado, provocará un error. En comunicaciones basadas en datagramas, los paquetes de datagramas contienen el número de puerto del destinatario. Los números de puertos tienen un rango de 0 a (porque están representados por un número de 16 bits). Los puertos entre los números están restringidos reservados para servicios bien conocidos como HTTP, FTP y otros servicios del sistema. Otras aplicaciones no deberían intentar usar estos puertos. A través de las clases del paquete java.net, los programas Java pueden utilizar TCP o UDP para comunicarse a través de Internet. Las clases URL, URLConnection, Socket, y SocketServer utilizan TCP para comunicarse a través de la red. Las clases DatagramPacket y DatagramServer utilizan UDP URL URL es un acrónimo que viene de Uniform Resource Locator y es una referencia (una dirección) a un recurso de Internet. Algunas veces es más sencillo (aunque no enteramente acertado) pensar en una URL como el nombre de un archivo en la red, porque la mayoría de las URLs se refieren a un archivo en alguna máquina de la red. Sin embargo, se debe recordar que las URLs pueden apuntar a otros recursos de la red, como consultas a bases de datos, o salidas de comandos. Lo siguiente es un ejemplo de una URL. Esta URL en particular, direcciona el sitio Web de Java hospedada por Sun Microsystems. La URL anterior, como otras URLs, tiene dos componentes principales. El idendificador de protocolo El nombre del recurso En el ejemplo, http es el identificador de protocolo y //java.sun.com/ es el nombre del recurso. El identificador de protocolo indica el nombre del protocolo a utilizar para buscar ese recurso. El ejemplo utiliza el protocolo Hyper Text Transfer Protocol (HTTP), que es utilizado típicamente para servir documentos de hypertexto. El nombre del recurso es la dirección completa al recurso. El formato del nombre del recurso depende completamente del protocolo utilizado, pero la mayoría de los formatos de nombres de recursos contienen uno o más de los siguientes componentes. 96 Departamento de Informática. U.T.F.S.M. Michael Moossen

109 CAPÍTULO 5. JAVA E INTERNET Nombre del host Nombre de la máquina donde reside el recurso. Nombre de archivo El path al archivo dentro de la máquina. Número de puerto El número de puerto a usar (normalmente es opcional). Referencia Una referencia a una posición marcada dentro del recurso; normalmente identifica una posición específica dentro de un archivo (normalmente es opcional). Para muchos protocolos, el nombre del host y el nombre del archivo son obligatorios y el número de puerto y la referencia son opcionales. Por ejemplo, el nombre del recurso para una URL HTTP debería especificar un servidor en la red (el nombre del host o su dirección IP) y el path al documento en esa máquina (nombre de archivo), y también puede especificar un número de puerto y una referencia. En la URL mostrada anteriormente, java.sun.com es el nombre del host, y la barra inclinada(slash) / es el path para el nombre del archivo index.html. Una URL absoluta contiene toda la información necesaria para alcanzar el recurso en cuestión. También, existen URLs relativas, que sólo contienen la información suficiente para alcanzar el recurso en relación a (o en el contexto de) otra URL. El paquete java.net contiene una clase llamada URL que utilizan los programas Java para representar una URL. Los programas Java pueden construir un objeto URL, abrir una conexión con el, leer y escribir datos a través de esa conexión. A continuación, una breve descripción de cada uno de los constructores de esta clase: URL(String DirAbsoluta) Este constructor, crea un objeto a partir de una URL absoluta. Ejemplo, URL inf = new URL("http://www.inf.utfsm.cl/"); URL(URL URLContexto, String DirRelativa) Permite crear una URL a partir de un contexto y una URL relativa. Por ejemplo, URL inf = new URL("http://www.inf.utfsm.cl/"); URL personas = new URL(inf, "personas.html"); //URL a recurso URL personasbottom = new URL(personas, "#BOTTOM"); //URL a referencia en recurso URL(String Protocolo, String Host, String Direccion) Este constructor, permite crear una URL a partir de sus componentes por separado, usando el puerto por defecto. Ejemplo, URL personas = new URL("http", "www.inf.utfsm.cl", "/personas.html"); URL(String Protocolo, String Host, int Puerto, String Direccion) Permite crear una URl, a partir de los componentes de la URL, incluyendo un puerto. Por ejemplo, URL personas = new URL("http", "www.inf.utfsm.cl", 80, "/personas.html"); Cada uno de los cuatro constructores de URL lanza una excepción de tipo MalformedURLException, si los argumentos del constructor son nulos o el protocolo es desconocido. A continuación, una breve descripción de cada uno de los métodos más importantes de esta clase. getprotocol() Devuelve el componente identificador de protocolo de la URL. Michael Moossen Departamento de Informática. U.T.F.S.M. 97

110 5.1. TRABAJO EN RED gethost() Devuelve el componente nombre del host de la URL. getport() Devuelve el componente número del puerto de la URL. Este método devuelve un entero que es el número de puerto. Si el puerto no está selccionado, devuelve -1. getfile() Devuelve el componente nombre de archivo de la URL. getref() Obtiene el componente referencia de la URL. toexternalform() Permite obtener una cadena que contiene la dirección URL completa. tostring() Entrega el mismo resultado que toexternalform(). Nota: No todas las direcciones URL contienen todos los componentes de una URL genérica. La clase URL proporciona estos métodos porque las URLs de HTTP, contienen todos estos componentes y quizás son las URLs más utilizadas. Este pequeño programa de ejemplo, crea una URL partiendo de una especificación y luego utiliza los métodos accesores del objeto URL para analizar la URL. import java.net.*; import java.io.*; class ParseURL { public static void main(string[] args) { URL aurl = null ; try { aurl = new URL("http://java.sun.com:80/tutorial/intro.html#DOWNLOADING"); System.out.println("protocol = "+ aurl.getprotocol()); System.out.println("host = "+ aurl.gethost()); System.out.println("filename = "+ aurl.getfile()); System.out.println("port = "+ aurl.getport()); System.out.println("ref = "+ aurl.getref()); catch (MalformedURLException e) { System.out.println("MalformedURLException: "+ e); La salida mostrada por el programa: protocol = http host = java.sun.com filename = /tutorial/intro.html port = 80 ref = DOWNLOADING Leer Directamente desde una URL Después de haber creado satisfactoriamente una URL, se puede llamar al método openstream() de la clase URL, para obtener un canal desde el que poder leer el contenido de la URL. El método retorna un objeto 98 Departamento de Informática. U.T.F.S.M. Michael Moossen

111 CAPÍTULO 5. JAVA E INTERNET java.io.inputstream por lo que se puede leer de la URL utilizando los métodos de InputStream. Leer desde una URL es tan sencillo como leer de un canal de entrada. El siguiente programa utiliza openstream(), para obtener un flujo de entrada a la URL Lee el contenido del canal de entrada y lo muestra por pantalla. import java.net.*; import java.io.*; class OpenStreamTest { public static void main(string[] args) { try { URL yahoo = new URL("http://www.yahoo.com/"); BufferedReader br = new BufferedReader( new InputStreamReader(yahoo.openStream())); String inputline; while ((inputline = br.readline())!= null ) { System.out.println(inputLine); br.close(); catch (MalformedURLException me) { System.out.println("MalformedURLException: "+ me); catch (IOException ioe) { System.out.println("IOException: "+ ioe); Cuando se ejecute el programa, se deberían ver los comandos HTML y el contenido textual del archivo HTML localizado en Establecer una Conexión a una URL Si se ha creado satisfactoriamente una URL, se puede llamar al método openconnection() de la clase URL, para establecer una conexión a ella. Cuando se ha establecido una conexión a una URL, se habrá inicializado un enlace de comunicación entre un programa y la URL a través de la red. Por ejemplo, se puede crear una conexión con el motor de búsqueda de Yahoo con el siguiente código: try { URL yahoo = new URL("http://www.yahoo.com/"); yahoo.openconnection(); catch (MalformedURLException e) { // nueva URL() fallada... catch (IOException e) { // openconnection() fallada... Si es posible, el método openconnection() crea un nuevo objeto URLConnection, lo inicializa, se conecta con la URL y devuelve el objeto URLConnection. En caso de error, por ejemplo, si el servidor de Yahoo está apagado, el método openconnection() lanza una excepción IOException. Si se ha utilizado satisfactoriamente openconnection() para inicializar la comunicación con una URL, se tendrá una referencia a un objeto URLConnection. La clase URLConnection contiene muchos métodos que permiten comunicarse con la URL a través de la red. URLConnection es una clase centrada sobre HTTP muchos de sus Michael Moossen Departamento de Informática. U.T.F.S.M. 99

112 5.1. TRABAJO EN RED métodos son útiles sólo cuando trabajan con URLs HTTP. Sin embargo, la mayoría de los protocolos permiten leer y escribir desde una conexión. Leer desde un Objeto URLConnection El siguiente programa realiza la misma función que el mostrado en la sección Sin embargo, mejor que abrir directamente un flujo desde la URL, este programa abre explícitamente una conexión a la URL, obtiene un flujo de entrada sobre la conexión, y lee desde el flujo de entrada. import java.net.*; import java.io.*; class ConnectionTest { public static void main(string[] args) { try { URL yahoo = new URL("http://www.yahoo.com/"); URLConnection yahooconnection = yahoo.openconnection(); BufferedReader br = new BufferedReader( new InputStreamReader(yahooConnection.getInputStream())); String inputline; while ((inputline = br.readline())!= null ) { System.out.println(inputLine); br.close(); catch (MalformedURLException me) { System.out.println("MalformedURLException: "+ me); catch (IOException ioe) { System.out.println("IOException: "+ ioe); La salida de este programa debiera ser idéntica a la salida del programa que abría directamente el flujo desde la URL. Se puede utilizar cualquiera de estas dos formas para leer desde una URL. Sin embargo, algunas veces leer desde una URLConnection en vez de leer directamente desde una URL podría ser más útil, ya que se puede utilizar el objeto URLConnection para otras tareas (como escribir sobre la conexión URL) al mismo tiempo. Escribir hacia una URLConnection Muchas páginas HTML contienen formularios campos de texto y otros objetos que permiten introducir datos, que luego son enviados al servidor. Después de ingresar la información requerida e iniciar la petición pulsando un botón, el navegador escribe los datos en la URL a través de la red. Después la otra parte de la conexión en el servidor, los procesa, y le envía de vuelta una respuesta, normalmente en la forma de una nueva página HTML. Los programas Java también pueden interactuar con el servidor. Sólo deben poder escribir a una URL, así proporcionan los datos al servidor. Un programa puede hacer esto siguiendo los siguientes pasos: Crear una URL. Abrir una conexión con la URL. Obtener un flujo de salida sobre la conexión. Este canal de salida está conectado al flujo de entrada estándar del servidor. Escribir en el flujo de salida. 100 Departamento de Informática. U.T.F.S.M. Michael Moossen

113 CAPÍTULO 5. JAVA E INTERNET Cerrar el flujo de salida. Hassan Schroeder, miembro del equipo de Java, escribió un script cgi-bin, llamado backwards, y está disponible en la Web site de Sun. Se puede utilizar este script para probar el siguiente programa de ejemplo. El script lee una cadena de la entrada estándar, invierte la cadena, y escribe el resultado en la salida estándar. El script requiere una entrada de la siguiente forma: string=string to reverse, donde string to reverse es la cadena cuyos caracteres se quieren invertir. Aquí hay un programa de ejemplo que ejecuta el script backwards, a través de la red, utilizando un URLConnection. 01 import java.io.*; 02 import java.net.*; public class ReverseTest { 05 public static void main(string[] args) { 06 try { 07 if (args.length!= 1) { 08 System.err.println("Usage: java ReverseTest string to reverse"); 09 System.exit(1); String stringtoreverse = URLEncoder.encode(args[0]); URL url = new URL("http://java.sun.com/cgi-bin/backwards"); 14 URLConnection connection = url.openconnection(); 15 connection.setdooutput(true ); PrintWriter writer = new PrintWriter(connection.getOutputStream()); 18 writer.println("string="+ stringtoreverse); 19 writer.close(); BufferedReader reader = new BufferedReader( 22 new InputStreamReader(connection.getInputStream())); 23 String inputline; while ((inputline = reader.readline())!= null ) { 26 System.out.println(inputLine); reader.close(); 29 catch (MalformedURLException me) { 30 System.err.println("MalformedURLException: "+ me); 31 catch (IOException ioe) { 32 System.err.println("IOException: "+ ioe); Primero, el programa procesa los argumentos de la línea de comandos, entre las líneas 07 y 11. Estas líneas aseguran que el usuario proporciona uno y sólo un argumento en la línea de comandos del programa y lo codifica. El argumento de la línea de comandos, es la cadena a invertir por el script cgi-bin backwards. El argumento de la línea de comandos, podría tener espacios u otros caractetes no alfanuméricos. Estos caracteres deben ser codificados, ya que algunos tienen significados específicos. Esto se consigue mediante la clase URLEncoder. Luego, en la línea 13, el programa crea el objeto url la URL para el script backwards en java.sun.com. El programa crea una URLConnection, en la línea 14, y abre un flujo de salida sobre esta conexión. El flujo de salida está filtrado a través de un PrintWriter. La línea 15 le indica a la conexión, que a continuación se Michael Moossen Departamento de Informática. U.T.F.S.M. 101

114 5.1. TRABAJO EN RED usará para enviar datos. Luego, se llama al método getoutputstream() sobre la conexión. Si el protocolo no soporta salida, este método lanza una excepción de tipo UnknownServiceException. Si el procolo soporta salida, este método devuelve un flujo de salida que está conectado al flujo de entrada estándar de la URL en el lado del servidor la salida del cliente es la entrada del servidor. En las líneas 18 y 19, el programa escribe la información requerida al flujo de salida utilizando el método println(). Como se puede ver, escribir datos a una URL es tan sencillo, como escribir datos en un flujo. Los datos escritos en el flujo de salida en el lado del cliente, son la entrada para el script backwards en el lado del servidor. El programa ReverseTest construye la entrada en la forma requirida por el script, mediante la concatenación de string para codificar la cadena, y cierra el flujo. Frecuentemente, como en este ejemplo, cuando se escribe en una URL, se está pasando información al script que lee la información que se escribe, se realiza alguna acción y luego se envía la información de vuelta mediante la misma URL. Por lo que, se querrá leer desde la URL, después de haber escrito en ella. El programa ReverseTest lo hace de esta forma, en las líneas desde la 21 hasta la 28. Cuando se ejecute el programa ReverseTest utilizando Invierteme como argumento, se debiera ver esta salida: Invierteme reversed is emetreivni Sockets Se utilizan las clases URL y URLConnection para comunicarse a través de la red a un nivel relativamente alto y para un propósito específico: acceder a los recuros de Internet. Algunas veces los programas requieren una comunicación en la red a un nivel más bajo, por ejemplo, el caso de una aplicación cliente-servidor. En aplicaciones cliente-servidor, el servidor proporciona algún servicio, como procesar consultas a bases de datos o enviar los precios actualizados del stock. El cliente utiliza el servicio proporcionado por el servidor para algún fin, mostrar los resultados de la consulta a la base de datos, o hacer recomendaciones de pedidos a un inversor. La comunicación que ocurre entre el cliente y el servidor debe ser fiable los datos no pueden perderse y deben llegar al lado del cliente en el mismo orden en el que fueron solicitados. TCP proporciona un canal de comunicación fiable punto a punto, lo que utilizan para comunicarse las aplicaciones cliente-servidor en Internet. Las clases Socket y ServerSocket del paquete java.net proporcionan un canal de comunicación independiente del sistema utilizando TCP. Una aplicación servidor normalmente escucha un puerto específico, esperando una petición de conexión de un cliente. Cuando llega una petición de conexión, el cliente y el servidor establecen una conexión dedicada sobre la que pueden comunicarse. Durante el proceso de conexión, el cliente es asignado a un número de puerto, y fija un socket a ella. El cliente se comunica con el servidor escribiendo y leyendo sobre el socket. Similarmente, el servidor obtiene un nuevo número de puerto local(necesita un nuevo puerto para poder continuar escuchando peticiones de conexión del puerto original). El servidor también fija un socket a este puerto local y se comunica mediante la lectura y escritura sobre él. El cliente y el servidor deben ponerse de acuerdo sobre el protocolo esto es, deben ponerse de acuerdo en el lenguaje para transferir la información de vuelta a través del socket. El paquete java.net del entorno de desarrollo de Java proporciona una clase Socket que representa un punto final de una comunicación de dos vías, entre un programa y otro en la red. La clase Socket implementa el lado del cliente de un enlace de dos vías. Si se está escribiendo un programa servidor, también se estará interesado en la clase ServerSocket, que implementa el lado del servidor en un enlace de dos vías. Lectura y Escritura sobre Sockets El siguiente programa es un ejemplo sencillo de cómo establecer una conexión entre un programa cliente y otro servidor, utilizando sockets. La clase Socket del paquete java.net, es una implementación independiente de la plataforma de un cliente para un enlace de comunicación de dos vías entre un cliente y un servidor. La clase Socket se sitúa en la parte superior de una implementación dependiente de la plataforma, ocultando los detalles 102 Departamento de Informática. U.T.F.S.M. Michael Moossen

115 CAPÍTULO 5. JAVA E INTERNET de los sistemas particulares, a un programa Java. Este programa cliente, EchoTest, se conecta con el servicio echo de cualquier servidor Unix(en el puerto 7) mediante un socket. El cliente lee y escribe a través del socket. EchoTest envía todo el texto ingresado en su entrada estándar al servicio echo del servidor, escribiendo el texto en el socket. El servidor repite todos los caracteres recibidos en su entrada desde el cliente de vuelta a través del socket al cliente. El programa cliente lee y muestra los datos pasados de vuelta desde el servidor. 01 import java.io.*; 02 import java.net.*; public class EchoTest { 05 public static void main(string[] args) { 06 Socket echosocket = null ; 07 DataOutputStream os = null ; 08 BufferedReader br = null ; 09 BufferedReader stdin = new BufferedReader( 10 new InputStreamReader(System.in)); try { 13 echosocket = new Socket("localhost", 7); 14 os = new DataOutputStream(echoSocket.getOutputStream()); 15 br = new BufferedReader( 16 new InputStreamReader(echoSocket.getInputStream())); 17 catch (UnknownHostException e) { 18 System.err.println("Host desconocido: localhost"); 19 catch (IOException e) { 20 System.err.println("Conexión rechazada"); if (echosocket!= null && os!= null && br!= null ) { 24 try { 25 String userinput; while ((userinput = stdin.readline())!= null ) { 28 os.writebytes(userinput); 29 os.writebyte( \n ); 30 System.out.println("echo: "+ br.readline()); os.close(); 33 br.close(); 34 echosocket.close(); 35 catch (IOException e) { 36 System.err.println("Error en la Conexión"); Al revisar el código, se aprecia que las líneas 13 a la 16, dentro del primer bloque try del método main(), son críticos establecen la conexión del socket entre el cliente y el servidor y abre un canal de entrada y un canal de salida sobre el socket. La línea 13 crea un nuevo objeto Socket y lo llama echosocket. El constructor Socket utilizado aquí(existen otros) requiere el nombre de la máquina y el número de puerto al que se quiere conectar. El programa de ejemplo Michael Moossen Departamento de Informática. U.T.F.S.M. 103

116 5.1. TRABAJO EN RED utiliza la maquina localhost. El segundo argumento es el número de puerto. El puerto número 7 es el puerto por el que escucha el servicio echo. La línea 14 del código, abre un canal de entrada sobre el socket, y las líneas 15 y 16 abren un canal de salida sobre el mismo socket. EchoTest sólo necesita escribir en el flujo de salida y leer del flujo de entrada para comunicarse a través del socket con el servidor. La siguiente sección de código, entre las líneas 25 y 31, lee desde el flujo de entrada estándar del programa, una línea a la vez. El programa escribe inmediatamente la entrada, seguida por un caracter de nueva línea en el flujo de salida conectado al socket. La última línea del bucle while, la 30, lee una línea de información desde el flujo de entrada conectado al socket. El método readline() se bloquea hasta que el servidor haya devuelto toda la línea. Cuando readline() retorna, el programa imprime la información en la salida estándar. Este bloque continúa El programa lee la entrada del usuario, la envía al servidor echo, obtiene una respuesta desde el servidor, y la muestra hasta que el usuario ingrese un caracter de final de entrada. Cuando el usuario ingresa un caracter de fin de entrada, el bucle while termina y el programa continúa ejecutando las líneas 32 a la 34. Estas tres líneas de código cierran los flujos de entrada y salida conectados al socket, y cierra la conexión del socket con el servidor. El orden es importante se deben cerrar los flujos conectados a un socket antes de cerrar el socket mismo. Este programa cliente tiene un comportamiento correcto y sencillo porque el servidor echo implementa un protocolo sencillo. El cliente envía texto al servidor, y el servidor lo devuelve. Cuando un programa cliente se comunique con un servidor más complejo, como un servidor http, el programa cliente también será más complejo. Sin embargo, las cosas básicas son las vistas en este programa: 1. Abrir un socket. 2. Abrir un flujo de entrada y otro de salida hacia el socket. 3. Leer y escribir a través del socket de acuerdo al protocolo del servidor. 4. Cerrar los streams. 5. Cerrar el socket. Sólo el tercer paso será diferente de un cliente a otro, dependiendo del servidor. Los otros pasos permanecen inalterables. El Lado Servidor de un Socket Esta sección muestra cómo escribir el lado del servidor de una conexión socket, implementando el servicio echo. El programa servidor está implementado por una sóla clase: EchoServer. EchoServer contiene el método main() para el program servidor y realiza todo el trabajo duro, de escuchar el puerto, establecer conexiones, leer y escribir a través del socket. A continuación el código: 01 import java.net.*; 02 import java.io.*; class EchoServer { 05 public static void main(string[] args) { 06 ServerSocket serversocket = null ; try { 09 serversocket = new ServerSocket(7); 10 catch (IOException e) { 11 System.out.println("No es posible usar el Puerto 7, "+ e); 12 System.exit(1); 104 Departamento de Informática. U.T.F.S.M. Michael Moossen

117 CAPÍTULO 5. JAVA E INTERNET Socket clientsocket = null ; 16 try { 17 clientsocket = serversocket.accept(); 18 catch (IOException e) { 19 System.out.println("Error al esperar al Cliente, "+ e); 20 System.exit(1); try { 24 BufferedReader br = new BufferedReader( 25 new InputStreamReader(clientSocket.getInputStream())); 26 PrintWriter pw = new PrintWriter(new BufferedOutputStream( 27 clientsocket.getoutputstream(), 1024), false ); 28 String inputline, outputline; while ((inputline = br.readline())!= null ) { 31 outputline = inputline; 32 pw.println(outputline); 33 pw.flush(); pw.close(); 36 br.close(); 37 clientsocket.close(); 38 serversocket.close(); 39 catch (IOException e) { 40 e.printstacktrace(); Este programa servidor empieza creando un nuevo objeto ServerSocket para escuchar en un puerto específico, el 7. Como se ve, en las líneas de la 8 a la 13. ServerSocket es una clase java.net que proporciona una implementación independiente del sistema del lado del servidor de una conexión cliente/servidor. El constructor de ServerSocket lanza una excepción cuando por alguna razón, por ejemplo, que el puerto ya está siendo utilizado, no puede escuchar en el puerto especificado. En este caso, el EchoServer no tiene elección y termina su ejecución. Si el servidor se conecta con éxito con el puerto deseado, el objeto ServerSocket se crea y el servidor continúa con el siguiente paso, que es aceptar una conexión de un cliente. Como se ve en las líneas entre la 15 y la 21. El método accept() se bloquea(espera) hasta que un cliente empiece y pida una conexión al puerto, que el servidor está escuchando. Cuando el método accept() establece con éxito la conexión con el cliente, devuelve un objeto Socket que apunta a un puerto local nuevo. El servidor puede continuar con el cliente sobre este nuevo Socket, en un puerto diferente del que estaba escuchando originalmente para las conexiones. Por eso, el servidor puede continuar escuchando nuevas peticiones de clientes, a través del puerto original del ServerSocket. Esta versión del programa de ejemplo no escucha más peticiones de clientes. Sin embargo, una versión modificada de este programa, porpocionada más adelante, si lo hace. El código que hay dentro del siguiente bloque try implementa el lado del servidor de una comunicación con el cliente. Esta sección del servidor es muy similar al lado del cliente (vista en el ejemplo EchoTest). Como se ve en las líneas entre la 23 y la 36. Se abre un flujo de entrada y otro de salida sobre un socket. Luego, se lee y escribe a través del socket. Finalmente, se cierran las conexiones. Michael Moossen Departamento de Informática. U.T.F.S.M. 105

118 5.1. TRABAJO EN RED Soporte para Múltiples Clientes El ejemplo EchoServer fue diseñado para escuchar y manejar una sola petición de conexión. Sin embargo, pueden recibirse varias peticiones sobre el mismo puerto y consecuentemente sobre el mismo ServerSocket. Las peticiones de conexiones de clientes se almacenan en el puerto, para que el servidor pueda aceptarlas de forma secuencial. Sin embargo, puede servirlas simultaneamente a través del uso de threads un thread para procesar cada conexión de cliente. El flujo lógico básico en este tipo de servidores, es: while (true ) { <aceptar una conexión>; <crear un thread para tratar a cada cliente>; El thread lee y escribe en la conexión del cliente cuando sea necesario. A continuación, un programa en dos clases: EchoMultiServer y EchoMultiServerThread. EchoMultiServer hace un bucle continuo, escuchando peticiones de conexión desde los clientes con un ServerSocket. Cuando llega una petición, se accepta, y se crea un objeto EchoMultiServerThread para procesarlo, manejando el socket devuelto por accept(), y se arranca el thread. Luego el servidor vuelve a escuchar en el puerto las peticiones de conexión. El objeto EchoMultiServerThread se comunica con el cliente que está leyendo y escribiendo a través del socket. Ahora se puede ejecutar el nuevo servidor y luego ejecutar varios clientes concurrentemente. 01 import java.net.*; 02 import java.io.*; public class EchoMultiServer { 05 public static void main(string[] args) { 06 ServerSocket serversocket = null ; try { 09 serversocket = new ServerSocket(7); 10 catch (IOException e) { 11 System.out.println("No es posible usar el Puerto 7, "+ e); 12 System.exit(1); while (true ) { 16 Socket clientsocket = null ; 17 try { 18 clientsocket = serversocket.accept(); 19 catch (IOException e) { 20 System.out.println("Error al esperar al Cliente, "+ e); 21 continue ; new EchoMultiServerThread(clientSocket).start(); try { 27 serversocket.close(); 28 catch (IOException e) { 29 e.printstacktrace(); Departamento de Informática. U.T.F.S.M. Michael Moossen

119 CAPÍTULO 5. JAVA E INTERNET class EchoMultiServerThread extends Thread { 35 Socket socket = null ; EchoMultiServerThread(Socket socket) { 38 super ("EchoMultiServerThread"); 39 this.socket = socket; public void run() { 43 try { 44 BufferedReader br = new BufferedReader( 45 new InputStreamReader(clientSocket.getInputStream())); 46 PrintWriter pw = new PrintWriter(new BufferedOutputStream( 47 clientsocket.getoutputstream(), 1024), false ); 48 String inputline, outputline; while ((inputline = br.readline())!= null ) { 51 outputline = inputline; 52 pw.println(outputline); 53 pw.flush(); pw.close(); 56 br.close(); 57 clientsocket.close(); 58 serversocket.close(); 59 catch (IOException e) { 60 e.printstacktrace(); Datagramas Los clientes y servidores que se comunican mediante un canal fiable (como una URL o un socket) tienen un canal punto a punto dedicado entre ellos (o al menos la ilusión de uno). Para comunicarse, establecen una conexión, transmiten los datos y luego cierran la conexión. Todos los datos enviados a través del canal se reciben en el mismo orden en el que fueron enviados. Esto está garantizado por el canal. En contraste, las aplicaciones que se comunican mediante datagramas, envían y reciben paquetes de información completamente independientes. Estos clientes y servidores no tienen y no necesitan un canal punto a punto dedicado. El envío de los datos a su destino no está garantizado, ni su orden de llegada. El paquete java.net, contiene dos clases que ayudan a escribir programas Java que utilicen datagramas para enviar y recibir paquetes a través de la red: DatagramSocket y DatagramPacket. Una aplicación envía y recibe DatagramPackets a través de un DatagramSocket. El ejemplo de esta sección está comprendido por dos aplicaciones: un cliente y un servidor. El servidor recibe continuamente paquetes de datagramas a través de un socket para datagramas. Cada paquete recibido por el servidor contiene una petición de la hora del cliente. Cuando el servidor recibe un datagrama, le responde enviando un datagrama que contiene la hora del servidor. La aplicación cliente de este ejemplo es muy sencilla envía un datagrama vacío al servidor que indica que se quiere saber la hora del servidor. Entonces, el cliente espera a que el servidor le envíe un datagrama en respuesta. Michael Moossen Departamento de Informática. U.T.F.S.M. 107

120 5.1. TRABAJO EN RED Dos clases implementan la aplicación servidor: DateServer y DateServerThread. Una sóla clase implementa la aplicación cliente: DateClient. Servidor de Fechas A continuación el código de las clases DateServer y DateServerThread: 01 import java.io.*; 02 import java.net.*; public class DateServer { 05 public static void main(string[] args) { 06 new DateServerThread().start(); class DateServerThread extends Thread { 11 DatagramSocket socket = null ; DateServerThread() { 14 super ("DateServer"); 15 try { 16 socket = new DatagramSocket(); 17 System.out.println("DateServer escuchando el puerto: " 18 + socket.getlocalport()); 19 catch (java.io.ioexception e) { 20 System.err.println("No se pudo levantar el servidor."); public void run() { 25 while (socket!= null ) { 26 byte [] buf = new byte [256]; 27 DatagramPacket packet; 28 InetAddress address; 29 int port; 30 String dstring = null ; try { 33 // recibir paquete 34 packet = new DatagramPacket(buf, 256); 35 socket.receive(packet); 36 address = packet.getaddress(); 37 port = packet.getport(); // enviar respuesta 40 dstring = (new Date()).toString(); 41 buf = dstring.getbytes(); 42 packet = new DatagramPacket(buf, buf.length, address, port); 43 socket.send(packet); 44 catch (IOException e) { 45 System.err.println("IOException: "+ e); 108 Departamento de Informática. U.T.F.S.M. Michael Moossen

121 CAPÍTULO 5. JAVA E INTERNET 46 e.printstacktrace(); La clase DateServer contiene un sólo método: el método main() para la aplicación servidor. El método main() sólo crea un nuevo objeto DateServerThread y lo arranca. La clase DateServerThread implementa la lógica principal del servidor: es un thread que se ejecuta continuamente esperando peticiones a través del un socket de datagramas. DateServerThread tiene una variable, llamada socket, que es una referencia a un objeto DatagramSocket. Esta variable se inicializa a null. Cuando el programa principal crea el DateServerThread, utiliza el único constructor disponible, como se ve en las líneas 13 a la 21. La línea 14 llama al constructor de la superclase(thread) para inicializar el thread con el nombre "DateServer". La siguiente sección de código, la línea 16 principalmente, es la parte crítica del constructor de DateServerThread crea un DatagramSocket. El DateServerThread utiliza este DatagramSocket para escuchar y responder las peticiones de los clientes. El socket es creado utilizando el constructor de DatagramSocket que no requiere argumentos. Una vez creado usando este constructor, el nuevo DatagramSocket se asigna a algún puerto local disponible. La clase DatagramSocket, tiene otro constructor que permite especificar el puerto que se quiere utilizar para asignarle al nuevo objeto DatagramSocket. Ciertos puertos estás dedicados a servicios bien-conocidos y que no pueden ser utilizados. Si se especifica un puerto que está siendo utilizado, fallará la creación del DatagramSocket. Después de crear con éxito el DatagramSocket, el DateServerThread muestra un mensaje indicando el puerto al que se ha asignado el DatagramSocket. El DateClient necesita este número de puerto para construir los paquetes de datagramas destinados a este puerto. Por eso, se debe utilizar este número de puerto, cuando se ejecute el DateClient. Ahora, la parte interesante de DateServerThread, es el método run()(que sobreescribe el método run() de la clase Thread y proporciona la implementación del thread). El método run() primero comprueba que se ha creado un objeto DatagramSocket válido durante su construcción. Si socket es null, entonces el DateServerThread no podría recibir ni enviar paquetes. Sin el socket, el servidor no puede operar, y el método run() retorna. De otra forma, el método run() entra en un bucle infinito. Este bucle espera continuamente las peticiones de los clientes y responde estas peticiones. Este bucle contiene dos secciones críticas de código: la sección que escucha las peticiones y la que las responde. La sección que escucha las peticiones, entre las líneas 33 y 37, crea primero un nuevo objeto DatagramPacket encargado de recibir un datagrama a través del socket. Se puede decir, que el nuevo DatagramPacket está encargado de recibir datos desde el socket debido al constructor utilizado para crearlo. Este constructor requiere sólo dos argumentos, un arreglo de bytes que contendrá los datos enviados por el cliente, y la longitud de este arreglo. Cuando se construye un DatagramPacket para enviarlo a través de un DatagramSocket, también se debe suministrar el host y el puerto de destino del paquete. La línea 35 recibe un datagrama desde el socket. La información contenida dentro del mensaje del datagrama se copia en el paquete creado en la línea anterior. El método receive() se bloquea hasta que se reciba un paquete. Si no se recibe ningún paquete, el servidor no hace ningún progreso y simplemente espera. Las dos líneas siguientes obtienen la dirección del host y el número de puerto desde el que se ha recibido el datagrama. Este es donde el servidor debe responder. En este ejemplo, el arreglo de bytes del datagrama contiene el número a ser elevado al cuadrado. Sólo la llegada del paquete indica una petición por parte del cliente, que puede ser encontrado en la dirección del host y el número de puerto indicado en el datagrama. En este punto, el servidor ha recibido un petición desde un cliente. Ahora el servidor debe responder. Y las siguientes líneas de código, entre la 39 y la 43 construyen la respuesta y la envian. La línea 42 crea un nuevo objeto DatagramPacket utilizado para enviar el mensaje a través del socket del datagrama. Se puede decir que, el nuevo DatagramPacket está destinado a enviar los datos a través del socket, Michael Moossen Departamento de Informática. U.T.F.S.M. 109

122 5.1. TRABAJO EN RED porque el constructor lo utiliza para eso. Este constructor requiere cuatro argumentos. El primero, es el mismo que el utilizado por el constructor para crear los datagramas receptores: un arreglo de bytes que contiene el mensaje del emisor al receptor y la longitud de este arreglo. Los dos siguientes argumentos son diferentes: una dirección de Internet y un número de puerto. Estos dos argumentos son la dirección completa del destino del datagrama y debe ser suministrada por el emisor del datagrama. Finalmente, la línea 43 envía el DatagramPacket creado de esta forma. Cliente de Fechas La clase DateClient implementa una aplicación cliente para el DateServer. Esta aplicación sólo envía una petición al DateServer, espera una respuesta, y cuando esta se recibe, la muestra en la salida estándar. A continuación el código: 01 import java.io.*; 02 import java.net.*; class DateClient { 05 public static void main(string[] args) { 06 int port; 07 InetAddress address; 08 DatagramSocket socket = null ; 09 DatagramPacket packet; 10 byte [] sendbuf = new byte [256]; if (args.length!= 2) { 13 System.out.println("Uso: java DateClient servidor puerto"); 14 return ; try { 18 socket = new DatagramSocket(); 19 catch (java.io.ioexception e) { 20 System.err.println("No se pudo crear la conexión."); 21 return ; try { 25 // enviar solicitud 26 port = Integer.parseInt(args[1]); 27 address = InetAddress.getByName(args[0]); 28 packet = new DatagramPacket(sendBuf, 256, address, port); 29 socket.send(packet); // obtener respuesta 32 packet = new DatagramPacket(sendBuf, 256); 33 socket.receive(packet); 34 String received = new String(packet.getData()); 35 System.out.println(received); socket.close(); 38 catch (IOException e) { 110 Departamento de Informática. U.T.F.S.M. Michael Moossen

123 CAPÍTULO 5. JAVA E INTERNET 39 System.err.println("IOException: "+ e); 40 e.printstacktrace(); La clase DateClient contiene un método el método main() para la aplicación cliente. La parte superior de main(), entre las líneas 5 y 10, declara varias variables locales para su posterior utilización. La siguiente sección, entre las líneas 12 y 15, procesa los argumentos de la línea de comandos utilizados para invocar la aplicación DateClient. Esta aplicación requiere dos argumentos: el nombre de la máquina en la que se está ejecutando DateServer, y el número de puerto por el cual DateServer está escuchando. Cuando arranca, el DateServer muestra un número de puerto. Este es el número de puerto que debe utilizar en la línea de comandos cuando arranque DateClient. Luego, entre las líneas 17 y 22, el cliente utiliza el mismo constructor para crear un DatagramSocket que el servidor. El DatagramSocket es asignado a cualquier puerto disponible. Finalmente, el método main() contiene un bloque try, entre las líneas 24 y 41, que contiene la lógica principal del programa cliente. Este bloque try contiene tres secciones: una sección que envía una petición al servidor, entre las líneas 25 y 29, el método getbyname() de la clase InetAddress obtiene una dirección válida a partir del nombre una máquina; y una sección que obtiene la respuesta del servidor, entre las líneas 31 y 35. Cuando el cliente recibe una respuesta del servidor, utiliza el método getdata() para recuperar los datos del paquete. El cliente convierte los datos en una cadena y los muestra. Finalmente, una sección que cierra la conexión, la línea HTML Cuando se produjo la explosión de Internet, el estándar de HTML que circulaba era el 2.0 (establecido en noviembre del 95), de modo que cualquier navegador que se encuentre será capaz de interpretarlo. Prácticamente todo lo que se vea en esta sección está contemplado por este estándar. Aunque la versión 2.0 cumplía bien el objetivo para el que se creó, carecía de herramientas para tener un control mínimamente complejo de los documentos. No se consideró necesario que lo tuviera, ya que por aquel entonces, Internet era un fenómeno más bien circunscrito a la actividad académica y el contenido primaba sobre el diseño. Debido a ello, Netscape (líder del mercado de navegadores en aquel entonces) empezó a incluir etiquetas nuevas que no formaban parte de ningún estándar. Por estos problemas, el IETF (comité que suele decidir todos los estándares dentro de Internet) comenzó a elaborar el borrador del HTML 3.0, que resultó ser demasiado grande para la época, lo que dificultó su aceptación en el mercado. Esto llevó a una serie de compañías (entre ellas Netscape, Microsoft, IBM, Sun, etc...) a crear un nuevo comité llamado W3C, que es el que actualmente elabora las nuevas versiones del HTML. Su primer trabajo consistió en crear el borrador del HTML 3.2, que incluía muchas de las mejoras que los principales navegadores (Netscape y Explorer) habían introducido en Internet, como son las tablas, los applets, etc.. Este borrador fue aprobado en enero de 1997 e inmediatamente el W3C se puso a trabajar en la elaboración del siguiente estándar: el 4.0. En julio de 1997 se presenta el borrador de este estándar. Por fin se estandarizan los marcos(frames), las hojas de estilo y los scripts(entre otras cosas). El 17 de diciembre de 1997, dicho borrador fue finalmente aprobado. HTML 4.01 es la revisión actual de HTML 4.0, que fue liberada el 24 de abril de Primera Página El Código <HTML> <HEAD> Michael Moossen Departamento de Informática. U.T.F.S.M. 111

124 5.2. HTML <TITLE>Mi Primera Página</TITLE> </HEAD> <BODY> <CENTER><H1>Mi Primera Página</H1></CENTER> <HR> <P>Esta es mi primera página. Por el momento no tiene nada más que un título y un párrafo de texto. </BODY> </HTML> La Explicación Lo primero que conviene explicar es, en qué consisten todos esos símbolos de mayor y menor. El lenguaje HTML se basa en la sintaxis SGML. Esto quiere decir que cualquier cosa que se haga en HTML estará encerrada entre dos etiquetas de esta manera: <ETIQUETA ATRIBUTO="valor" >... </ETIQUETA> Hay ocasiones en que no es necesario cerrar la etiqueta. Mirando el código se visualizan un par de ejemplos de esto(hr y P). Lo primero que se debe indicar, es que el texto que se está componiendo es un documento HTML, pues se indica así: <HTML>... </HTML> Un documento HTML tiene una estructura que lo separa en dos partes: cuerpo y cabecera. En la primera estará la página en sí, mientras que en la segunda se incluyen algunas cosas que pueden llegar a ser muy importantes, no estando directamente ligadas al aspecto de la página, como son por ejemplo, información del autor, código script o definiciones de estilos. Lo primero que hay que incluir en el código es, la cabecera. En el ejemplo: <HEAD> <TITLE>Mi Primera Página</TITLE> </HEAD> Dentro de la cabecera sólo hay otra etiqueta. Es la única imprescindible: el título de la página. Es lo que se visualizará como título de la ventana en los navegadores que lo permitan. Es como se conocerá la página en algunos buscadores y en la agenda de direcciones(bookmarks) de los usuarios. Por lo tanto, es importante pensar en como llamarla. El Cuerpo del Documento Ahora se indicará el contenido. Lo primero será indicar que se está en el cuerpo del documento: <BODY>... </BODY> Luego se escribe el título algo recalcado: <CENTER><H1>... </H1></CENTER> Con esto se coloca el texto centrado(<center>) y en formato <H1> que asegura que aumentará el tamaño del tipo de letra lo suficiente como para que se vea resaltado. Luego se separa ese título que se le ha puesto a la página, del texto, por medio de una línea horizontal: <HR> 112 Departamento de Informática. U.T.F.S.M. Michael Moossen

125 CAPÍTULO 5. JAVA E INTERNET La línea horizontal carece de etiqueta de cierre. Esto es normal en etiquetas que no varían los atributos de texto, sino que insertan un elemento. Por ejemplo, para indicar que se quiere separar el texto de la línea horizontal, con un espacio vertical correspondiente a un párrafo nuevo, se usa la etiqueta <P>: <P>Esta es mi primera página. Por el momento no tiene nada más que un título y un párrafo de texto. En la sección siguiente se verán algunas etiquetas que permitirán cambiar el aspecto del texto Formateo Básico Se pueden establecer varias categorías dentro de las etiquetas usadas para formatear el texto. Aquí se dividirán entre aquellas que sirven para cambiar párrafos enteros y las que son capaces de formatear cadenas de caracteres dentro del párrafo. Formato de Párrafo Estas son las etiquetas más importantes (excluyendo algunas que se verán más adelante): <P> Sirve para delimitar un párrafo. Inserta una línea en blanco antes del texto. <CENTER>... </CENTER> Permite centrar todo el texto del párrafo. <PRE WIDTH=x>... </PRE> Representa el texto encerrado en ella con un tipo de letra monoespaciada. Es útil a la hora de representar código fuente. El atributo WIDTH especifica el número máximo de caracteres en una línea. <DIV ALIGN=x>... </DIV> Permite justificar el texto del párrafo a la izquierda (ALIGN="LEFT"), derecha ("RIGHT"), al centro ("CENTER") o a ambos márgenes ("JUSTIFY"). <BLOCKQUOTE>... </BLOCKQUOTE> Para citar un texto ajeno. Se suele implementar dejando márgenes tanto a la izquierda como a la derecha, razón por la que se usa habitualmente. Las 6 Fuentes de Cabecera HTML ofrece seis etiquetas distintas para mostrar cabeceras. Son éstas: <H1>... </H1> Cabecera de nivel 1 <H2>... </H2> Cabecera de nivel 2 <H3>... </H3> Cabecera de nivel 3 <H4>... </H4> Michael Moossen Departamento de Informática. U.T.F.S.M. 113

126 5.2. HTML <H5>... </H5> <H6>... </H6> Cabecera de nivel 4 Cabecera de nivel 5 Cabecera de nivel 6 No resulta recomendable utilizarlas para aumentar o disminuir el tamaño del tipo de letra, ya que cada navegador los muestra de manera diferente. Se usan para dividir correctamente en secciones nuestra página, tal y como se hace en un documento de texto normal. Cambiando el Tipo de Letra Todas estas etiquetas que permiten cambiar de una manera u otra el aspecto del tipo de letra que se está utilizando y se pueden utilizar con cadenas de caracteres dentro de un párrafo. <B>... </B> Pone el texto en negrita. <I>... </I> Representa el texto en cursiva. <U>... </U> Para subrayar texto. <S>... </S> Para tachar. <SUP>... </SUP> Letra superíndice. <SUB>... </SUB> Letra subíndice. <BIG>... </BIG> Incrementa el tamaño del tipo de letra. <SMALL>... </SMALL> Disminuye el tamaño del tipo de letra. Otros Elementos Por último, se presentan algunas etiquetas que no afectan al texto y que se pueden incorporar a una página. <HR> Inserta una barra horizontal. <BR> Salto de línea. <! > Comentarios. 114 Departamento de Informática. U.T.F.S.M. Michael Moossen

127 CAPÍTULO 5. JAVA E INTERNET Caracteres Especiales Las máquinas manejan la información en formato binario(unos y ceros). Estos, a su vez, forman números, los cuales se traducen en letras. Cómo? Mediante tablas. Se puede asignar el valor 65 a la letra A, el 66 a la B, etc.. El problema está en que cada computador es de un fabricante distinto y puede adoptar una tabla diferente al resto. Para evitarlo, existen diversos estándares, y el más extendido es el ASCII. De hecho, actualmente, todos los computadores tienen la misma tabla ASCII para los primeros 127 caracteres. Pero esa tabla no contiene vocales con acento, ni eñes, ni símbolos de abrir interrogación o exclamación... Esto pasa por dejar que los norteamericanos sean quienes construyan los computadores. HTML 2.0 eligió como tabla estándar la ISO-Latin-1, que comparte con la ASCII los 127 caracteres e incluye unos cuantos más hasta el número 255. Caracteres Extendidos en HTML La manera de incluir los caracteres extendidos (cuyo número está más allá del 127) consiste en encerrar el código entre los caracteres &# y ;. Así pues, lo siguiente: ½ debería resultar en el caracter un medio( 1 2 ). También existe una serie de sinónimos para poder recordar con más facilidad estos caracteres. Así, por ejemplo, ½ también se puede escribir como ½. Caracteres de Control En el HTML existen cuatro caracteres de control, que se usan para formar etiquetas, establecer atributos, etc.. Para poder emplearlos sin riesgo, se deben escribir los siguiente códigos: Código Resultado < < > > & & " " Espacio en Blanco Enlaces Las siglas HTML significan HyperText Markup Language, es decir, es un lenguaje para hipertexto. Existen múltiples formatos de hipertexto (por ejemplo, los archivos de ayuda de Windows) y lo que tienen en común es que todos poseen enlaces. Un enlace es una zona de texto o gráficos que si son seleccionados, trasladan a otro documento de hipertexto o a otra posición dentro del documento actual. Siendo HTML el lenguaje de Internet, la diferencia que posee con respecto a otros tipos de hipertexto, es que ese otro documento puede estar físicamente en la otra punta del planeta. Son los enlaces lo que hacen de la World Wide Web lo que es. La Etiqueta <A> Para incorporar un enlace hay que utilizar esta etiqueta. Todo lo que se encierre entre <A> y </A>, ya sea texto o imágenes, será considerado como enlace y sufrirá dos modificaciones: Se visualizará de manera distinta en el navegador. Generalmente, el texto aparecerá subrayado y de un color distinto al habitual, y las imágenes estarán rodeadas por un borde del mismo color que el del texto de un enlace. Al pulsar sobre un enlace, se desplegará al documento al que apuntaba el enlace. Para que un enlace funcione, se le debe especificar una dirección. Lo que se logra de la siguiente manera: <A HREF="direccion" >Pulsame</A> La dirección debe estar en formato URL(Uniform Resource Locator). Michael Moossen Departamento de Informática. U.T.F.S.M. 115

128 5.2. HTML Anclas Como ya se dijo, es posible acceder a una posición de un documento HTML. Para hacerlo, primero se debe especificar el lugar del documento al que se quiere acceder: <A NAME="ancla" > Así, un link en la misma página que redireccione a esta posición, será: <A HREF="#ancla" >Vamos a donde antes</a> También, se puede acceder a anclas situadas en documentos remotos. Para ello se añade el nombre del ancla a la URL, así: <A HREF="enlaces.html#ancla" >Otra vez</a> Listas Listas de Ítemes La etiqueta <UL> permite presentar listas de elementos sin orden alguno. Cada elemento de la lista irá normalmente precedido por un círculo. Por ejemplo, <UL> <LI>Primer elemento <LI>Segundo elemento </UL> Que se verá como: Primer elemento Segundo elemento La etiqueta <UL> admite los siguientes atributos: COMPACT Indica al navegador que debe representar la lista de la manera más compacta posible. TYPE="..." Indica al navegador el dibujo que debe precedir a cada elemento de la lista. Para mayor flexibilidad, se admite también como parámetro de <LI>. El tipo puede ser disc, circle o square. Listas Enumeradas La etiqueta <OL> permite presentar listas de elementos enumerados. Por ejemplo, <OL> <LI>Primer elemento <LI>Segundo elemento </OL> Que se verá como: 1. Primer elemento 2. Segundo elemento La etiqueta <OL> admite los siguientes atributos: 116 Departamento de Informática. U.T.F.S.M. Michael Moossen

129 CAPÍTULO 5. JAVA E INTERNET COMPACT Indica al navegador que debe representar la lista de la manera más compacta posible. TYPE="..." Indica al navegador el tipo de numeración que precederá a cada elemento de la lista. Para mayor flexibilidad se admite también como parámetro de <LI>. El Tipo puede ser 1, a, A, i ó I. START="num" Indica al navegador el número por el que se comenzará a contar los elementos de la lista. Listas de Definiciones Este es el único tipo de lista que no utiliza la etiqueta <LI>. Al tener como objetivo presentar una lista de definiciones, de modo que tiene que representar de manera distinta el objeto definido y la definición. Esto se hace por medio de las etiquetas <DT> y <DD>: <DL> <DT>Primer elemento<dd>es un elemento muy bonito. <DT>Segundo elemento<dd>este, en cambio, es peor. </DL> Que se verá como: Primer elemento Es un elemento muy bonito. Segundo elemento Este, en cambio, es peor. La etiqueta <DL> sólo admite como parámetro el ya conocido COMPACT, que tiene el mismo comportamiento que con los otros dos tipos de lista anteriores Imágenes Para incluir gráficos e imágenes en páginas se utiliza la etiqueta <IMG> de esta manera: <IMG SRC="archivo grafico" ALT="descripcion" > El atributo SRC especifica el nombre del archivo que contiene el gráfico. Los formatos estándares en la red son el GIF y el JPG. Las últimas versiones de Netscape y Explorer aceptan también el formato PNG. El atributo ALT especifica el texto que se mostrará en lugar del gráfico en aquellos navegadores que no sean capaces de mostrarlos (como el Lynx) o en el supuesto de que el usuario los haya desactivado. Algunos navegadores lo muestran cuando se pasa el mouse por encima de la imagen. Es por eso que, aunque algunos usuarios no lo lleguen a ver nunca, conviene ponerlo siempre. De hecho, el estádar HTML 4.0 obliga a hacerlo. Existen dos atributos que, aunque opcionales, conviene indicar siempre: la altura y el ancho del gráfico en pixels. De este modo, el navegador puede mostrar un recuadro del tamaño real de la imagen, mientras la va cargando de la red, y así poder mostrar el resto de la página correctamente mientras tanto. <IMG SRC="graficos/dwnldns.gif" ALT="Netscape 6.0" WIDTH=88 HEIGHT=31> Imágenes y Enlaces Suele ser común incluir enlaces dentro de un gráfico. En ese caso, por defecto, los navegadores le pondrán un borde al gráfico para indicar que efectivamente es un enlace. Práctico, pero la mayoría de las veces bastante poco estético. Por medio del atributo BORDER se puede alterar el grosor de ese borde o incluso eliminarlo dejándolo en cero. Michael Moossen Departamento de Informática. U.T.F.S.M. 117

130 5.2. HTML <A HREF="http://www.netscape.com" > <IMG SRC="graficos/dwnldns.gif" ALT="Netscape 6.0" WIDTH=88 HEIGHT=31 BORDER=0> </A> Alineación respecto al texto Para poder diseñar páginas que contengan texto y gráficos a la vez, HTML proporciona, por medio del atributo ALIGN, las siguientes maneras de alinear una imagen respecto del texto que la acompaña: TOP Coloca el punto más alto de la imagen coincidiendo con el más alto de la línea de texto actual. MIDDLE Alinea el punto medio (en altura) de la imagen con la base del texto. BOTTOM Valor por defecto. Alínea el punto más bajo de la imagen con la base del texto. LEFT Coloca la imagen a la izquierda del texto. RIGHT Coloca la imagen a la derecha del texto Formateo Fino Lo ideal cuando se trabaja con texto sería poder cambiarlo al tamaño más conveniente, ponerlo de colores y cambiar el tipo de letra. Todo esto se puede lograr con la etiqueta <FONT>. Colores Para cambiar el color del texto se utiliza el atributo COLOR. La manera de especificar el color es común a todas las etiquetas HTML: o bien indicando el nombre, si es un color normal, o bien especificando el porcentaje de rojo, verde y azul(código RGB) del mismo. Los colores reconocidos son los siguientes: black silver gray white maroon red purple fuchsia green lime olive yellow navy blue teal aqua <FONT COLOR="red" >Estoy en rojo</font> El modo de indicar el color RGB es el siguiente: <FONT COLOR="#FF0000" >D</FONT> <FONT COLOR="#EF0000" >E</FONT> <FONT COLOR="#DF0000" >G</FONT> <FONT COLOR="#CF0000" >R</FONT> <FONT COLOR="#BF0000" >A</FONT> <FONT COLOR="#AF0000" >D</FONT> <FONT COLOR="#9F0000" >A</FONT> <FONT COLOR="#8F0000" >D</FONT> <FONT COLOR="#7F0000" >O</FONT> Lo que da como resultado, lo siguiente: DEGRADADO La primera componente en hexadecimal es el rojo, la segunda el verde y la tercera el azul(red-green-blue, RGB). 118 Departamento de Informática. U.T.F.S.M. Michael Moossen

131 CAPÍTULO 5. JAVA E INTERNET Tamaño del Texto El parámetro utilizado para indicar el tamaño es SIZE. Puede utilizarse para indicar tamaños absolutos, del 1 al 7. El tamaño por defecto es 3. También se pueden utilizar los modificadores + y - para indicar un incremento (o decremento) relativo del tamaño del tipo de letra. Así, por ejemplo, si se indica un tamaño de -2 se pide al navegador que muestre el tipo de letra dos veces más pequeño que el actual. <FONT SIZE=2>Tamaño 2<FONT SIZE="+3" >Tamaño 5</FONT></FONT> Tipo de Letra Por último, se puede especificar el nombre del tipo de letra que se desea utilizar gracias al parámetro FACE. Como en principio no se tiene manera de saber que tipo de letra tiene instalado el computador del usuario que está viendo la página, se puede indicar más de uno separado por comas. Si el navegador no encuentra ninguno seguirá utilizando el que tiene por defecto: <FONT FACE="Helvetica,Arial,Times" >No sé como voy a salir exactamente</font> De todos modos es recomendable no utilizar confiadamente este atributo en Internet, ya que impide que todos puedan ver las páginas como se desea. Internet, siempre que lo permitan Microsoft y Netscape, debe ser lo más estándar posible Estructura del Documento La estructura de un documento HTML se puede resumir así: tipo de documento <HTML> <HEAD> <TITLE>titulo de la página</title> cosas que afectan a la página, pero no a su contenido </HEAD> <BODY ATRIBUTO> contenido de la página </BODY> </HTML> En el tipo de documento se debe especificar a que estándar de HTML responde la página entre una de las siguientes opciones: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN" > Cumple el estándar HTML 2.0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN" > Cumple el estándar HTML 3.2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > Cumple el estándar HTML 4.0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" > Cumple el estándar HTML 4.0 y no contiene además elementos desaconsejables <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" > Es una definición de marcos que cumple el estándar HTML 4.0 El HTML 4.0 considera desaconsejables aquellos elementos que, aún siendo soportados, han sido sustituidos por otros más potentes y, por ello, es posible que sean eliminados del estándar HTML en el futuro. Michael Moossen Departamento de Informática. U.T.F.S.M. 119

132 5.2. HTML La Cabecera Suele ser el lugar más indicado para colocar aquellos elementos de la página que no alteran el contenido de la misma, aunque si la forma de presentarlo y su comportamiento. Es por eso que es el lugar más recomendable para colocar los scripts y las hojas de estilo. Además del título de la página, uno de los elementos que se pueden incluir aquí son los META. Entre otras cosas, sirven para indicar propiedades de la página como pueda ser el nombre de su autor. Por ejemplo, <META NAME="GENERATOR" CONTENT="Mozilla/4.03 [es] (Win95; I) [Netscape]" > Esto indica la herramienta con que se ha creado la página (en este caso la versión 4.03 en español para Windows 95 del Composer de Netscape). Estas son las propiedades más comunes: AUTHOR Autor de la página. GENERATOR Herramienta utilizada para hacer la página. CLASSIFICATION Palabras que permite clasificar la página dentro de un buscador jerárquico (como Yahoo). KEYWORDS Palabras clave por las que encontrarán la página los buscadores. DESCRIPTION Descripción del contenido de la página. Hay también otro elemento interesante que se puede incluir en nuestras cabeceras. Cuando se específica una URL relativa en un enlace, en principio, es para acceder a una página situada en el mismo servidor. Sin embargo, si se específica: <BASE HREF="http://www.inf.utfsm.cl" > Ahora todas las URLs relativas se referirán al servidor El Cuerpo A continuación, una breve descripción de los atributos más importantes que admite la etiqueta <BODY>: BACKGROUND Permite definir un gráfico de fondo para la página. BGCOLOR Permite definir el color de fondo de la página. BGPROPERTIES Cuando es igual a FIXED el gráfico definido como fondo de la página permanecerá inmóvil. TEXT Cambia el color del texto. LINK Cambia el color de un enlace no visitado (por defecto azul). VLINK Cambia el color de un enlace ya visitado (por defecto púrpura). 120 Departamento de Informática. U.T.F.S.M. Michael Moossen

133 CAPÍTULO 5. JAVA E INTERNET ALINK Cambia el color que toma un enlace mientras se están pulsando (por defecto rojo). MARGINWIDTH y MARGINHEIGHT Especifican el número de pixels que dejará de margen entre el borde de la ventana y el contenido de la página. Se suelen utilizar para dejarlos a cero. No resulta recomendable cambiar los colores del texto y enlaces a no ser que exista alguna dificultad al leerlos por haber cambiado el fondo, ya que en muchas ocasiones el usuario ha podido cambiarlos en las opciones de su navegador y estarán ya a su gusto Formularios Una de las mayores ventajas de la web es que resulta interactiva. Los usuarios de una página no tienen más que escribir al autor de la misma para comentarle cualquier cosa de ella. Sin embargo, si se desea manipular alguna información concreta(responder a alguna pregunta, seleccionar entre varias opciones, etc..) se deben utilizar formularios. Por ejemplo, <FORM ACTION=" " METHOD=POST> Nombre:<BR><INPUT NAME="nombre" TYPE=TEXT SIZE=32> <BR>>Cuantos son dos y dos?<br> <INPUT NAME="Respuesta" TYPE=RADIO VALUE="mal" >3<BR> <INPUT NAME="Respuesta" TYPE=RADIO VALUE="bien" >4<BR> <INPUT NAME="Respuesta" TYPE=RADIO VALUE="mal" >5<BR> <INPUT TYPE="SUBMIT" VALUE="Comprobar" > </FORM> Que se verá así: El botón no hace nada porque no se ha definido qué debe hacer, normalmente se redirecciona a otra página que procesa la información, está se ingresa en el atributo ACTION de la etiqueta <FORM>. También se suele llamar a una función javascript, para hacer validaciones, por ejemplo. Todos los elementos de un formulario deben estar encerrados entre <FORM> y </FORM>. Como atributos cabe destacar tres. ACTION define la acción que deberá ejecutar el formulario, cuando se presione un botón de tipo SUBMIT. Puede ser una dirección de correo (precedida del inevitable mailto:), en cuyo caso se debe añadir el atributo ENCTYPE="text/plain" para que se reciba texto legible. Por otro lado, se tiene el atributo METHOD que define la manera en que se mandará los datos del formulario. Es recomendable utilizar POST. En el caso de que se mande el formulario a una dirección de correo electrónico, es obligatorio usarlo. Ahora se verán uno a uno todos los elementos que se pueden incluir en un formulario. Se verá que todos ellos tienen algo en común. Como el resultado de cualquier formulario es una lista de variables y valores asignados a las mismas, todos ellos tendrán un atributo en común: el nombre de su variable. El atributo también será común a todos: NAME. Michael Moossen Departamento de Informática. U.T.F.S.M. 121

134 5.2. HTML Cajas de Texto Existen tres maneras de conseguir que el usuario introduzca texto en un formulario. Las dos primeras se obtienen por medio de la etiqueta <INPUT>: <INPUT TYPE=TEXT> <INPUT TYPE=PASSWORD> El primero dibujará una caja donde se puede escribir texto(de una sola línea). El segundo es equivalente, pero no se verá lo que se ingrese en él. Estos son los atributos para modificarlos: SIZE Tamaño de la caja de texto. MAXLENGTH Número máximo de caracteres que puede introducir el usuario. VALUE Texto por defecto que contendrá la caja. Por otro lado, puede que se necesite que el usuario pueda introducir más de una línea. En ese caso, se utilizará la siguiente etiqueta: <TEXTAREA>Por defecto</textarea> Lo que se incluya entre las dos etiquetas será lo que se muestre por defecto dentro de la caja. Admite estos parámetros: ROWS Filas que ocupará la caja de texto. COLS Columnas que ocupará la caja de texto. Opciones Si lo que se desea es que el usuario decida entre varias opciones, se puede hacer de dos modos. El primero, es el que se vió en el ejemplo inicial: 3<INPUT NAME="Respuesta" TYPE=RADIO VALUE="mal" ><BR> 4<INPUT NAME="Respuesta" TYPE=RADIO VALUE="bien" ><BR> 5<INPUT NAME="Respuesta" TYPE=RADIO VALUE="mal" ><BR> Para asociar varios botones de radio a una misma variable se les pone a todos ellos el mismo NAME. Aparte de esto acepta los siguientes parámetros: VALUE Este es el valor que asignará a la variable. CHECKED Si se indica en una de las opciones esta será la que esté activada por defecto. Pero también se tiene la posibilidad de usar listas desplegables. Para emplearlas se deben utilizar dos etiquetas, SELECT y OPTION: 122 Departamento de Informática. U.T.F.S.M. Michael Moossen

135 CAPÍTULO 5. JAVA E INTERNET <SELECT NAME="Navegador" > <OPTION>Netscape <OPTION>Explorer <OPTION>Opera <OPTION>Lynx <OPTION>Otros </SELECT> Los atributos que admite SELECT son los siguientes: SIZE El número de opciones que se visualizan. Si es mayor que uno, se visualizará una lista de selección y, si no, se visualizará una lista desplegable(un ComboBox). MULTIPLE Si se indica, se podrá seleccionar más de una opción. Y OPTION estos: VALUE Este es el valor que asignará a la variable. SELECTED Si se indica en una de las opciones, esta será la seleccionada por defecto. Botones del Formulario Existen botones de dos tipos: uno que se utiliza para mandar el formulario y otro que sirve para limpiar todo lo que haya seleccionado el usuario: <INPUT TYPE=SUBMIT><BR> <INPUT TYPE=RESET> Se puede cambiar el texto que el navegador pone por defecto en esos botones, utilizando el atributo VALUE. Otros Elementos Puede que se necesite que el usuario sencillamente confirme o niegue una condición. Esto se puede lograr por medio de controles de confirmación: <INPUT NAME="moreinfo" TYPE=CHECKBOX>Desea recibir mayor información Si se quiere que el control esté activado por defecto, se le añade el atributo CHECKED. El formulario asignará a la variable NAME el valor on u off. Por último, existe la posibilidad de que se necesite tener en el formulario alguna variable con un valor previamente asignado y que no sea visible por el usuario. Por ejemplo, aunque no es la manera óptima de hacerlo, parámetros de control: <INPUT TYPE=HIDDEN NAME="TipoMenu" VALUE="VERTICAL" > Cuando el usuario haga clic sobre un botón de tipo SUBMIT, también se enviará la variable TipoMenu, con el valor asignado. Michael Moossen Departamento de Informática. U.T.F.S.M. 123

136 5.2. HTML Tablas Las tablas son posiblemente la manera más clara de organizar la información. También es el modo más adecuado de diseñar páginas de texto y gráficos de una manera más controlada que con los parámetros ALIGN. Las tablas se definen de la siguiente manera. Primero, las características de la tabla, luego las de cada fila y dentro de ésta, cada celda. Así pues, una tabla con 2 filas y 3 columnas se declarará así: Resultado: <TABLE> <TR> <TD>1,1</TD> <TD>1,2</TD> <TD>1,3</TD> </TR> <TR> <TD>2,1</TD> <TD>2,2</TD> <TD>2,3</TD> </TR> </TABLE> 1,11,21,3 2,12,22,3 Como se puede apreciar, no tiene mucho aspecto de tabla. Quedaría mejor con bordes, y con mayor espacio entre celdas o mayor anchura. Estos son los aspectos que se pueden cambiar con los atributos de TABLE: BORDER Especifica el grosor del borde que se dibujará alrededor de las celdas. Por defecto es cero, lo que significa que no dibujará borde alguno. CELLSPACING Define el número de pixels que separarán las celdas. CELLPADDING Especifica el número de pixels que habrá entre el borde de una celda y su contenido. WIDTH Especifica el ancho de la tabla. Puede estar tanto en pixels como en porcentaje del ancho total disponible para él, es decir, 100 % si se quiere ocupar todo el ancho de la ventana del navegador. ALIGN Alinea la tabla a izquierda (LEFT), derecha (RIGHT) o centro (CENTER). Si ahora, por ejemplo, se define la tabla anterior como: <TABLE BORDER=1 WIDTH="50 %" ALIGN=CENTER> Se verá lo siguiente: 124 Departamento de Informática. U.T.F.S.M. Michael Moossen

137 CAPÍTULO 5. JAVA E INTERNET Definición de Filas Ahora que se ha definido la tabla, es necesario hacer lo mismo con las filas. Cada fila se define con una etiqueta TR, que tiene los siguientes atributos: ALIGN Alinea el contenido de las celdas de la fila horizontalmente, a la izquierda (LEFT), la derecha (RIGHT) o al centro (CENTER). VALIGN Alinea el contenido de las celdas de la fila verticalmente, hacia arriba (TOP), abajo (BOTTOM) o al centro (MIDDLE). Definición de Celdas Por último, queda definir cada celda por medio de las etiquetas TD y TH. Estas etiquetas son equivalentes, pero la última se utiliza para encabezados, de modo que su interior se escribirá por defecto en negrita y centrado. Estos son los atributos de ambas: ALIGN Alinea el contenido de la celda horizontalmente, a la izquierda (LEFT), la derecha (RIGHT) o al centro (CENTER). VALIGN Alinea el contenido de la celda verticalmente, hacia arriba (TOP), abajo (BOTTOM) o al centro (MIDDLE). WIDTH Especifica el ancho de la celda. Se puede especificar tanto en pixels como en porcentaje, teniendo en cuenta que, en este último caso, será un porcentaje respecto al ancho total de la tabla (no de la ventana del navegador). NOWRAP Impide que al interior de la celda, se inserten saltos de línea para ajustar el texto al ancho de la celda. COLSPAN Especifica el número de celdas de la fila situadas a la derecha de la actual que se unen a ésta (incluyendo la celda en que se declara este parámetro). Es por defecto uno. Si se indica cero, se unirán todas las celdas que queden a la derecha. ROWSPAN Especifica el número de celdas de la columna situadas debajo de la actual, que se unen a ésta. Posiblemente los dos últimos parámetros no queden claros sin ejemplos. De hecho, aún entendiendo perfectamente su función, es habitual confundir uno con otro. Por lo tanto, se verá una tabla de 3 3 con una celda que se une a una de la derecha y otra que se une a su adyacente por debajo: <TABLE BORDER=1> <TR> <TD COLSPAN=2>1,1 y 1,2</TD> <TD>1,3</TD> </TR> <TR> <TD ROWSPAN=2>2,1 y 3,1</TD> <TD>2,2</TD> <TD>2,3</TD> </TR> Michael Moossen Departamento de Informática. U.T.F.S.M. 125

138 5.2. HTML Resultado: <TR> <TD>3,2</TD> <TD>3,3</TD> </TR> </TABLE> Como se puede ver, cuando se declara una celda con ROWSPAN o COLSPAN, no se deben declarar las celdas absorbidas Marcos Un marco (o frame) es una ventana independiente dentro de la ventana del navegador. Cada marco tiene sus bordes y sus propias barras de desplazamiento. Así, cada página se dividirá, en la práctica, en varias páginas independientes. Para crearlos se necesita un documento HTML específico, el documento de definición de marcos. En él se especifican el tamaño y posición de cada marco y el documento HTML que contendrá. A continuación, un ejemplo de este tipo de documento: <HTML> <HEAD> <TITLE>Mi primera página con marcos</title> </HEAD> <FRAMESET COLS="20 %,80 %" > <FRAME NAME="indice" SRC="indice.html" > <FRAME NAME="principal" SRC="introduccion.html" > <NOFRAMES> <P>Lo siento, pero sólo podrá ver esta página si su navegador tiene la capacidad de visualizar marcos. </NOFRAMES> </FRAMESET> </HTML> Se observa que la cabecera de la página es similar a un documento normal, pero el habitual BODY es sustituido por un FRAMESET. En cada FRAMESET se divide la ventana actual (sea la principal o un marco) en varias ventanas definidas por el atributo COLS o por ROWS. En estos atributos, separado por comas, se define el número de marcos y el tamaño de cada uno. Dentro del FRAMESET se definen cada uno de los marcos, dándoles un nombre y especificando el archivo HTML que le corresponde mediante la etiqueta FRAME. También, se especifica lo que visualizará el usuario en el supuesto (cada vez más raro) de que su navegador no soporte frames dentro de la etiqueta NOFRAMES. Ahora se verán todos estos elementos en mayor detalle. Etiqueta FRAMESET Según el estándar, esta etiqueta sólo debería contener el número y tamaño de cada marco, pero las extensiones de Netscape y Explorer al estándar, obligan a estudiar un par de parámetros más. 126 Departamento de Informática. U.T.F.S.M. Michael Moossen

139 CAPÍTULO 5. JAVA E INTERNET En general, los navegadores dibujan un borde de separación entre los marcos. Esto se puede manejar de dos maneras: utilizando el atributo BORDER en las etiquetas FRAME de cada uno de los marcos contiguos o incluyendo el atributo FRAMEBORDER en la etiqueta FRAMESET. También se puede controlar el espacio ente marcos, mediante el atributo FRAMESPACING. Por último, los parámetros COLS y ROWS, se les debe asignar una lista de tamaños separada por comas. Se admiten los siguientes formatos de tamaño: Con Porcentajes: Al igual que con las tablas, se puede definir el tamaño de un marco como un porcentaje del espacio total disponible. Absolutos: Si se utiliza un número a secas, el marco correspondiente tendrá el tamaño especificado en pixels. Sobre el Espacio Sobrante: Si se utiliza un asterisco(*), se hace referencia al espacio sobrante. Se puede utilizar este símbolo en varios marcos, que se repartirán el espacio equitativamente. Así, un marco con un espacio de 3*, será tres veces más grande que su compañero, que tiene un asterisco sólo. Por ejemplo, el siguiente código es una muestra de cómo combinar los tres métodos: <FRAMESET COLS="10 %,*,200,2*" > Supóngase que el ancho total de la ventana son 640 pixels. El primer marco ocupará el 10 %, es decir, 64 pixels. El tercero necesita 200 pixeles, luego quedan 476 pixeles para los otros dos marcos. Como el cuarto debe tener el doble de espacio que el segundo, se tienen aproximadamente 158 pixels para este último y 316 para el cuarto marco. Tener cuidado cuando se usan valores absolutos en la definición de marcos; se debe asegurar de tener al menos un marco con un tamaño relativo, si se quiere estar seguro del aspecto final de la página. Por último, considérese que las etiquetas FRAMESET se pueden anidar. Esto se hace colocando otro FRAMESET donde normalmente se colocaría una etiqueta FRAME tal que así: <FRAMESET COLS="20 %,80 %" > <FRAME NAME="indice" SRC="indice.html" > <FRAMESET ROWS="*,80" > <FRAME NAME="principal" SRC="introduccion.html <FRAME NAME="ejemplos" SRC="ejemplo.html </FRAMESET> </FRAMESET> El resultado del anidamiento se puede contemplar a continuación: Etiqueta FRAME Esta etiqueta define las características de un marco determinado. Estos son los atributos que admite: Michael Moossen Departamento de Informática. U.T.F.S.M. 127

140 5.2. HTML NAME Asigna un nombre a un marco para que después se pueda hacer referencia a él. SRC Indica la dirección del documento HTML que ocupará el marco. SCROLLING Decide si se colocan o no barras de desplazamiento al marco para que poder navergar por su contenido. Su valor es por defecto AUTO, que deja al navegador la decisión. Las otras opciones que se tienen son YES y NO. NORESIZE Si se especifica el usuario no podrá cambiar de tamaño el marco. FRAMEBORDER Es idéntico a su homónimo en la etiqueta FRAMESET. MARGINWIDTH Permite cambiar los márgenes horizontales dentro de un marco. Se representa en pixels. MARGINHEIGHT Igual al anterior pero para márgenes verticales. Acceso a Otros Marcos Por defecto, cuando se pulsa sobre un enlace situado dentro de un marco, la nueva página a la que se quiere acceder, será cargada en ese mismo marco. Es posible que se desee que esto no ocurra. Por ejemplo, si se tiene un marco que sirve de índice y otro donde se muestran los contenidos, sería deseable que los enlaces del marco índice se abrieran en el otro marco. Esto es posible hacerlo gracias al atributo TARGET. Este atributo se puede usar en tres etiquetas: A, AREA y BASE. En las dos primeras sirve para indicar el marco en el que se cargará ese enlace en particular y el último modifica el marco en el que por defecto se cargan todos los enlaces. Pero para que un atributo funcione, es habitual que se asigne un valor, y TARGET no es una excepción. Para indicarle el marco que se desea, se le asigna el nombre del mismo. Así, en el ejemplo anterior, si en el marco llamado indice, se tiene un enlace que se quiera abrir en el marco principal, se utiliza: <A HREF="pagina.html" TARGET="principal" > También existen cuatro nombres reservados que se pueden utilizar en el parámetro TARGET: top Elimina todos los marcos existentes y muestra la nueva página en la ventana original del navegador sin marcos. blank Muestra la nueva página en una ventana nueva y sin nombre del navegador. self Muestra la nueva página en el marco donde está declarado el enlace. parent Muestra la nueva página en el FRAMESET que contiene al marco donde se declara el enlace. En el ejemplo anterior, un enlace situado en el marco ejemplo cuyo parámetro TARGET fuese igual a parent, eliminaría la separación entre los marcos ejemplos y principal y mostraría en ese nuevo marco la nueva página. 128 Departamento de Informática. U.T.F.S.M. Michael Moossen

141 CAPÍTULO 5. JAVA E INTERNET 5.3. Java Server Pages Java Server Pages (JSP) permite separar la parte dinámica de las páginas Web del HTML estático. Simplemente se escribe el HTML regular de forma normal. Luego, se encierra el código de las partes dinámicas entre etiquetas especiales, la mayoría de las cuales empiezan con "< %" y terminan con " %>". Por ejemplo, aquí se muestra una sección de una página JSP que resulta en algo así como Ud. acaba de ganar $2 Millones, jugando Loto para una URL como Ud. acaba de ganar <B>$ < %=new java.util.random().nextint(10) %>Millones</B>, jugando <I>< %= request.getparameter("juego") %></I> Normalmente se usa la extensión.jsp, y los archivos JSP se instalan en el mismo sitio que una página Web normal. Aunque lo que se escriba, frecuentemente se parezca a un archivo HTML normal, detrás de la escena, la página JSP se traduce en un servlet, donde el HTML estático simplemente se imprime en el flujo de salida estándard asociado con el método service() del servlet. Esto normalmente sólo se hace la primera vez que se solicita la página, y se puede solicitar la página explicitamente cuando se instala por primera vez, si se quiere estar seguro de que el primer usuario real no tenga un retardo momentáneo cuando la página JSP sea traducida a un servlet, y este sea compilado y cargado. Observese también, que muchos servidores Web permiten definir alias para que una URL que parece apuntar a un archivo HTML, realmente apunte a un servelt o a una página JSP. Además del HTML normal, hay tres tipos de construcciones JSP que se pueden incrustar en una página JSP: elementos de script, directivas y acciones. Los elementos de script permiten especificar código Java que se convertirá en parte del servlet resultante, las directivas permiten controlar la estructura general del servlet, y las acciones permiten especificar componentes que deberían ser usuados, y de otro modo controlar el comportamiento del motor JSP. Para simplificar los elementos de script, se tiene acceso a un número de variables predefinidas como request del fragmento de código anterior Plantilla de Texto En muchos casos, un gran porcentaje de las páginas JSP consistirá en HTML estático, conocido como plantilla de texto. En casi todos lo s aspectos, este HTML es HTML normal, sigue la mismas reglas de sintaxis, y simplemente pasa a través del cliente por el servlet creado para manejar la página. Hasta puede ser creado con cualquier herramienta que se desee para generar páginas Web. La única excepción a la regla de que la plantilla de texto se pasa tal y como es es que, si se quier tener "< %" en la salida, se debe escribir "<\ %" en la plantilla de texto Elementos de Script Los elementos de script permiten insertar código Java dentro del servlet que se generará desde la página JSP actual. Hay tres formas: Expresiones que son evaluadas e insertadas en la salida. Scriptlets que se insertan dentro del método service del servlet, y Declaraciones que se insertan en el cuerpo de la clase del servlet, fuera de cualquier método existente. Expresiones Una expresión JSP se usa para insertar valores Java directamente en la salida. Tiene la siguiente forma: < %= expresion %> La expresion es evaluada, convertida a un string, e insertada en la página. Esta evaluación se realiza durante la ejecución (cuando se solicita la página) y así se tiene total acceso a la información sobre la solicitud. Por ejemplo, esto muestra la fecha y hora en que se solicitó la página: Michael Moossen Departamento de Informática. U.T.F.S.M. 129

142 5.3. JAVA SERVER PAGES La fecha: < %= new java.util.date() %> Para simplificar estas expresiones, hay un gran número de variables predefinidas que se pueden usar. Estos objetos implícitos se describen más adelante con más detalle, pero para el propósito de las expresiones, los más importantes son: request: Un HttpServletRequest, que permite la comunicación del servidor a la página. response: Un HttpServletResponse, que permite la comunicación de la página al servidor. session: Un HttpSession asociada con el request, si existe, que contiene la información de la sesión de trabajo del usuario. out: Un PrintWriter, una versión con buffer del tipo JspWriter, usada para envíar la salida al cliente. Aquí un ejemplo: Su máquina: < %= request.getremotehost() %> Scriptlets Si se quiere hacer algo más complejo que insertar una simple expresión, los scriptlets JSP permiten insertar código arbitrario dentro del servlet que será construido al generar la página. Los Scriptlets tienen la siguiente forma: < % codigo %> Los Scriptlets tienen acceso a las mismas variables predefinidas que las expresiones. Por eso, por ejemplo, si se quiere que la salida aparezca en la página resultante, se debe usar la variable out: <% String querydata = request.getquerystring(); out.println("datos atachados: "+ querydata); %> Obsérvese que el código dentro de un scriptlet se insertará exactamente como está escrito, y cualquier HTML estático (plantilla de texto) anterior o posterior al scriptlet, se convierte en sentencias print. Esto significa que los scriptlets no necesitan completar las sentencias Java, y los bloques abiertos pueden afectar al HTML estático fuera de los scriplets. Por ejemplo, el siguiente fragmento JSP, contiene una mezcla de texto y scritplets: <% if (Math.random() <0.5) { %> Voy a ver tele <% else { %> Voy a trabajar <% %> Que se traducirá en algo como: <% if (Math.random() <0.5) { %> out.println("voy a ver tele"); <% else { %> out.println("voy a trabajar"); <% %> Si se quiere usar los caracteres " %>" dentro de un scriptlet, se debe usar "\ %>". 130 Departamento de Informática. U.T.F.S.M. Michael Moossen

143 CAPÍTULO 5. JAVA E INTERNET Declaraciones Una declaración JSP permite definir métodos o campos que serán insertados dentro del cuerpo principal de la clase Servlet (fuera del método service() que procesa la petición). Tiene la siguiente forma: < %! codigo %> Como las declaraciones no generan ninguna salida, normalmente se usan en conjunción con expresiones o scriptlets. Por ejemplo, aquí se tiene un fragmento JSP, que imprime el número de veces que se ha solicitado la página actual desde que el servidor se arrancó (o la clase del servlet se modificó o se recargó): <%! private int accesscount = 0;%> Cantidad de accesos a la página: < %= ++accesscount %> Como con los scriptlet, si se quiere usar los caracteres " %>", se debe usar "\ %>" Directivas Una directiva JSP afecta a la estructura general de la clase Servlet. Normalmente tienen la siguiente forma: <% directiva AtributoA="valor1"... AtributoN="valorN"%> Hay dos tipos principales de directivas: page, que permite importar clases, personalizar la superclase del servlet, etc. e include, que permite insertar un archivo dentro de la clase Servlet al momento que el archivo JSP es traducido a un servlet. La Directiva page La directiva page permite definir uno o más de los siguientes atributos(sensibles a las mayúsculas): import Permite especificar los paquetes que deben ser importados. Por ejemplo: <% page import="java.util.*"%> El atributo import es el único que puede aparecer múltiples veces. contenttype Especifica el tipo MIME de la salida. El valor por defecto es text/html. Por ejemplo, la directiva: <% page contenttype="text/plain"%> Tiene el mismo resultado que el scriptlet: <% response.setcontenttype("text/plain"); %> isthreadsafe Un valor true (por defecto), indica un procesamiento del servlet normal, donde múltiples peticiones pueden procesarse concurrentemente con un sólo ejemplar del servlet, bajo la suposición que el autor sincroniza las variables de ejemplar. Un valor false, indica que el servlet debería implementar SingleThreadModel, con peticiones enviadas serialmente o con peticiones concurrentes, siendo entregadas a distintos ejemplares del servlet. session Un valor true (por defecto), indica que la variable predefinida session(del tipo HttpSession) debería unirse a la sesión existente si existe una, y si no existe, se debería crear una nueva sesión para unirla. Un valor false, indica que no se usarán sesiones, y los intentos de acceder a la variable session resultarán ser errores al momento en que la página JSP sea traducida a un servlet. Michael Moossen Departamento de Informática. U.T.F.S.M. 131

144 5.3. JAVA SERVER PAGES buffer Esto especifica el tamaño del buffer para el JspWriter out en kilobytes. El valor por defecto es específico del servidor, y debería ser de al menos 8Kb. autoflush Un valor true (por defecto), indica que el buffer debería descargase cuando esté lleno. Un valor false, raramente utilizado, indica que se debe lanzar una excepción cuando el buffer se sobrecargue. Un valor false, es ilegal cuando se usa buffer="none". extends Esto indica la superclase del servlet que se va a generar. Se debe usar con extrema precaución, ya que el servidor podría utilizar una superclase personalizada. info Define un string que puede usarse para ser recuperado mediante el método getservletinfo. errorpage Especifica una página JSP que se debería procesar, si se lanzara cualquier Throwable, pero no fuera capturado en la página actual. iserrorpage Indica si la página actual actúa o no como página de error de otra página JSP. El valor por defecto es false. La Directiva include Esta directiva permite incluir archivos en el momento en que la página JSP es traducida a un servlet. La directiva tiene esta forma: <% include file="url relativa"%> La URL especificada normalmente se interpreta como relativa a la página JSP a la que se refiere. Los contenidos del archivo incluido son analizados como texto normal JSP, y así pueden incluir HTML estático, elementos de script, directivas y acciones. Por ejemplo, muchos sitios incluyen una pequeña barra de navegación en cada página. Debido a los problemas con los marcos HTML, esto normalmente se implementa mediante una pequeña tabla que cruza la parte superior de la página o el lado izquierdo, con el HTML repetido para cada página del sitio. La directiva include es una forma natural de hacer esto, ahorrando a los desarrolladores el mantenimiento engorroso de copiar realmente el HTML en cada archivo separado, sin mencionar los costos de mantenimiento. Aquí se tiene un ejemplo: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD>... </HEAD> <BODY> <% include file="/navbar.html"%>... </BODY> </HTML> Obsérvese que como la directiva include inserta los archivos en el momento en que la página es traducida, si la barra de navegación cambia, es necesario re-traducir todas las páginas JSP que la referencian. Esto es un buen 132 Departamento de Informática. U.T.F.S.M. Michael Moossen

145 CAPÍTULO 5. JAVA E INTERNET compromiso en una situación como esta, ya que las barras de navegación no cambian frecuentemente, y se quiere que el proceso de inclusión sea tan eficiente como sea posible. Si, sin embargo, los archivos incluidos cambian de forma más frecuente, se podría usar la acción jsp :include en su lugar. Esto incluye el archivo en el momento en que se solicita la página JSP, como se describe posteriormente Variables Predefinidas Para simplificar el código en expresiones y scriplets JSP, se tienen ocho variables predefinidas, algunas veces llamadas objetos implícitos. Las variables disponibles son: request, response, out, session, application, config, pagecontext, y page. request Este es el HttpServletRequest asociado con la petición, y permite usar los parámetros de la petición (mediante el método getparameter()), el tipo de petición (GET, POST, HEAD, etc.), y las cabeceras HTTP entrantes (cookies, Referer, etc.). Estrictamente hablando, se permite que la petición sea una subclase de ServletRequest distinta de HttpServletRequest, si el protocolo de la petición es distinto de HTTP. response Este es el HttpServletResponse asociado con la respuesta al cliente. Permite entre otras cosas, obtener el flujo de salida (ver out más abajo). out Este es el PrintWriter usado para envíar la salida al cliente, que se obtiene(automáticamente) del objeto response. Es una versión con buffer de PrintWriter llamada JspWriter. Obsérvese que se puede ajustar el tamaño del buffer, o incluso desactivarlo, usando el atributo buffer de la directiva page. out se usa casi exclusivamente en scriptlets, ya que las expresiones JSP obtienen(automáticamente) un lugar en el flujo de salida, y por eso raramente se refieren explícitamente a out. session Este es el objeto HttpSession asociado con la petición. Las sesiones se crean automáticamente, por ello esta variable se une incluso si no hubiera una sesión de referencia entrante. La única excepción es al usar el atributo session de la directiva page para desactivar las sesiones, en cuyo caso los intentos de referenciar la variable session causarán un error al momento de traducir la página JSP a un servlet. application Este es el ServletContext obtenido mediante getservletconfig().getcontext(). Mediante sus métodos getattribute y setattribute, es posible almacenar cualquier tipo de dato arbitrario y compartirlo con otras páginas JSP o servlet, ya que todas las páginas de una misma aplicación web comparten el mismo objeto application config Este es el objeto ServletConfig para esta página. pagecontext JSP presenta una nueva clase llamada PageContext para encapsular características de uso específicas del servidor como JspWriters de alto rendimiento. También puede ser un objeto alternativo para compartir datos, pero como es específico de cada servidor es poco usado. Michael Moossen Departamento de Informática. U.T.F.S.M. 133

146 5.3. JAVA SERVER PAGES page Esto es sólo un sinónimo de this, y aún no es muy útil Accciones Las acciones JSP usan construcciones de síntaxis XML para controlar el comportamiento del motor de Servlets. Se puede insertar un archivo dinámicamente, reutilizar componentes JavaBeans, generar código genérico para la invocación de un applet, al igual que la etiqueta APPLET o reenviar al usuario a otra página. Notar que, como en XML, los nombre de elementos y atributos son sensibles a las mayúsculas. Las acciones que se verán en este documento son: Acción jsp:include Esta acción permite insertar archivos en una página que está siendo generada. La síntaxis es: <jsp:include page ="URL" flush="true" /> Al contrario que la directiva include, que inserta el archivo en el momento de la conversión de la página JSP a un servlet, esta acción inserta el archivo en el momento en que la página es solicitada. Esto se paga con eficiencia, e imposiblita a la página incluida contener código JSP general (no puede seleccionar cabeceras HTTP, por ejemplo), pero se obtiene una significante flexibilidad. Acción jsp:forward Esta acción permite reenviar la petición a otra página. Tiene un sólo atributo, page, que debería consistir en una URL relativa. Este podría ser un valor estático, o podría ser calculado en el momento de la petición, como en estos dos ejemplo: <jsp:forward page="/utils/errorreporter.jsp" /> <jsp:forward page=" < %= somejavaexpression %>" /> Ejemplo Aquí se muestra un sencillo ejemplo que muestra el uso de expresiones, scriptlets, declaraciones y directivas JSP: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD> <TITLE>Ejemplo de JSP</TITLE> <META NAME="author" CONTENT="Michael Moossen" > <META NAME="keywords" CONTENT="JSP,Java Server Pages,servlets" > <META NAME="description" CONTENT="Un ejemplo de JSP." > </HEAD> <BODY BGCOLOR="#FDF5E6" TEXT="#000000" LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000" > <CENTER> <TABLE BORDER=5 BGCOLOR="#EF8429" > <TR><TH> Ejemplo de Java Server Pages </TH></TR> 134 Departamento de Informática. U.T.F.S.M. Michael Moossen

147 CAPÍTULO 5. JAVA E INTERNET </TABLE > </CENTER> <P> Algunos mecanismos JSP para crear contenido dinámico: <UL> <LI><B>Expresiones:</B><BR> Su máquina: < %= request.getremotehost() %>. <LI><B>Scriptlets:</B><BR> <% out.println("datos atachados: "+ request.getquerystring()); %> <LI><B>Declaraciones:</B><BR> <%! private int accesscount = 0;%> Cantidad de accesos a la página: < %= ++accesscount %> <LI><B>Directivas:</B><BR> <% page import= "java.util.*"%> La fecha: < %= new Date() %> </UL> </BODY> </HTML> Aquí un resultado típico: 5.4. Applets Un applet es una mini-aplicación, escrita en Java, que se ejecuta en un browser (Netscape Navigator, Microsoft Internet Explorer, etc.) al cargar una página HTML que incluye información sobre el applet a ejecutar por medio de los tags <APPLET>... </APPLET>. A continuación se detallan algunas características de los applets: Los archivos de Java compilados(*.class) se descargan a través de la red desde un servidor Web o servidor HTTP hasta el browser en cuya JVM se ejecutan. Pueden incluir también archivos de imágenes y sonido. Los applets no tienen ventana propia: se ejecutan en la ventana del browser (en un panel). Por la propia naturaleza abierta de Internet, los applets tienen importantes restricciones de seguridad, que se comprueban al llegar al browser: sólo pueden leer y escribir archivos en el servidor del que han venido, sólo pueden acceder a una limitada información sobre el computador en el que se están ejecutando, etc. Con ciertas condiciones, los applets de confianza (trusted applets) pueden pasar por encima de estas restricciones. Michael Moossen Departamento de Informática. U.T.F.S.M. 135

148 5.4. APPLETS Aunque su entorno de ejecución es un browser, los applets se pueden probar sin necesidad de browser con la aplicación appletviewer del JDK de Sun. Las características de los applets más importantes, desde el punto de vista del programador, son: Los applets no tienen un método main() con el que comience la ejecución. El papel central de su ejecución lo asumen otros métodos que se verán posteriormente. Todos los applets derivan de la clase java.applet.applet. Los applets deben redefinir ciertos métodos heredados de Applet que controlan su ejecución: init(), start(), stop(), destroy(). Se heredan otros muchos métodos de las super-clases de Applet, relacionados con la generación de interfaces gráficas de usuario (AWT). Así, los métodos gráficos se heredan de Component, mientras que la capacidad de añadir componentes de interface de usuario se hereda de Container y de Panel. Los applets también suelen redefinir ciertos métodos gráficos: los más importantes son paint() y update(), heredados de Component y de Container; y repaint() heredado de Component. Los applets disponen de métodos relacionados con la obtención de información, como por ejemplo: getappletinfo(), getappletcontext(), getparameterinfo(), getparameter(), getcodebase(), getdocumentbase(), e isactive(). El método showstatus() se utiliza para mostrar información en la barra de estado del browser. Existen otros métodos relacionados con imágenes y sonido: getimage(), getaudioclip(), play(), etc El Tag APPLET de HTML Para llamar a un applet desde una página HTML se utiliza el tag <APPLET>...</APPLET>, cuya forma general es (los elementos opcionales aparecen entre corchetes): <APPLET CODE="miApplet.class" [CODEBASE="unURL"] [NAME="unName"] WIDTH="wpixels" HEIGHT="hpixels" [ALT="TextoAlternativo"] ARCHIVE="file1.zip, file2.jar" > [<PARAM NAME="MyName1" VALUE="valueOfMyName1" >] [<PARAM NAME="MyName2" VALUE="valueOfMyName2" >] </APPLET> El atributo NAME permite dar un nombre opcional al applet, con objeto de poder comunicarse con otros applets o con otros elementos que se estén ejecutando en la misma página. El atributo ARCHIVE permite indicar uno o varios archivos Jar o Zip (separados por comas), donde se deben buscar las clases. El atributo ALT permite indicar texto alternativo para browsers que reconocen el tag <APPLET> pero no pueden ejecutar el applet. Otros posibles atributos de <APPLET> son ALIGN, VSPACE y HSPACE, que tienen el mismo significado que el tag IMG de HTML. Paso de Parámetros Los tags PARAM permiten pasar diversos parámetros desde el archivo HTML al programa Java del applet, de forma análoga a la que se utiliza para pasar argumentos a main(). Cada parámetro tiene un nombre y un valor. Ambos se dan en forma de String, aunque el valor sea numérico. El applet recupera estos parámetros y, si es necesario, convierte los Strings en valores numéricos. El valor de los parámetros se obtienen con el siguiente método de la clase Applet: String getparameter(string name) La conversión de Strings a los tipos primitivos se puede hacer con los métodos asociados a los wrappers que Java proporciona para dichos tipos fundamentales (Integer.parseInt(String), Double.valueOf(String),...). En los nombres de los parámetros no se distingue entre mayúsculas y minúsculas, pero sí en los valores, ya que serán interpretados por un programa Java, que sí distingue. 136 Departamento de Informática. U.T.F.S.M. Michael Moossen

149 CAPÍTULO 5. JAVA E INTERNET El programador del applet debería prever siempre unos valores por defecto para los parámetros del applet, para el caso de que en la página HTML que llama al applet no se definan. El método getparameterinfo() devuelve una matriz de strings (String[][]) con información sobre cada uno de los parámetros soportados por el applet: nombre, tipo y descripción, cada uno de ellos en un String. Este método debe ser redefinido por el programador del applet y utilizado por la persona que prepara la página HTML que llama al applet. En muchas ocasiones serán personas distintas, y ésta es una forma de que el programador del applet dé información al usuario Ciclo de Vida de un Applet El ciclo de vida de una applet, es como sigue: primero se inicializa usando el método init(), luego comienza su ejecución el en método start(), esta ejecución puede ser detenida temporalmente a través del método stop, y finalizada por medio del método destroy(). A continuación se detalla el manejo de cada uno de estos métodos: init() Se llama automáticamente al método init() en cuanto el browser o visualizador carga el applet. Este método se ocupa de todas las tareas de inicialización, realizando las funciones del constructor (al que el browser no llama). start() El método start() se llama automáticamente en cuanto el applet se hace visible, después de haber sido inicializada. Se llama también cada vez que el applet se hace de nuevo visible, después de haber estado oculta (por dejar de estar activa esa página del browser, al cambiar el tamaño de la ventana del browser, al hacer reload, etc.). Es habitual crear threads en este método para aquellas tareas que, por el tiempo que requieren, dejarían sin recursos al applet o incluso al browser. Las animaciones y ciertas tareas a través de Internet son ejemplos de este tipo de tareas. stop() El método stop() se llama de forma automática al ocultar el applet (por haber haber dejado de estar activa la página del browser, por hacer reload o resize, etc.). Con objeto de no consumir recursos inútilmente, en este método se suelen parar los threads que estén corriendo en el applet, por ejemplo para mostrar animaciones. destroy() Se llama a este método cuando el applet va a ser descargado para liberar los recursos que tenga reservados (excepto la memoria). Generalmente, no es necesario redefinir este método, pues el que se hereda cumple bien con esta misión. Generalmente el programador tiene que redefinir uno o más de estos métodos, pero no tiene que preocuparse de llamarlos: el browser se encarga de hacerlo. Además, si el applet usa funciones gráficas, se deben manejar algunos métodos adicionales, que se verán a continuación Métodos para Dibujar un Applet Los applets son aplicaciones gráficas que aparecen en una zona de la ventana del browser. Por ello deben redefinir los métodos gráficos paint() y update() de la clase java.awt.container de la cual hereda Applet. El método paint() se declara en la forma: void paint(graphics g) El objeto gráfico g pertenece a la clase java.awt.graphics, que siempre debe ser importada por el applet. Este objeto define un contexto o estado gráfico para dibujar (métodos gráficos, colores, fonts, etc.) y es creado por el browser. Todo el trabajo gráfico del applet (dibujo de líneas, formas gráficas, texto, etc.) se debe incluir en el método Michael Moossen Departamento de Informática. U.T.F.S.M. 137

150 5.4. APPLETS paint(), porque este método es llamado cuando el applet se dibuja por primera vez y también de forma automática cada vez que el applet se debe redibujar. En general, el programador crea el método paint() pero no lo suele llamar. Para pedir explícitamente al sistema que vuelva a dibujar el applet (por ejemplo, por haber realizado algún cambio) se utiliza el método repaint(), que es más fácil de usar, pues no requiere argumentos. Este método es parte de java.awt.component, de la cual Applet también hereda. El método repaint() se encarga de llamar a paint() a través de update(). El método repaint() llama a update(), que borra todo pintando de nuevo con el color de fondo y luego llama a paint(). A veces esto produce parpadeo de pantalla o flickering. Existen dos formas de evitar el flickering: Redefinir update() de forma que no borre toda la ventana sino sólo lo necesario. Redefinir paint() y update() para utilizar doble buffer. Eliminación de Parpadeo Redefiniendo el Método update() El problema del flicker se localiza en la llamada al método update(), que borra todo pintando con el color de fondo y después llama a paint(). Una forma de resolver esta dificultad, es redefinir el método update(), de forma que se adapte mejor al problema que se trata de resolver. Una posibilidad es no repintar todo con el color de fondo, no llamar a paint() e introducir en update() el código encargado de realizar los dibujos, cambiando sólo aquello que haya que cambiar. A pesar de esto, es necesario redefinir paint(), pues es el método que se llama de forma automática cuando la ventana de Java es tapada por otra que luego se retira. Una posible solución es hacer que paint() llame a update(), terminando por establecer un orden de llamadas opuesto al de defecto. Hay que tener en cuenta que, al no borrar todo pintando con el color de fondo, el programador tiene que preocuparse de borrar de forma selectiva entre frame y frame, lo que sea necesario. Los métodos setclip() y cliprect() de la clase Graphics permiten hacer que las operaciones gráficas no surtan efecto fuera de un área rectangular previamente determinada. Al ser dependiente del tipo de gráficos concretos de que se trate, este método no siempre proporciona soluciones adecuadas. Técnica de Doble Buffer La técnica del doble buffer proporciona la mejor solución para el problema de las animaciones, aunque requiere una programación algo más complicada. La idea básica del doble buffer es realizar los dibujos en una imagen invisible, distinta de la que se está viendo en la pantalla, y hacerla visible cuando se ha terminado de dibujar, de forma que aparezca instantáneamente. Para crear el segundo buffer o imagen invisible, hay que crear un objeto de la clase Image del mismo tamaño que la imagen que se está viendo y crear un contexto gráfico u objeto de la clase Graphics que permita dibujar sobre la imagen invisible. Esto se hace con las sentencias: Image imginv; Graphics graphinv; Dimension diminv; Dimension d = size(); // se obtiene la dimensión del panel en la clase que controle el dibujo (por ejemplo en una clase que derive de Panel). En el método update(), se modifica el código de modo que primero se dibuje en la imagen invisible y luego ésta se haga visible: public void update(graphics g) { // se comprueba si existe el objeto invisible y si sus dimensiones son correctas if ((graphinv==null ) (d.width!=diminv.width) (d.height!=diminv.height)) { diminv = d; // se llama al método createimage de la clase Component imginv = createimage(d.width, d.height); // se llama al método getgraphics de la clase Image graphinv = imginv.getgraphics(); 138 Departamento de Informática. U.T.F.S.M. Michael Moossen

151 CAPÍTULO 5. JAVA E INTERNET // se establecen las propiedades del contexto gráfico invisible, // y se dibuja sobre él graphinv.setcolor(getbackground());... // finalmente se hace visible la imagen invisible a partir del punto (0, 0) // utilizando el propio panel como ImageObserver g.drawimage(imginv, 0, 0, this ); // fin del método update() Los gráficos y las animaciones son particularmente útiles en los applets Comunicación entre Applet y Browser La comunicación entre el applet y el browser en el que se está ejecutando, se puede controlar mediante la interfaz AppletContext (package java.applet). AppletContext es una interfaz implementada por el browser, cuyos métodos pueden ser utilizados por el applet para obtener información y realizar ciertas operaciones, como por ejemplo, sacar mensajes breves en la barra de estado del browser. Hay que tener en cuenta que la barra de estado es compartida por el browser y los applets, lo que tiene el peligro de que el mensaje sea rápidamente sobreescrito por el browser u otros applets y que el usuario no llegue a enterarse del mensaje. Los mensajes breves a la barra de estado se producen con el método showstatus(), como por ejemplo, getappletcontext().showstatus("cargado desde el archivo "+ filename); Los mensajes más importantes se deben dirigir a la salida estándar o a la salida de errores, que en Netscape Navigator es la Java Console. Estos mensajes se pueden enviar con las sentencias: System.out.print(); System.out.println(); System.err.print(); System.err.println(); Para mostrar documentos HTML en una ventana del browser se pueden utilizar los métodos siguientes: showdocument(url miurl, String target) Muestra un documento HTML en el frame del browser indicado por target (name, top, parent, blank, self). showdocument(url miurl) Muestra un documento HTML en la ventana actual del browser. Un applet puede conseguir información de otros applets que están corriendo en la misma página del browser, enviarles mensajes y ejecutar sus métodos. El mensaje se envía invocando los métodos del otro applet con los argumentos apropiados. Algunos browsers exigen, para que los applets se puedan comunicar, que provengan del mismo browser o incluso del mismo directorio (que tengan el mismo codebase). Por ejemplo, para obtener información de otros applets se pueden utilizar los métodos: getapplet(string name) Devuelve el applet llamado name (o null si no la encuentra). El nombre del applet se pone con el atributo opcional NAME o con un parámetro llamado NAME. getapplets() Devuelve una enumeración con todos los applets de la página. Michael Moossen Departamento de Informática. U.T.F.S.M. 139

152 5.4. APPLETS Para poder utilizar todos los métodos de un applet que se está ejecutando en la misma página HTML (y no sólo los métodos comunes heredados de Applet), debe hacerse un casting del objeto de la clase Applet, que se obtiene como valor de retorno de getapplet(), a la clase concreta del applet. Para que pueda haber respuesta (es decir, comunicación en los dos sentidos), el primer applet que envía un mensaje debe enviar una referencia a sí misma por medio del argumento this Sonidos en Applets La clase Applet y la interfaz AudioClip permiten utilizar sonidos en applets. A continucación se muestran algunos métodos interesantes al respecto. Métodos de Applet: AudioClip getaudioclip(url url) Devuelve el objeto especificado por url, que implementa la interfaz AudioClip. AudioClip getaudioclip(url url, String name) Devuelve el objeto especificado por url (dirección base) y name (dirección relativa). void play(url url), void play(url url, String name) Ejecuta el AudioClip correspondiente a la dirección especificada. Métodos de la interface AudioClip (en java.applet): void loop() Ejecuta el sonido repetidamente. void play() Ejecuta el sonido una sola vez. void stop() Detiene la ejecución del sonido. Respecto a la carga de sonidos, por lo general es mejor cargar los sonidos en un thread distinto (creado en el método init()) que en el propio método init(), que tardaría en devolver el control y permitir al usuario empezar a interaccionar con el applet. Si el sonido no ha terminado de cargarse (en el thread especial para ello) y el usuario interacciona con el applet para ejecutarlo, el applet puede darle un aviso de que no se ha terminado de cargar Imágenes en Applets Los applets admiten los formatos JPEG y GIF para representar imágenes a partir de archivos localizados en el servidor. Estas imágenes se pueden cargar con el método getimage() de la clase Applet, que puede tener las formas siguientes: Image getimage(url url) Image getimage(url url, String name) Estos métodos devuelven el control inmediatamente. Las imágenes se cargan cuando se da la orden de dibujarlas en pantalla. El dibujo se realiza entonces de forma incremental, a medida que el contenido va llegando. Para dibujar imágenes se utiliza el método drawimage() de la clase Graphics, que tiene las formas siguientes: boolean drawimage(image img, int x, int y, Color bgcolor, ImageObserver observer) boolean drawimage(image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) 140 Departamento de Informática. U.T.F.S.M. Michael Moossen

153 CAPÍTULO 5. JAVA E INTERNET El primero de ellos dibuja la imagen con su tamaño natural, mientras que el segundo realiza un cambio en la escala de la imagen. Los métodos drawimage() van dibujando la parte de la imagen que ha llegado, con su tamaño, a partir de las coordenadas (x, y) indicadas, utilizando bgcolor para los pixels transparentes. Estos métodos devuelven el control inmediatamente, aunque la imagen no esté del todo cargada. En este caso devuelve false. En cuanto se carga una parte adicional de la imagen, el proceso que realiza el dibujo avisa al ImageObserver especificado. java.awt.image.imageobserver es una interfaz implementada por Applet que permite seguir el proceso de carga de una imagen Threads en Applets Un applet puede ejecutarse con varios threads, y en muchas ocasiones será necesario o conveniente hacerlo así. Hay que tener en cuenta que un applet se ejecuta siempre en un browser (o en la aplicación appletviewer). Así, los threads en los que se ejecutan los métodos principales init(), start(), stop() y destroy() dependen del browser o del entorno de ejecución. Los métodos gráficos paint(), update() y repaint() se ejecutan siempre desde un thread especial del AWT. Algunos browsers dedican un thread para cada applet en una misma página; otros crean un grupo de threads para cada applet (para poderlos matar al mismo tiempo, por ejemplo). En cualquier caso se garantiza que todos los threads creados por los métodos principales pertenecen al mismo grupo. Se deben introducir threads en applets siempre que haya tareas que consuman mucho tiempo (cargar una imagen o un sonido, hacer una conexión a Internet, etc.). Si estas tareas pesadas se ponen en el método init(), bloquean cualquier actividad del applet o incluso de la página HTML hasta que se completen. Las tareas pesadas pueden ser de dos tipos: Las que sólo se hacen una vez. Las que se repiten muchas veces. Un ejemplo de tarea que se repite muchas veces puede ser una animación. En este caso, la tarea repetitiva se ejecuta, por ejemplo, dentro de un bucle while, dentro del thread. El thread se debería crear dentro del método start() del applet y destruirse en stop(). De este modo, cuando el applet no está visible, se dejan de consumir recursos. Al crear el thread en el método start(), se pasa una referencia al applet con la palabra this, que se refiere al applet. El applet deberá implementar la interfaz Runnable, y por tanto debe definir el método run(), que es el centro del thread. Un ejemplo de tarea que se realiza una sola vez es la carga de una imágen, que se realiza automáticamente en un thread especial. Sin embargo, los sonidos no se cargan en threads especiales de forma automática; los debe crear el programador para cargarlos en background. Este es un caso típico de un programa producer-consumer: el thread es el producer y el applet el consumer. Los threads deben estar sincronizados, para lo que se utilizan los métodos wait() y notifyall(). A continuación se presenta un ejemplo de thread con una tarea repetitiva: public void start() { if (repetitivethread == null ) { repetitivethread = new Thread(this ); // se crea un nuevo thread repetitivethread.start(); // se arranca el thread creado: start() llama a run() public void stop() { repetitivethread = null ; // para parar la ejecución del thread Michael Moossen Departamento de Informática. U.T.F.S.M. 141

154 5.4. APPLETS public void run() {... while (Thread.currentThread() == repetitivethread) {... // realizar la tarea repetitiva. El método run() se detendrá en cuanto se ejecute el método stop(), porque la referencia al thread está a null Applets como Aplicaciones Es muy interesante desarrollar aplicaciones que pueden funcionar también como applets y viceversa. En concreto, para hacer que un applet pueda ejecutarse como una aplicación, pueden seguirse las siguientes instrucciones: 1. Se añade un método main() a la clase MiApplet (que deriva de Applet). 2. El método main() debe crear un objeto de la clase MiApplet e introducirlo en un Frame. 3. El método main() debe también ocuparse de hacer lo que haría el browser, es decir, llamar a los métodos init() y start() de la clase MiApplet. 4. Se puede añadir también una static inner class que derive de WindowAdapter y que gestione el evento de cerrar la ventana de la aplicación definiendo el método windowclosing(). Este método ejecuta System.exit(0). Según como sea el applet, el método windowclosing(), previamente deberá también llamar a los métodos MiApplet.stop() y MiApplet.destroy(), cosa que para los applets se encarga de hacer el browser. En este caso, conviene que el objeto de MiApplet creado por main() sea static, en lugar de una variable local. A continuación se presenta un ejemplo: import java.applet.*; import java.awt.event.*; import java.awt.*; public class MiApplet extends Applet { static MiApplet unapplet = null ; public void init() { System.out.println("init"); public void start() { System.out.println("start"); public void stop() { System.out.println("stop"); public void destroy() { System.out.println("destroy"); // clase para seguir el comportamiento de la ventana static class WL extends WindowAdapter { public void windowclosing(windowevent e) { unapplet.stop(); unapplet.destroy(); System.exit(0); public void windowdeactivated(windowevent e) { unapplet.stop(); 142 Departamento de Informática. U.T.F.S.M. Michael Moossen

155 CAPÍTULO 5. JAVA E INTERNET public void windowactivated(windowevent e) { unapplet.start(); // fin de la clase WL // programa principal public static void main(string[] args) { unapplet = new MiApplet(); Frame unframe = new Frame("MiApplet"); unframe.addwindowlistener(new WL()); unframe.add(unapplet, BorderLayout.CENTER); unframe.setsize(400,400); unapplet.init(); unframe.setvisible(true ); // fin de la clase MiApplet Restricciones de Seguridad Uno de los principales objetivos del entorno Java es hacer que los usuarios de navegadores se sientan seguros cuando ejecutan cualquier applet. Para alcanzar este objetivo, se han restringido varias capacidades, de forma conservadora. En esta sección se verán las restricciones actuales de los applets, desde el punto de vista de cómo afectan al diseño de los mismos. Todo visualizador de applets tiene un objeto SecurityManager, que chequea las violaciones de seguridad de los applets. Cuando un SecurityManager detecta una violación, crea y lanza un objeto SecurityException. Generalmetne, el constructor de la SecurityException imprime un mensaje de aviso en la salida estándar. Un applet puede capturar las SecurityExceptions y reaccionar de forma apropiada, como avisar al usuario y saltar a una forma segura (pero menos ideal) de realizar la tarea. Algunos visualizadores de applets ocultan algunas SecurityExceptions, para que el applet nunca obtenga la SecurityException. Por ejemplo, la implementación del appletviewer del JDK de los métodos getapplet() y getapplets() de AppletContext, simplemente capturan e ignoran cualquier SecurityExceptions. El usuario puede ver los mensajes de error en la salida estándar, pero el applet obtiene un resultado válido desde los métodos. Esto tiene sentido, ya que desde getapplets() debería ser posible volver cualquier applet válido que encuentre, incluso si encuentra unos inválidos (El appletviewer considera un applet válido si fue cargado desde el mismo host que el applet que llamó a getapplets()). Los applets tienen las siguientes restricciones: Los applets no pueden cargar librerías o definir métodos nativos. Los applets sólo pueden usar su propio código Java y el API Java que proporciona el visualizador. Como mínimo, a todo applet se le debe proporcionar acceso al API definido en los paquetes java.*. Normalmente un applet no puede leer o escribir archivos en el host en el que se está ejecutando. El appletviewer del JDK permite algunas excepciones a esta regla especificadas por el usuario, pero por ejemplo Netscape Navigator, no lo hace. Los applets en cualquier visualizador pueden leer archivos especificados con URLs completas, en lugar de con nombres de archivos. Un atajo para no tener que escribir archivos, es tener un applet que reenvíe datos a una aplicación en el servidor del que vino. Esta aplicación puede escribir los datos en archivos de su propio host. Un applet no puede hacer conexiones de red excepto con el host del que vino. El atajo para esta restricción es hacer que el applet funcione con una aplicación en el host del que vino. La aplicación puede hacer sus propias conexiones a cualquier lugar de la red. Michael Moossen Departamento de Informática. U.T.F.S.M. 143

156 5.5. SEGURIDAD Un applet no puede arrancar ningún programa en el host en el que se está ejecutando. Un applet no puede leer ciertas propiedades del sistema. Las ventanas que muestran los applets tienen un aspecto distinto a las que muestran las aplicaciones. Las ventanas de los applets tienen algún texto de aviso y una barra coloreada o una imagen. Esto ayuda al usuario a distinguir las ventanas de los applets, de aquellas de las aplicaciones verdaderas Seguridad Controlador de Seguridad La seguridad se vuelve importante cuando se escriben programas que interactúan con Internet. Es imposible que los computadores en Internet estén completamente seguros del ataque de unos pocos villanos externos. Sin embargo, se pueden seguir algunos pasos para proporcionar un nivel de protección significante. Una de las formas que proporciona Java frente a la seguridad, es a través del uso de los controladores de seguridad. Un controlador de seguridad implementa e impone una política de seguridad para una aplicación. Toda aplicación Java puede tener su propio objeto controlador de seguridad, que actúa como un guardia de seguridad a tiempo completo. La clase SecurityManager del paquete java.lang, es una clase abstracta que proporciona la interfaz de programación y una implementación parcial para todos los controladores de seguridad de Java. Por defecto, una aplicación no tiene controlador de seguridad. Esto es, el sistema de ejecución de Java no crea automáticamente un controlador de seguridad para cada aplicación. Entonces, por defecto, una aplicación permite todas las operaciones que están sujetas a las restricciones de seguridad. Para cambiar este comportamiento indulgente, una aplicación puede crear e instalar su propio controlador de seguridad. Sin embargo, hay que tener en cuenta que, los navegadores existentes y los visualizadores de applets crean su propio controlador de seguridad cuando arrancan. Así, un applet está sujeto a las restricciones de acceso que sean impuestas por el controlador de seguridad de la aplicación particular en la que el applet se está ejecutando. Se puede obtener el controlador de seguridad actual de una aplicación utilizando el método getsecuritymanager() de la clase System. SecurityManager appsm = System.getSecurityManager(); Obsérvese que getsecuritymanager() devuelve null si no hay ningún controlador de seguridad actual en la aplicación, por lo que debería asegurarse de que tiene un objeto válido, antes de llamar a cualquiera de sus métodos. Una vez que se tiene el controlador de seguridad, se puede pedir permiso para permitir o prohibir ciertas operaciones. De hecho, muchas de las clases en los paquetes de Java hacen esto. Por ejemplo, el método System.exit(), que finaliza el interprete Java, utiliza el método checkexit() del controlador de seguridad, para aprobar la operación de salida. SecurityManager security = System.getSecurityManager(); if (security!= null ) { security.checkexit(status); Si el controlador de seguridad aprueba la operación de salida, la llamada checkexit() retorna normalmente. Si el controlador de seguridad prohibe la operación, esta llamada lanza una SecurityException. De esta forma, el controlador de seguridad permite o prohibe una operación potencialmente dañina, antes de que pueda ser completada. La clase SecurityManager define muchos otros métodos utilizados para verificar otras clases de operaciones. Por ejemplo, el método checkaccess(), verifica los accesos a los threads, y checkpropertyaccess() verifica el acceso a la propiedad de sistema especificada. Cada operación o grupo de operaciones tiene su propio método checkxxx(). 144 Departamento de Informática. U.T.F.S.M. Michael Moossen

157 CAPÍTULO 5. JAVA E INTERNET Además, el conjunto de métodos checkxxx(), representa el conjunto de operaciones de las clases de los paquetes Java y el sistema de ejecución de Java que ya están sujetos a la protección del controlador de seguridad. Por eso, normalmente, el código propio no tendrá que llamar a ningún método checkxxx() las clases de los paquetes de Java hacen esto a un nivel lo suficientemente bajo, de tal forma cualquier operación representada por un método checkxxx() ya está protegida. Sin embargo, cuando se escriba un controlador de seguridad propio, se tendrá que sobreescribir algún método checkxxx() de SecurityManager, para modificar la política de seguridad de las operaciones específicas, o se podría tener que añadir algunos propios para poner otras clases de operaciones para el escrutinio del controlador de seguridad Un Controlador de Seguridad Propio Para escribir un controlador de seguridad propio, se debe crear una subclase de la clase SecurityManager. Esta subclase debe sobreescribir varios métodos de SecurityManager, para personalizar las verificaciones y aprobaciones necesarias para una aplicación Java. Esta sección muestra un controlador de seguridad de ejemplo, que restringe la lectura y escritura en el sistema de archivos. Para obtener la aprobación del controlador de seguridad, un método que abra un archivo para leer, invoca uno de los métodos checkread() de SecurityManager, un método que abre un archivo para escribir, invoca a uno de los métodos checkwrite() de SecurityManager. Si el controlador de seguridad aprueba la operación, el método checkxxx() retorna nomalmente, de otra forma checkxxx() lanza una SecurityException. Para imponer una política restrictiva en los accesos al sistema de archivos, el ejemplo debe sobreescribir los métodos checkread() y checkwrite() de SecurityManager. SecurityManager proporciona tres versiones de checkread() y dos versiones de checkwrite(). Cada una de ellas debería verificar si la aplicación puede abrir un archivo para I/O. Esta es una política implementada frecuentemente en los navegadores, para que los applets cargados a través de la red no puedan leer o escribir en el sistema local de archivos, a menos que el usuario lo apruebe. La política implementada por el ejemplo le pide al usuario una password cuando la aplicación intenta abrir un archivo para leer o escribir. Si la password es correcta se permite el acceso. Todos los controladores de seguridad deben ser una subclase de SecurityManager. Así, la PasswordSecurityManager desciende de SecurityManager. class PasswordSecurityManager extends SecurityManager {... Luego, PasswordSecurityManager declara un ejemplar de la variable privada password para contener la palabra clave que el usuario debe introducir, para permitir el acceso al sistema de archivos restringido. La palabra clave, se selecciona durante la construcción. PasswordSecurityManager(String pwd) { super(); this.password = pwd; El siguiente método en la clase PasswordSecurityManager es un método de ayuda privado llamado accessok(). Este método le pide al usuario una password y la verifica. Si el usuairo introduce una password válida, el método devuelve true, de otra forma devuelve false. private boolean accessok() { int c; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String response; System.out.println("What s the secret password?"); try { Michael Moossen Departamento de Informática. U.T.F.S.M. 145

158 5.5. SEGURIDAD response = br.readline(); if (response.equals(password)) return true ; else return false ; catch (IOException e) { return false ; Finalmente, la clase PasswordSecurityManager sobreescribe los tres métodos checkread() y los dos métodos checkwrite(). public void checkread(filedescriptor filedescriptor) { if (!accessok()) throw new SecurityException("Not a Chance!"); public void checkread(string filename) { if (!accessok()) throw new SecurityException("No Way!"); public void checkread(string filename, Object executioncontext) { if (!accessok()) throw new SecurityException("Forget It!"); public void checkwrite(filedescriptor filedescriptor) { if (!accessok()) throw new SecurityException("Not!"); public void checkwrite(string filename) { if (!accessok()) throw new SecurityException("Not Even!"); Todos los métodos checkxxx() llaman a accessok() para pedirle al usuario la password. Si el acceso falla, entonces checkxxx() lanza una SecurityException. De otra froma, checkxxx() retorna normalmente. Obsérvese que SecurityException, es una excepción en tiempo de ejecución, y no necesita ser declarada en la cláusula throws de estos métodos. checkread() y checkwrite() son sólo unos pocos de los muchos métodos checkxxx() de SecurityManager que verifican varias clases de operaciones. Se pueden sobreescribir o añadir cualquier número de métodos checkxxx() para implementar una política de seguridad propia. No se necesita sobreescribir todos los métodos checkxxx() de SecurityManager, sólo aquellos que se quieran personalizar. Sin embargo, la implementación por defecto proporcionada por la clase SecurityManager para todos los métodos checkxxx(), lanza una SecurityException. En otras palabras, por defecto, la clase SecurityManager prohibe todas las operaciones que están sujetas a las restricciones de seguridad. Por lo que, podría ser que se tienen que sobreescribir muchos métodos checkxxx() para obtener el comportamiento deseado. Todos los métodos checkxxx() de la clase SecurityManager operan de la misma forma. Si el acceso está permitido, el método retorna. Si el acceso no está permitido, el método lanza una SecurityException. Así, implementar un controlador de seguridad es sencillo, sólo se necesita: 146 Departamento de Informática. U.T.F.S.M. Michael Moossen

159 CAPÍTULO 5. JAVA E INTERNET Crear una subclase de SecurityManager. Sobreescribir unos cuantos métodos. El truco está en determinar los métodos que se deben sobreescribir para implementar la política de seguridad propia Instalar un Controlador de Seguridad Una vez que está lista la subclase de SecurityManager, se debe instalar como el controlador de seguridad por defecto de la aplicación. Esto se puede hacer utilizando el método setsecuritymanager() de la clase System. Aquí se muestra una pequeña aplicación de prueba, SecurityManagerTest, que instala la clase PasswordSecurityManager de la sección anterior como el controlador de seguridad por defecto. Luego, para verificar que el controlador de seguridad está en su lugar y es operacional, esta aplicación abre dos archivos uno para leer y otro para escribir y copia el contenido del primero en el segundo. El método main() comienza con la instalación del nuevo controlador de seguridad. try { System.setSecurityManager(new PasswordSecurityManager("tarzan")); catch (SecurityException se) { System.out.println("SecurityManager already set!"); La segunda línea del código anterior crea un nuevo ejemplar de la clase PasswordSecurityManager con la clave "tarzan". Este ejemplar es pasado al método setsecuritymanager() de la clase System, que instala el objeto como el controlador de seguridad por defecto para la aplicación que se está ejecutando. Este controlador de seguridad permanecerá efectivo durante toda la ejecución de esta aplicación. Sólo se puede seleccionar una vez el controlador de seguridad de una aplicación. En otras palabras, una aplicación Java sólo pude invocar una vez a System.setSecurityManager() durante su ciclo de vida. Cualquier intento posterior de instalar un controlador de seguridad dentro de una aplicación Java, resultará en una SecurityException. El resto del programa copia el contenido de este archivo inputtext.txt en un archivo de salida llamado outputtext.txt. Es sólo un texto que verifica que PasswordSecurityManager se ha instalado de forma apropiada. try { BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream("inputtext.txt"))); DataOutputStream fos = new DataOutputStream(new FileOutputStream( "outputtext.txt")); String inputstring; while ((inputstring = br.readline())!= null ) { fos.writebytes(inputstring); fos.writebyte( \n ); br.close(); fos.close(); catch (IOException ioe) { System.err.println("I/O failed for SecurityManagerTest."); ioe.printstacktrace(); Las primeras dos líneas del código anterior son los accesos al sistema de archivos restringido. Estas llamadas a métodos resultan en una llamada al método checkaccess() del PasswordSecurityManager. Michael Moossen Departamento de Informática. U.T.F.S.M. 147

160 5.5. SEGURIDAD Cuando se ejecute la aplicación SecurityManagerTest, se pedirá dos veces la password: una cuando la aplicación abre el archivo de entrada y otra cuando abre el archivo de salida. Si se ingresa la password correcta, se permite el acceso al objeto archivo y la aplicación prosigue con la siguiente sentencia. Si se ingresa una password incorrecta, checkxxx() lanza una SecurityException, que la aplicación no intenta capturar por lo que aplicación termina. Este es un ejemplo de la salida de aplicación cuando se ingresa la password correcta la primera vez, pero no la segunda. What s the secret password? tarzan What s the secret password? xxx java.lang.securityexception: Not Even! at PasswordSecurityManager.checkWrite(PasswordSecurityManager.java:46) at java.io.fileoutputstream.<init>(fileoutputstream.java:169) at java.io.fileoutputstream.<init>(fileoutputstream.java:70) at SecurityManagerTest.main(SecurityManagerTest.java:13) Obsérvese que el mensaje de error que muestra la aplicación es el mensaje contenido en el método checkwrite(string) Decidir qué Métodos Sobreescribir del SecurityManager Se podría tener que sobreescribir varios métodos checkxxx() del SecurityManager dependiendo de las operaciones a las que se quiera que el controlador de seguridad les imponga restricciones. La primera columna de la siguiente tabla, son objetos sobre los que se pueden realizar varias operaciones. La segunda columna, lista los métodos de SecurityManager que aprueban las operaciones de los objetos de la primera columna. Operaciones sobre Sockets Threads Cargador de Clases Sistema de Archivos Comandos del Sistema Interprete Paquetes Red Ventanas Aprobadas por checkaccept(string host, int port) checkconnect(string host, int port) checkconnect(string host, int port, Object executioncontext) checklisten(int port) checkaccess(thread thread) checkaccess(threadgroup threadgroup) checkcreateclassloader() checkdelete(string filename) checklink(string library) checkread(filedescriptor filedescriptor) checkread(string filename) checkread(string filename, Object executioncontext) checkwrite(filedescriptor filedescriptor) checkwrite(string filename) checkexec(string command) checkexit(int status) checkpackageaccess(string packagename) checkpackagedefinition(string packagename) checksetfactory() checktoplevelwindow(object window) 148 Departamento de Informática. U.T.F.S.M. Michael Moossen

161 CAPÍTULO 5. JAVA E INTERNET Operaciones sobre Propiedades del Sistema Aprobadas por checkpropertiesaccess() checkpropertyaccess(string key) checkpropertyaccess(string key, String def) Dependiendo de la política de seguridad, se puede sobreescribir algunos o todos estos métodos. Por ejemplo, supóngase que se está escribiendo un Navegador Web o un visualizador de applets, y se quiere evitar que los applets utilicen sockets. Se puede hacer esto sobreescribiendo los cuatro métodos que afectan al acceso a los sockets. Muchos de los métodos checkxxx() son llamados en múltiples situaciones. Por ejemplo, el método checkaccess(threadgroup), es llamado cuando se crea un ThreadGroup, se selecciona su estado de servicio, se detiene su ejecución, etc. Cuando se sobreescriba un método checkxxx(), es necesario asegurase de que se comprenden todas las situaciones en las que puede ser llamado. La implementación por defecto suministrada por la clase SecurityManager para todos los métodos checkxxx() es: public void checkxxx(...) { throw new SecurityException(); La mayoría de las veces se querrá que haga algo más selectivo que prohibirlo todo! Por eso, podría ser útil sobresscribir todos los métodos checkxxx() de SecurityManager. Michael Moossen Departamento de Informática. U.T.F.S.M. 149

162 5.5. SEGURIDAD 150 Departamento de Informática. U.T.F.S.M. Michael Moossen

163 Capítulo 6 Interfaces Gráficas Antes de explicar que son las interfaces gráficas y como se programan con java, es necesario definirlas. En términos simples la interfaz de una aplicación, es aquel mecanismo que permite al usuario interactuar con la la aplicación. Existen varios niveles de interfaces, de las más simples a las más sofisticadas: Interfaces de Consola En general, en estos tipos de interfaces, el único dispositivo de entrada usado es el teclado. Se tienen tres subtipos: Línea de Comandos Este tipo de interfaz consiste en que el usuario puede especificar algunas opciones y parámetros al momento de ejecutar la aplicación, mediante la línea de comandos. Ejemplo: javac -cp c:\codebase;. -jar tools.jar com.soft.ejemplo Interacción Pregunta-Respuesta Este tipo de interfaz consiste en que la apliación le hace una serie de preguntas al usuario, las cuales deben ser respondidas por el usuario antes de poder seguir la ejecución de la aplicación. Ejemplo: Desea sobrescribir el archivo "miclase.java" (s/n)? Ventanas basadas en Texto Este tipo de interfaz consiste en emular un sistema de ventanas gráficas, usando caracteres ASCII extendidos, ofreciendo así al usuario la mayoría de las ventajas de una interfaz gráfica, incluyendo por ejemplo el Mouse como dispositivo de entrada. Como ejemplo de aplicaciones que usan este tipo de interfaces están el Norton Disk Doctor for DOS y el Partition Magic for DOS Interfaces Gráficas Este tipo de interfaz consiste en dibujar un sistemas de ventanas gráficas, que contienen una serie de componentes gráficas como botones, tablas, menús, árboles jerárquicos, etc. para facilitar la interacción con el usuario. En general, una aplicación de interfaz gráfica necesita una plataforma gráfica, que le provea de todos los elementos básicos. Además, la manera estándar como que se ven las aplicaciones, está fuertemente relacionada con la plataforma sobre la cual se ejecuta. Este tipo de interfaces generalemnete usan otros dispositivos de entrada, además del teclado, como por ejemplo el Mouse, Voz, Joystick, Pantallas sensibles (TouchScreen), etc. Ejemplo: 151

164 6.1. CONTEXTO HISTÓRICO 6.1. Contexto Histórico Como las interfaces gráficas dependen en gran medida de la plataforma en la cual se están ejecutan, su implementación varia mucho de una plataforma a otra. Por lo cual, el objetivo de tener interfaces gráficas para java portables e independientes de plataforma, no es de ninguna manera fácil de lograr. Por este mismo es que la corta historia de java, está llena de intentos buscando este objetivo, unos más existosos que otros, y otros simplementes fracasados. Las interfaces gráficas en java comenzaron con la primera versión de este. Está versión incluía un paquete llamado AWT (Abstract Windows Toolkit), el cual era el punto más débil de aquella primera versión. Se trataba de una capa de comunicación entre java y las interfaces nativas, así sólo ofrecia soporte para aquellos controles disponibles en todos las plataformas por igual. Lo que lo hacía muy limitado y poco flexible, además de tener un pésimo desempeño. Los eventos eran controlados por cada componente, tipo Visual Basic, y presentaba una serie de problemas serios con los eventos del Mouse. Debido a todo esto, es que AWT en la actualidad es sólo un mal recuerdo. Luego, en la versión 1.1 de java vino Swing junto con las Java Foundation Classes (JFC), lo cual puso un poco de orden en la escena. Estaba programado 100 % en java, por lo tanto es 100 % portable, ofrece una mayor funcionalidad de los componentes, cambio la arquitectura de manejo de eventos haciendolo más flexible, también introduce el manejo del Look & Feel por software. Se dice que Swing reemplaza, mejora y extiende a AWT, esto debido a que reemplaza un sin número de componentes de AWT, usa también una serie de funcionalidades de AWT y también extiende la funcionalidad de algunos componentes de AWT. A la vez comenzaron a introducirse una serie de APIs que extienden las funcionalidades de las interfaces gráficas, como lo son: Accesibility Introduce una serie de funcionalidades para mejorar la accesibilidad de los componentes. Java2D y Java3D Introduce una serie de funcionalidades para crear y manipular gráficas 2D y 3D. Drag & Drop Introduce una serie de funcionalidades para arrastrar y soltar objetos entre distintos componentes. Printing Introduce una serie de funcionalidades para mejorar el manejo de la impresora. Además, Sun en cada nueva versión de java, sigue mejorando y ampliando las características de Swing. Sin embargo, aunque las mejoras han sido notables, Swing aún deja bastante que desear si se compara con interfaces nativas, principalmente debido a temas de desempeño. Así surge SWT (Standart Widget Toolkit) de IBM, enmarcado en su proyecto opensource Eclipse(http: //www.eclipse.org/). Esto es una librería para crear interfaces gráficas con Java. Es una delgada capa java que comunica con una librería de código nativo, ofreciendo un mayor desempeño, además de un renovado Look & Feel. Debido a que necesita una librería de código nativo es menos flexible que Swing, sin embargo, ya existen distribuciones de SWT para las siguientes plataformas: Win32, Linux, Solaris, HPUnix, AIX, MacOS, Win32ce, QNX. En resumen, SWT está entrando con fuerza en el mundo Java, sin embargo, aún es bastante nuevo. Por otro lado, Swing es flexible y estable, teniendo ya más de 5 años de historia, por lo que lejos es lo más usado en el mundo. Pero en definitiva, pareciera que SWT va a desplazar muy rápido Swing, aunque en Sun sólo hablen mal de él. Este cambio no debiese ser problema para ningún desarrollador java, ya que su manejo es bastante similar a Swing. 152 Departamento de Informática. U.T.F.S.M. Michael Moossen

165 CAPÍTULO 6. INTERFACES GRÁFICAS 6.2. Introducción a Swing La arquitectura básica de Swing está basada en Contenedores que contienen componentes (o subcontenedores) formándose una jerárquia de componentes, a apartir de un contenedor maestro y terminando en un componente básico. Esto se logra a través de una programación de alto nivel, limpia de detalles dependientes de plataforma como los hwnd de windows o las llamadas a X en Unix. Por otro lado, en general, las ventanas se programan y no se pintan como en lenguajes típicos como Visual Basic o Visual C++. Por lo mismo, es que también se prefiere un mecanismo de disposición de componentes administrados lógicamente, mediante Layout Managers, por sobre el mecanismo de disposición de componentes con posiciones y tamaños absolutos, aunque también se pueden usan con Java. Sin embargo, la mayoría de las IDEs para java ofrecen herramientas para pintar ventanas. Además el manejo de eventos se produce mediante interfaces de escuchadores de eventos(listeners) o adaptadores(adapters), con lo cual un mismo evento puede ser procesado de varias maneras simultaneamente. Los componentes son elementos gráficos básicos como etiquettas, cajas de texto y botones. Estos hacen la interacción con el usuario posible, y todos estan encapsulados por una subclase de la clase Component. Los contenedores en cambio agrupan, contienen y organizan componentes, y son a la vez también componentes, lo que posibilita que exista una jerárquia de componentes. Todos los contenedores estan encapsulados por una subclase de la clase Container. Ejemplo: En este ejemplo, tenemos la siguiente estructura jerárquica: La ventana principal es de tipo JFrame ella contiene un panel de tipo JPanel que a la vez contiene un panel de desplazamiento de tipo JScrollPane que contiene una área de texto JTextArea, así mismo el primer panel, contiene otro panel del mismo tipo (JPanel), que contiene dos botones de tipo JButton Contenedores Existen varios tipos de contenedores: Contenedores Maestros Estos contenedores son aquellos que pueden ser la raíz de una jerárquia de componentes, dependiendo del tipo, es el tipo de ventana que se obtiene: JApplet Permiten crear un applet con componentes Swing. JFrame Permite crear una ventana típica. JDialog Permite crear una ventana de diálogo, ya sea modal o no. Todos ellos tienen un panel de contenidos de tipo JPanel donde se agregan sus componentes. Contenedores de Propósito General JPanel Un panel simple que agrupa componentes. Michael Moossen Departamento de Informática. U.T.F.S.M. 153

166 6.2. INTRODUCCIÓN A SWING JScrollPane Un panel de desplazamiento que permite al usuario desplazarse por su contenido. JSplitPane Un panel de división que permite que el usuario ajuste el tamaño de sus componentes. JTabbedPane Un panel de pestañas que permite al usuario seleccionar un conjunto de controles a la vez. JToolBar Un panel de herramientas que dan al usuario acceso directo a cierto controles. JMenuBar Un panel de menú que dan de funciones al usuario mediante un sistema de menús desplegables. Contenedores Específicos JDesktopPane y JInternalFrame Permiten la implementación de aplicaciones MDI (Multiple Document Interface). JLayeredPane Permiten mantener el control de la profundidad de los componentes JRootPane Permiten manejar algunas funcionalidad entre componentes (de tal vez, distintos paneles), como por ejemplo indicar el botón por defecto de un formulario. La clase JPanel Para clarificar un poco el uso de los contenedores, se verá como ejemplo la clase JPanel. La jerárquia de esta clase es como sigue: java.lang.object java.awt.component java.awt.container javax.swing.jcomponent javax.swing.jpanel Es decir, como casi todos los componentes gráficos, es tanto un componente como un contenedor, y sus métodos más importantes para agregar componentes al panel: Component add(component c) Component add(component c, int pos) Component add(component c, String name) Todos estos métodos agregan componentes al contenedor, y el segundo parámetro de algunos de estos métodos tiene relación con el administrador de disposición usado para indicar la posición del componente. Métodos para manipular el administrador de disposición: void setlayout(layoutmanager lm) LayoutManager getlayout() Métodos para manipular el borde: void setborder(border b) Border getborder() 154 Departamento de Informática. U.T.F.S.M. Michael Moossen

167 CAPÍTULO 6. INTERFACES GRÁFICAS La clase JFrame La raíz típica de un árbol de componentes jerárquico es tipo JFrame que encapsula la funcionalidad de una ventana típica, por lo cual es el contenedor maestro más usado. Esta clase contiene una serie de métodos esenciales para el manejo de ventanas: void settitle(string s) Permite indicar el título de la ventana, el cual se despliega en la barra superior. void setsize(int sx, int sy) Permite indicar el tamaño inicial de la ventana en píxeles. void pack() Establece el tamaño ideal de la ventana, según el administrador de disposición. void setvisible(boolean v) Permite desplegar u ocultar la ventana. void setdefaultcloseoperation(int v) Permite indicar un manejo de eventos de cierre simple, por defecto. Para esto se usan las constantes definidas en la clase WindowConstants: EXIT ON CLOSE, HIDE ON CLOSE y DISPOSE ON CLOSE. Estos eventos pueden ser manejados más extensamente usando un WindowAdapter. void setcontentpane(container c) Permite indicar el panel de contenidos, es decir, el panel principal, a usar. Container getcontentpane() Permite acceder al panel de contenidos actual. void setjmenubar(jmenubar m) Permite indicar la barra de menú a usar. JMenuBar getjmenubar() Permite acceder a la barra de menú actual. También existen otros métodos para acceder a una serie de otros paneles más específicos con que cuenta un JFrame: Root Pane: Este panel, de clase JRootPane, permite realizar algunas operaciones conjuntas entre componentes de distintos subpaneles. Por ejemplo, indicar el botón por defecto de la ventana, lo cual se realiza mediante el comando: frame.getrootpane().setdefaultbutton(boton); Layered Pane: Este panel, de clase JLayeredPane, se encarga de distribuir adecuadamente la profundidad de los subpaneles en el eje Z. Glass Pane: Este panel, de clase Component y permite, por ejemplo, dibujar por encima de los componentes, incluso abarcando varios a la vez. A continuación una imagen que ilustra claramente la relación de todos estos paneles: Michael Moossen Departamento de Informática. U.T.F.S.M. 155

168 6.2. INTRODUCCIÓN A SWING Componentes Existen varios tipos de componentes: Botones JButton Es un botón tipo. JRadioButton Es un botón que permite seleccionar una opción entre varias de un grupo. JCheckBox Es un botón que permite mantener un estado binario: seleccionado o no. JToggleButton Muy parecido al anterior sólo que de distinta apariencia. Despliege de Información no Editable JLabel Permite desplegar texto o imágen. JProgressBar Permite desplegar el avance de una operación. JToolTip Permite desplegar algún texto de ayuda al mover el mouse sobre el componente asociado. Cajas de Texto sin Formato JTextField Permite ingresar texto en una sola línea. JPasswordField Idéntico al anterior, sólo que no se despliega el contenido. JTextArea Permite ingresar texto multilínea. Controles de Menú JMenu Permite crear un menú ha ser insertado en una barra de menú, o un submenú de un menú. JPopupMenu Permiten crear un menú que puede ser desplegado sobre un componente dependiendo de ciertos eventos. JMenuItem Representa un ítem simple de menú. JSeparator Permite introducir separaciones dentro de un menú. JRadioButtonMenuItem Es un ítem de menú que es tratado como un JRadioButton. JCheckBoxMenuItem Es un ítem de menú que es tratado como un JCheckBox. Controles de Selección 156 Departamento de Informática. U.T.F.S.M. Michael Moossen

169 CAPÍTULO 6. INTERFACES GRÁFICAS JComboBox Permite seleccionar una opción entre varias de una lista desplegable. JList Permite seleccionar una o más opciones entre varias de una lista. JSlider Permite seleccionar un valor dentro de un intervalo. Información Formateada JTable Permite crear tablas de datos. JTree Permite crear árboles jerárquicos de datos. JEditorPane Permite manipular texto formateado (HTML o RTF). La clase JComponent Para ejemplificar el manejo de componentes, se verá como ejemplo la clase JComponent de la cual heredan casi todos los componentes gráficos. En forma general, tiene algunos métodos para sugerirle al administrador de disposición el tamaño. Estos métodos son: void setminimumsize(dimension d) void setpreferredsize(dimension d) void setmaximumsize(dimension d) Aquí el parámetro d es un objeto de la clase Dimension que presenta una dimensión planar, es decir tiene alto y ancho. Para sugerir la alineación del componente, se pueden usar: void setalignmentx(float pos) void setalignmenty(float pos) Donde pos es un número entre 0.0 y 1.0 que indica, en forma porcentual, la posición relativa de este componente con el próximo. Por ejemplo, supóngase que se tiene un panel con dos botones, uno encima del otro. Si el primer botón tiene una alineación en el eje X de 0.5, entonces el segundo botón se desplegará comenzando del punto medio del primero. También hay unos métodos muy usados para manejar ToolTips: void settooltiptext(string tt) String gettooltiptext() Ejemplo: Michael Moossen Departamento de Informática. U.T.F.S.M. 157

170 6.3. MANEJO DE EVENTOS 6.3. Manejo de Eventos En el contexto de las interfaces gráficas, existen dos tipos de eventos: Eventos de Bajo Nivel Estos son los eventos producidos directamente por el usuario mediante el hardware. Por ejemplo, eventos de mouse, de teclado, de voz, de joystick, etc. Eventos Semánticos Estos son eventos de más alto nivel, gatillados por los componentes, generalmente debido a algún evento de bajo nivel. Por ejemplo, si el usuario cambia el tamaño de una columna de una tabla con el mouse, no es necesario manejar el evento del mouse, y con la posición del mouse determinar el la columna afectada y el nuevo ancho, sino que la misma tabla maneja el código de bajo nivel, y luego gatilla un evento semántico diciendo que una columna cambio su tamaño, por si se quiere hacer alguna operación adicional. Entre estos eventos se encuentran acciones, y eventos de componentes. Para manipular eventos java provee de una arquitectura publisher/subscriber, es decir, para poder reaccionar ante un evento es necesario registrarse(subscribirse) con el componente que puede lanzar el evento en particular, así si se produce el evento el componente originador(publisher) envía el evento a todos aquellos que se registraron. El registro se produce por medio de una interfaz llamada Listener o de sus clases derivadas o implementadas. También existen los adaptadores que, en general, agrupan un conjunto de listeners relacionados. La ejecución de cada evento debe ser lo más rápida posible, debido a que todos los eventos se ejecutan en forma secuencial, incluído el evento de repintado de las ventanas, en una hebra dedicada llamada event-dispatching thread Existen distintos tipos de listeners, dependiente del origen del o de los eventos que esperan, los más usados son: MouseListener Escucha eventos básicos del Mouse, sólo clics. MouseMotionListener Escucha eventos de movimiento del Mouse. WindowListener Escucha eventos de ventana, como cambio de tamaño, minimización, etc. FocusListener Escucha eventos de obtención y pérdida del foco de lso componentes. ActionListener Escucha eventos de acciones. Por ejemplo, presionar un botón o seleccionar un ítem de menú. KeyListener Escucha eventos de teclado. ListSelectionListener Escucha eventos sobre cambios en la selección de una lista. 158 Departamento de Informática. U.T.F.S.M. Michael Moossen

171 CAPÍTULO 6. INTERFACES GRÁFICAS En general, todos los métodos de un listener tienen un parámetro de tipo EventObject o alguno de sus derivados, del cual se puede obtener toda la información necesaria para manejar de forma correcta el evento. Por ejemplo, son un MouseEvent se pueden obtener las coordenadas y sobre que componente el usuario hizo clic con el mouse. Así para implementar un manejador de eventos es necesario seguir dos pasos: Primero, es necesario declarar una clase que implemente un Listener o que extienda un Adapter e implementar los métodos necesarios. Luego, es necesario registrar esta nueva clase con el componente correspondiente, para que sepa que cuando se gatillan ciertos eventos debe avisarle a esta nueva clase. El siguiente ejemplo, implementa un nuevo ActionListener que hace beep cuando es llamado: public class MiManejador implements ActionListener { public void actionperformed(actionevent e) { Toolkit.getDefaultToolkit().beep(); El cual se puede registrar en un botón con la siguiente instrucción: boton.addactionlistener(new MiManejador()); En la mayoría de las ocasiones un manejador se implementa especificamente para un componente, además su código es bastante reducido, lo que ofrece el ambiente propicio para usar clases anónimas Diseño de Interfaces Administración de Disposición Como se ha mencionado anteriormente, la forma correcta de ubicar componentes en un panel es mediante el uso de un administrador de disposición (LayoutManager), el cual se encarga, bajo ciertos parámetros, de darle el tamaño y la posición adecuada a cada uno de los componentes. Existen varios tipos de administradores de disposición, que manejan la disposición de los componentes de distintas maneras. Por defecto, los paneles de contenidos, tienen un BorderLayout y los demás paneles tienen un FlowLayout. La clase BorderLayout Un BorderLayout ofrece cinco sectores para posicionar componentes, como se puede apreciar en el ejemplo. Estos sectores se identifican por constantes definidas por esta clase: NORTH, SOUTH, WEST, EAST y CENTER. No es necesario ocupar los cinco sectores siempre. Y su comportamiento al cambiar el tamaño del contenedor es el siguiente: Los sectores NORTH y SOUTH ajustan su tamaño a lo largo, en el eje X. Los sectores WEST y EAST ajustan su tamaño a lo alto, en el eje Y. El sector CENTER ajusta su tamaño tanto a lo ancho como a lo alto. Para usarlo, basta con crear un nuevo objeto y registrarlo con el panel en cuestión, para esto los constructores más usados son: BorderLayout() Michael Moossen Departamento de Informática. U.T.F.S.M. 159

172 6.4. DISEÑO DE INTERFACES BorderLayout(int hgap, int vgap) El último constructor separa los sectores hgap píxeles horizontalmente y vgap píxeles verticalmente. Ejemplo: La clase BoxLayout Esta clase representa un stack de componentes el cual puede ser tanto vertical como horizontal. Su constructor más usado es: BoxLayout(Component c, int pos) En donde, c es el panel con el cual se va a registrar este BoxLayout y pos puede ser Y AXIS para indicar un stack vertical de arriba hacia abajo, o X AXIS para indicar un stack horizontal de izquierda a derecha. La clase CardLayout Esta clase permite, tener un conjunto de paneles en una misma posición, pero siempre uno sólo será visible a la vez, lo cual se puede ir variando dependiendo de las circunstancias, como por ejemplo el estado de otro control. Para usar un CardLayout se usa su constructor por defecto, y al agregar distintos paneles a un contenedor con este tipo de administrador se puede usar el método add(component, String), donde el String es un identificador único. Para desplegar un panel específico se pueden usar los siguientes métodos: void first(container c) void last(container c) void previous(container c) void next(container c) void show(container c, String s) En donde, c es el contenedor que usa el CardLayout, y s es el identificador de un panel específico. La clase FlowLayout Este administrador de disposición, es el más simple de todos, y es muy parecido a un BoxLayout horizontal, con la diferencia que inserta saltos de línea para acomodar mejor los componentes. También permite ajustar el alineamiento de cada fila. Sus contructores más usados son: FlowLayout() FlowLayout(int align) FlowLayout(int align, int hgap, int vgap) En donde, align puede ser LEFT, RIGHT o CENTER, y hgap y vgap tienen el mismo significado que con BorderLayout. 160 Departamento de Informática. U.T.F.S.M. Michael Moossen

173 CAPÍTULO 6. INTERFACES GRÁFICAS La clase GridLayout Este administrador de disposición representa un grilla de componentes, donde el espacio siempre se distribuye de forma uniforme. Sus constructores son: GridLayout(int rows, int cols) GridLayout(int rows, int cols, int hgap, int vgap) En donde, rows y cols indican el tamaño de la grilla, en número de componentes. Cualquiera de las dos dimensiones puede ser cero, lo que indica que la grilla crece en esa dirección de tal manera que pueda contener todos los componentes del panel. Ejemplo: contentpane.setlayout(new GridLayout(0,2)); contentpane.add(new JButton("Button 1")); contentpane.add(new JButton("2")); contentpane.add(new JButton("Button 3")); contentpane.add(new JButton("Long-Named Button 4")); contentpane.add(new JButton("Button 5")); La clase GridBagLayout Este es el administrador de disposición más complejo, ya que encapsula una funcionalidad similar a la de una tabla HTML. Siendo una grilla de componentes, como también lo es GridLayout pero mucho más flexible, permitiendo por ejemplo, unir celdas y/o columnas. Para esto cada celda tiene un conjunto de restricciones, representadas por un objeto de la clase GridBagConstraints. Para crear un GridBagLayout se usa su constructor estándar, y para indicar las restricciones de cada componente se usa el método: void setconstraints(component c, GridBagConstraints gbc) La clase GridBagConstraint Está clase no es más que una lista de atributos que pueden ser modificados, en general, usando las constantes que define la misma clase, para cambiar el comportamiento de un celda en particular de una grilla de componentes manejada por un GridBagLayout. A continuación se detallan los atributos más usados: gridx, gridy Indican la nueva celda, comenzando a numerar desde cero, por supuesto. También se puede usar la constante RELATIVE que se refiere a la celda a la derecha de la actual, y si es la última de la fila, a la primera de la fila siguiente. gridwidth, gridheight Estos atributos indican el tamaño de la nueva celda, y tienen el mismo significado que lso atributos COLSPAN y ROWSPAN de una tabla HTML, respectivamente. Aquí se puede usar la constante REMAINDER para indicar el uso de todas las celdas posibles en esa dirección. Michael Moossen Departamento de Informática. U.T.F.S.M. 161

174 6.4. DISEÑO DE INTERFACES fill Indica como ajustar el tamaño del componente que contiene la celda. Para esto se puede usar alguna de las siguientes contantes: NONE, HORIZONTAL, VERTICAL, BOTH ipadx, ipady Indican el margen interno de la celda, en ambos caso, la cantidad de píxeles indicada se distribuye a ambos lados. insets Indica el espaciado interno, como un objeto de la clase Insets, cuyo constructor más importante es: Insets(int t, int l, int b, int r) Donde t es la cantidad de píxeles por arriba, l por la izquierda, b por abajo y r por la derecha. anchor Indica la alineación del control dentro de la celda. Se pueden usar las siguientes constantes: CENTER, NORTH, SOUTH, EAST, WEST, NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST. weightx, weighty Permite controlar la distribución del espacio, asignando pesos relativos a cada celda. Por defecto, la esquina inferior derecha tiene todo el peso, por lo tanto, es la única que puede cambiar de tamaño. Ejemplo: Posicionamiento Absoluto También existe la posibilidad de usar posicionamiento y tamaño absoluto, para esto es necesario eliminar el administrador de disposición por defecto del panel en cuestión, mediante el método setlayout() y pasándole como parámetro null. Además, existen un par de métodos que permiten indicar el tamaño y la posición de un componente: void setbounds(int pl, int pt, int sx, int sy) Este método permite indicar tanto la posición del componente, indicando su esquina superior izquierda, como el tamaño del mismo, indicando su ancho y alto. void setsize(int sx, int sy) Este método permite indicar el tamaño de un componente mediante su ancho y alto Relleno A veces es necesario dejar espacios en blanco entre componentes, para obtener una disposición óptima. Para esto se usa la clase Box, con la cual se pueden crear una variedad de componentes que ocupan espacio, pero no son visibles. Rectángulo Rígido Para crear un rectángulo rígido se puede usar el siguiente método estático: 162 Departamento de Informática. U.T.F.S.M. Michael Moossen

175 CAPÍTULO 6. INTERFACES GRÁFICAS Component Box.createRigidArea(Dimension size) Elástico Para ocupar todo el espacio posible entre dos componentes, existen los elásticos: Component Box.createHorizontalGlue() Component Box.createVerticalGlue() Rectángulo Flexible También se puede crear un rectángulo con cierta flexibilidad, definiendo su tamaño mínimo, máximo y preferido. Box.Filler(Dimension min, Dimension pre, Dimension max) Bordes Para ponerle bordes a los componentes, existe la clase BorderFactory, con la cual se pueden crear un sin número de bordes. Bordes Vacíos Este tipo de borde se usa para darle mayor espacio al componente, y se crea usando el método: Border createemptyborder(int t, int l, int b, int r) Bordes Simples Este tipo de borde simplemente dibuja una línea de cierto color y ancho alrededor del componente, y se crea usando el método: Border createlineborder(color c, int w) Bordes tipo Marco Este tipo de bordes, permiten especificar el ancho del borde en cada dirección: MatteBorder creatematteborder(int t, int l, int b, int r, Color c) También existen métodos que permiten usar una imagen como patrón del borde. Bordes con Título Para crear un borde con título, basta crear un borde cualquiera y luego ponerle el título con este método: TitledBorder createtitledborder(border b, String t) Bordes Compuestos Se pueden combinar también varios bordes en uno, mediante este método: CompoundBorder createcompoundborder(border b1, Border b2) Por ejemplo, un botón con un margen rojo: JButton b = new JButton("botón"); b.setborder(borderfactory.createlineborder(color.red, 5); Michael Moossen Departamento de Informática. U.T.F.S.M. 163

176 6.4. DISEÑO DE INTERFACES Look & Feel Look & Feel es un término que se usa para denominar el aspecto general que tiene una aplicación gráfica. Por ejemplo, aspectos como: El color de la barra de título, el color, tipo, y tamaño de las fuentes, etc. El Look & Feel normalmente depende del sistema operativo, ya que es él, el que en definitiva ofrece el sistema de ventanas, así por ejemplo una aplicación típica para windows 98, se ve como se ve porque el sistema operativo la pinta a su pinta, y así la misma aplicación ejecutándose en Windows XP tiene un Look & Feel totalmente diferente, es debido a que la mayoría de las aplicaciones no manejan directamente su Look & Feel, aunque es posible hacerlo, como es el caso por ejemplo de WinAmp. Así, como Java es independiente de plataforma, sus sistema de ventanas es también totalmente independiente del sistema operativo, por lo que en general, las aplicaciones Java se ven igual en todas las plataformas y también muy distintas a otras aplicaciones nativas de esa plataforma. Sin embargo, es posible que una aplicación Java se pinte como una aplicación nativa, más aún también es posible implementar un Look & Feel propio. La clase UIManager Está clase permite manipular el Look & Feel de una aplicación, para esto se disponen de los siguientes métodos. void setlookandfeel(string classname) Este método necesita como argumento el nombre de una clase que extienda la clase LookAndFeel. Si la interfaz aún no está creada no es necesario hacer nada más. Sin embargo, si ya se ha creado la interfaz, es necesario actualizarla, lo cual se hace vía: SwingUtilities.updateComponentTreeUI(frame); Donde, frame es la ventana principal de la aplicación, o la raíz del árbol de componentes que se desea actualizar. Debido a que también pueden haber cambios de tamaño de los componentes, es aconsejable, volver a distribuir los componentes vía: frame.pack();. String getcrossplatformlookandfeelclassname() Para obtener el nombre de la clase que implementa el Look & Feel independiente de la plataforma, en general, será: javax.swing.plaf.metal.metallookandfeel Este es el Look & Feel por defecto de la aplicaciones Java. String getsystemlookandfeelclassname() Este método sirve para obtener la clase que implementa el Look & Feel de la plataforma en la cual se está ejecutando, las que pueden ser: Windows: com.sun.java.swing.plaf.windows.windowslookandfeel Solaris: javax.swing.plaf.mac.maclookandfeel Macintosh: javax.swing.plaf.metal.metallookandfeel Se debe tener presente que, en general, los Look & Feel de plataformas específicas sólo están disponibles en esas plataformas. Ejemplo: 164 Departamento de Informática. U.T.F.S.M. Michael Moossen

177 CAPÍTULO 6. INTERFACES GRÁFICAS 6.5. Contenedores Ventanas de Diálogo Existen dos tipos de ventanas de dialogo, las modales, que bloquean la aplicación mientras esperan la interacción con el usuario y las no-modales, que no bloquean la aplicación. La clase que encapsula toda la funcionalidad de una ventana de dialogo, es la JDialog, sin embargo, la clase JOptionPane ofrece una variada gama de ventanas de dialogo modales simples: Ventanas de Mensaje Este tipo de ventana sólo despliega un mensaje y espera hasta que el usuario lo lea y presione el botón. Se usa mediante: Donde static void showmessagedialog(component parent, Object msg, String title, int msgtype, Icon icon) parent es el componente a bloquear msg es el mensaje a desplegar title es el título de la ventana msgtype es el tipo de mensaje, dependiendo del cual se muestra un ícono por defecto, el cual puede tomar cualquiera de los siguientes valores: INFORMATION MESSAGE, WARNING MESSAGE, ERROR MESSAGE, QUESTION MESSAGE o PLAIN MESSAGE, y icon es un ícono personalizado. También existen otras versiones de este métodos(y los demás) con menos parámetros, los que toman algunos valores por defecto. Ventanas de Confirmación Este tipo de ventana pide la confimación o cancelación de una acción al usuario. Se ejecuta vía: static int showconfirmdialog(component parent, Object msg, String title, int opttype, int msgtype, Icon icon) Los parámetros son idénticos a los de la venta de mensaje, excepto por opttype que indica si se despliegan dos o tres botones, y que puede ser: YES NO OPTION o YES NO CANCEL OPTION. Además, este método devuelve el valor del botón seleccionado por el usuario, que puede ser: YES OPTION, NO OPTION o CANCEL OPTION. Ventanas de Entrada Esta ventana permite al usuario ingresar un valor. static String showinputdialog(component parent, Object msg, String title, int msgtype) El método retorna el valor ingresado por el usuario. Ventanas de Opciones Está es la ventana más genérica de todas, que permite personalizar las etiquetas de los botones mediante su parámtero options. static int showoptiondialog(component parent, Object msg, String title, int opttype, int msgtype, Icon icon, Object[] options, Object initialvalue) Michael Moossen Departamento de Informática. U.T.F.S.M. 165

178 6.5. CONTENEDORES También existen algunas ventanas de dialogo más especializadas, como lo son: Ventanas de Selección de Colores Existe un control especializado para la selección de colores encapsulado en la clase JColorChooser, el cual puede ser usado en cualquier contenedor, y que además provee de un dialogo por defecto, el que se usa mediante el método: static Color showdialog(component parent, String title, Color default) El valor de retorno es el color seleccionado. Ventanas de Selección de Archivos También existen un control especializado para la selección de archivos(o directorios) encapsulado en la clase JFileChooser. Los principales constructores de esta clase son: JFileChooser() JFileChooser(String inidir) JFileChooser(File inidir) Permitiendo indicar un directorio de inicio para la selección, sino es la carpeta del usuario, según el sistema operativo. Otros métodos que influyen en el comportamiento de la ventana de dialogo: File getselectedfile() Este método devuelve el archivo seleccionado. void setfilehidingenabled(boolean fh) Permite indicar si se desea acceder a archivos ocultos o no. void setfilefilter(filefilter ff) void addchoosablefilefilter(filefilter ff) Estos dos métodos permiten filtrar los archivos a seleccionar. Para esto es necesario extender la clase javax.swing.filechooser.filefilter especificando los dos métodos: boolean accept(file f) String getdescription() El primero filtra los archivos aceptando o rechazando cada uno, y el segundo método da una descripción del tipo de archivos que se filtran. Por ejemplo, class GIFFileFilter extends FileFilter { public boolean accept(file f) { return (f.isdirectory() (f.getname().endswith(".gif"))); public String getdescription() { return "Archivos GIF"; void setfileselectionmode(int mode) Permite indicar el modo de operación del control. Se pueden usar las siguentes constantes: FILES ONLY, DIRECTORIES ONLY o FILES AND DIRECTORIES. Finalmente, la ventana de dialogo se ejecuta mediante alguno de los siguientes métodos: int showopendialog(component parent) int showsavedialog(component parent) int showdialog(component parent, String title) 166 Departamento de Informática. U.T.F.S.M. Michael Moossen

179 CAPÍTULO 6. INTERFACES GRÁFICAS Los primeros dos métodos sólo obtienen el título que despliegan de la configuración del sistema operativo en que se ejecuta la aplicación. El valor de retorno puede ser APPROVE OPTION o CANCEL OPTION. Todas estas ventanas son modales, para crear y usar otras ventanas de dialogo, ya sea modales o no, es necesario extender la clase JDialog Contenedores Generales Panel de Desplazamiento Este tipo de panel permite desplazarse por un objeto(que debe implementar la interfaz Scrollable) que es más grande que el area visible, por ejemplo una imagen o un documento de texto. Esta funcionalidad está encapsulada por la clase JScrollPane. Sus principales constructores son: JScrollPane(Component c) JScrollPane(Component c, int hp, int vp) En donde, c es el componente al cual se le desea atachar las barras de deslizamiento, y, hp y vp, son las opciones para las barras de desplazamiento horizontales y verticales. Las cuales pueden ser: VERTICAL SCROLLBAR AS NEEDED, HORIZONTAL SCROLLBAR AS NEEDED, VERTICAL SCROLLBAR ALWAYS, HORIZONTAL SCROLLBAR ALWAYS, VERTICAL SCROLLBAR y HORIZONTAL SCROLLBAR NEVER respectivamente. También existen una serie de áreas que se pueden personalizar en un JScrollPane, como lo son las esquinas y los llamados encabezados de fila y columna, esto se ve más claramente en el siguiente diagrama: Para personalizar estas áreas se usan los siguientes métodos: void setcolumnheaderview(component c) void setrowheaderview(component c) void setcorner(string key, Component c) En donde, c es el componente que personaliza el área especificada, además el último método tiene un parámetro key que indica que esquina se va a personalizar, para esto se usan las constantes: UPPER LEFT CORNER, LOWER LEFT CORNER, UPPER RIGHT CORNER y LOWER RIGHT CORNER Michael Moossen Departamento de Informática. U.T.F.S.M. 167

180 6.5. CONTENEDORES Panel de División Un panel de división permite agrupar dos componentes en un contenedor divididos por una barra desplazable, que permite ajustar el tamaño de los componentes. La clase que escapsula esta funcionalidad se llama JSplitPane, y su constructor más usado es: JSplitPane(int o, Component c1, Component c2) En donde, c1 y c2 son los dos componentes a separar, y o es la orientación de la barra separadora, que puede ser: HORIZONTAL SPLIT, VERTICAL SPLIT. Sus métodos más usados son: void setonetouchexpandable(boolean e) Este método indica si se deben o no dibujar los botones en el divisor, que permiten maximizar el espacio de cada componente. void setdividerlocation(int pxl) Este componente indicar la posición inicial del divisor en píxeles desde la esquina superior izquierda. void setdividerlocation(double perc) Lo mismo que el anterior pero en porcentaje. void setdividersize(int pxl) Indica el tamaño del divisor en píxeles. void setresizeweight(double rat) Indica el peso de cada componente al momento de cambiar el tamaño del contenedor. Ejemplo: Panel de Pestañas Un panel de pestañas permite, seleccionar de un gran conjunto de componentes un subgrupo a la vez, identificados por pestañas con nombre. La clase que encapsula este comportamiento es JTabbedPane. Sus constructores más importantes son: JTabbedPane(int tp) Este constructor permite indicar la posición de las pestañas, mediante las constantes: RIGHT, LEFT, TOP y BOTTOM. JTabbedPane(int tp, int tl) Con este constructor, además de poder indicar la posición de las pestañas, se puede indicar el comportamiento de pestañas, si no todas caben en el espacio dado: WRAP TAB LAYOUT con lo cual se acomodan en líneas y SCROLL TAB LAYOUT con lo cual se agrega botones de desplazamiento. Sus métodos más usados son: 168 Departamento de Informática. U.T.F.S.M. Michael Moossen

181 CAPÍTULO 6. INTERFACES GRÁFICAS void addtab(string title, Icon icon, Component c, String tip) Este método agrega una nueva pestaña de nombre title, con un ícono icon, con un texto de ayuda tip, para el componente c. También existen otras versiones de este mismo método con menos parámetros. void setenabledat(boolean b, int index) Permite habilitar o deshabilitar la pestaña index. int getselectedindex() Permite obtener el índice de la pestaña seleccionada actualmente. Ejemplo: Barra de Herramientas Una barra de herramientas es un panel con un conjunto de componentes, generalmente botones, pero pueden ser cualquier cosa, que permiten acceder de forma fácil y rápida a ciertas funcionalidades de una aplicación, esta barra generalmente es flotante, es decir, puede ser reubicada por el usuario. Esta funcionalidad es encapsulada por la clase JToolBar. Su constructor más importante es: JToolBar(String name, int ori) En donde, name es el nombre de la barra y ori es la orientación inicial de esta, la cual puede ser HORIZONTAL o VERTICAL. Sus métodos más importantes son: void add(component c) Este método agrega un nuevo componente a la barra. void addseparator() Este método agrega un separador a la barra. void setfloatable(boolean b) Este método permite indicar si la barra es flotante o no. Barra de Menu Una barra de menú contiene una serie de menús desplegables, que permiten acceder a la mayor parte de las funcionalidades de una aplicación típica. Por defecto se ubica en el borde superior de una ventana. Esta funcionalidad está encapsulada por la clase JMenuBar. Sus métodos más importantes son: JMenu add(jmenu m) Este método permite agregar un nuevo menú a la barra. Michael Moossen Departamento de Informática. U.T.F.S.M. 169

182 6.5. CONTENEDORES JMenu getmenu(int index) Permite obtener el menú en la posición indicada. void setmargin(insets mgs) Permite indicar los márgenes de la barra. Menú Un menú es una lista desplegable de comandos, que se activan desde una barra de menú, son los más típicos, y su funcionalidad se encapsula en la clase JMenu. También hay otros tipos de menús que se activan al ejecutar alguna acción sobre algún componente, estos son los llamados Pop-up Menús, cuya funcionalidad se encuentra encapsulada en la clase JPopupMenu. El constructor más usado de JMenu: JMenu(String text) Con el cual se crea un menú con nombre text. Sus métodos más importantes son: JMenuItem add(jmenuitem item) El cual agrega un nuevo ítem al menú. void addseparator() Este método agrega al menú un separador. La clase JPopupMenu se usa casi igual, excepto que hay que desplegar el menú, para esto está el método: void show(component c, int x, int y) El cual despliega el menú, sobre el componente c y en las coordenadas indicadas. El despliegue se efectúa, generalmente, en un evento del mouse. Así, la clase MouseEvent provee todos los métodos necesarios para desplegar un menú: Component getcomponent() Permite obtener el componente sobre el cual sucedió el evento. boolean ispopuptrigger() Permite discriminar si el evento es o no gatillador de un menú. int getx() int gety() Estos métodos devuelven las coordenadas en que sucedió el evento. Item de Menú Un ítem de menú puede ser una etiqueta con o sin un ícono (JMenuItem), o un check box (JCheckBoxMenuItem) o un radio button (JRadioButtonMenuItem). Todos los cuales se usan de manera bastante similar, excepto en el manejo de eventos, esto debido a su super clase en común llamada AbstractButton, la cual ofrece todos los métodos necesarios: void settext(string label) Permite indica un texto a desplegar en el botón. void seticon(icon icon) Permite, además del texto, desplegar un ícono. Existen también una serie de otros métodos similares, que permiten personalizar los íconos cuando el bóton esta deshabilitado, o seleccionado, etc. 170 Departamento de Informática. U.T.F.S.M. Michael Moossen

183 CAPÍTULO 6. INTERFACES GRÁFICAS void setenabled(boolean b) Permite indicar si el botón esta habilitado o no. void setmargin(insets mrg) Permite indicar los márgenes del botón void seticontextgap(int gap) Permite indicar el tamaño de la separación entre el ícono y el texto. void sethorizontaltextposition(int p) Permite indicar la posición del texto relativa al ícono. Se pueden usar las siguientes constantes: LEFT, RIGHT o CENTER. void setactioncommand(string cmd) Permite indicar una palabra clave, con la cual se puede luego en un manejador de eventos discriminar el botón accionado. Por defecto, se usa el texto del botón. void setmnemonic(int mn) Permite indicar la letra resaltada, con la cual se activa el ítem si se presiona ALT- y la letra indicada. Este método se usa en conjunto con la clase KeyEvent que provee de las siguientes constantes: Números: Desde la VK 0 a la VK 9. Letras: Desde la VK A a la VK Z. Teclas de Función: Desde la VK F1 a la VK F24. Además de todos estos métodos heredados de AbstractButton, también todos son un JMenuItem, cuyo mayor aporte es el siguiente método: void setaccelerator(keystroke ks) Este método asigna una combinación de teclas directamente al menú ítem en cuestión. Para crear un KeyStroke se tiene el método: KeyStroke.getKeyStroke(int key, int modifiers) Donde key es una tecla de KeyEvent y modifiers es una composición de las constantes de InputEvent: CTRL MASK, SHIFT MASK y/o ALT MASK Componentes En esta sección se verán una serie de componentes gráficos que permiten la interacción con el usuario Etiquetas Las etiquetas son componentes que sólo permiten desplegar información simple, ya sea através de una línea de texto y/o un ícono. Esta funcionalidad es encapculada por la clase JLabel. Sus constructores más usados son: JLabel(String label) JLabel(Icon icon) Y sus métodos más importantes: void seticon(icon icon) Este método permite setear el ícono a desplegar. void settext(string label) Este método permite indicar el texto a desplegar. Este texto puede ser texto HTML. Michael Moossen Departamento de Informática. U.T.F.S.M. 171

184 6.6. COMPONENTES Botones Los botones son componentes que por naturaleza ejecutan una acción al ser presionados (tanto por teclado o mouse). Swing ofrece una serie de botones con comportamientos específicos, y todos extienden la clase AbstractButton, por lo tanto, están disponibles todos los métodos indicados en la sección??. Botones Simples Un botón simple es un componente que ejecuta una acción directmante cuando es presionado y que vuelve a su estado original inmediatamente. Esta funcionalidad es encapsulada por la clase JButton. Sus constructores más usados son: JButton(String label) JButton(String label, Icon icon) Para indicar el botón por defecto de una ventana, se usa la siguiente sentencia: frame.getrootpane().setdefaultbutton(button); Botones de Dos Estados Un botón de dos estados es un componente, que por defecto no ejecuta ninguna acción inmediata al ser presionado, sino que mantiene su estado el cual puede ser de dos tipos: presionado o no, activado/desactivado, seleccionado o no, etc. Su funcionalidad básica se encapsula en la clase JToggleButton. Su contructor más usado es: JToggleButton(String label, Icon icon, boolean selected) Donde se indica, además de una etiqueta e ícono, su estado inicial: true si esta presionado, false sino. Su apariencia es bastante parecida a la de un botón simple. Sin embargo, está clase posee dos subclases, que apesar de tener la misma funcionalidad básica, tienen algunas variaciones en apariencia principalmente y funcionalidad también. Botones de Selección Simple Este tipo de botón, tiene exactamente la misma funcionalidad que la vista anteriormente, la única diferencia radica en su apariencia. Su funcionalidad está encapsulada en la clase JCheckBox. Como es de esperarse su uso es idéntico: JCheckBox(String label, Icon icon, boolean selected) Botones de Selección Grupal Este tipo de botón, además de diferenciarse por su apariencia, también modifica su funcionalidad, en cuanto permiten seleccionar una y sólo una de las opciones de un grupo a la vez. Su funcionalidad está encapsulada en la clase JRadioButton. Su constructor no cambia: JRadioButton(String label, Icon icon, boolean selected) Pero, debe usarse en conjunto con la clase ButtonGroup, la cual tiene el siguiente método: void add(abstractbutton button) 172 Departamento de Informática. U.T.F.S.M. Michael Moossen

185 CAPÍTULO 6. INTERFACES GRÁFICAS Listas Desplegables Una lista desplegable es un componente que permite seleccionar(o ingresar) una opción entre varias alternativas, desplegando sólo la opción seleccionada. Su funcionalidad se encapsula en la clase JComboBox. Sus constructores más usados son: JComboBox(Object[] items) JComboBox(Vector items) Los cuales indican las opciones ofrecidas al usuario por defecto. Sus métodos más importantes son: Object getselecteditem() Este método permite obtener el ítem seleccionado. void setselecteditem(object item) Este método permite indicar el ítem seleccionado por defecto. void seteditable(boolean flag) Este método permite indicar si el componente admite que el usuario ingrese otra opción no contenida en la lista de opciones. void setenabled(boolean flag) Este método permite indicar si el componente está habilitado o no Cajas de Texto Una caja de texto permite al usuario ingresar un texto corto en una línea. Esta funcionalidad está encapsulada en la clase JTextField. Sus constructores más usados son: JTextField(int cols) JTextField(String text, int cols) Los cuales permiten indicar el tamaño del componente en caracteres y opcionalmente un texto por defecto. Sus métodos más importantes son: void sethorizontalalignment(int ha) Este método permite indicar el alineamiento del texto, el cual puede ser LEFT, RIGHT o CENTER. void setcolumns(int cols) Este método permite indicar cambiar el tamaño del componente. String gettext() Este método permite obtener el texto actual de la caja de texto Campos de Password Este componente es muy similar a una caja de texto, excepto que el texto ingresado, se despliega con un caracter comodín. Esta funcionalidad se encapsula en la clase JPasswordField. Sus constructores son idénticos a los de una caja de texto: JPasswordField(int cols) JPasswordField(String text, int cols) Michael Moossen Departamento de Informática. U.T.F.S.M. 173

186 6.6. COMPONENTES Y sus métodos más importantes son: void setechochar(char echo) Permite indicar el caracter comodín a desplegar. char [] getpassword() Permite obtener el texto ingresado Áreas de Texto Una área de texto permite al usuario ingresar y/o editar texto a através de varias líneas, lo cual es la limitación de una caja de texto. Está funcionalidad está encapsulada en la clase JTextArea. Sus constructores son idénticos a los de una caja de texto: JTextArea(String text) JTextArea(int cols, int rows) Que permiten indicar un texto por defecto y el tamaño inicial del área de texto. Y sus métodos más importantes son: void setlinewrap(boolean lw) Permite indicar si al llegar el texto a un extremo del área de texto, este se corta automáticamente, siguiendo en la línea siguiente. void setwrapstyledword(boolean wtw) Permite indicar el estilo de corte de línea automático, ya sea por palabras o por caracteres. void setfont(font font) Permite indicar la fuente a usar para desplegar el texto Listas Las listas son componentes que despliegan una serie de opciones y que permiten que el usuario seleccione una o más ítemes. Esta funcionalidad se encapsula en la clase JList Sus principales constructores son: JList(Object[] items) JList(Vector items) JList(ListModel model) Todos permiten indicar los ítemes iniciales. Luego se verá qué es un objeto ListModel. Sus métodos más importantes son: void ensureindexisvisible(int index) Este método manipula el viewport actual, de tal manera que el ítem indicado sea visto por el usuario. ListModel getmodel() Este método obtiene el modelo de datos de la lista. int getselectedindex() Este método obtiene el índice del ítem seleccionado. ListSelectionModel getselectionmodel() Este método obtiene el índice del ítem seleccionado. 174 Departamento de Informática. U.T.F.S.M. Michael Moossen

187 CAPÍTULO 6. INTERFACES GRÁFICAS Object getselectedvalue() Este método obtiene el ítem seleccionado. void setlayoutorientation(int lo) Este método permite especificar el esquema usado para desplegar la mayor cantidad de ítemes posibles a la vez, el cual puede ser: VERTICAL: Indica una lista vertical simple. VERTICAL WRAP: Indica una lista vertical, que al llegar al extremo del viewport sigue en una nueva columna. HORIZONTAL WRAP: Indica una lista horizontal, que al llegar al extremo del viewport sigue en una nueva fila. void setselectionmode(int sm) Permite indicar el modo de selección, el cual puede ser cualquiera de los indicados por la clase ListSelectionModel: SINGLE SELECTION: Indica selección simple. SINGLE INTERVAL SELECTION: Indica selección mediante un sólo intervalo. MULTIPLE INTERVAL SELECTION: Indica selección múltiple, totalmente libre. Para poder manipular los ítemes seleccionados, existe la interfaz ListSelectionModel, cuyos principales métodos son: boolean isselectedindex(int index) Este método permite comprobar si un ítem específico esta o no seleccionado. boolean isselectionempty() Permite comprobar si hay o no algún ítem seleccionado. int getminselectionindex() Devuelve el índice del primer ítem seleccionado. int getmaxselectionindex() Devuelve el índice del último ítem seleccionado. void addlistselectionlistener(listselectionlistener lsl) Permite agregar un nuevo manejador de eventos de selección. Para manejar los eventos de selección de una lista, existe la interfaz ListSelectionListener. Cuyo método más importante es: void valuechanged(listselectionevent e) Que se gatilla, al efecuarse un cambio en los ítemes seleccionados de la lista. Luego, el evento mismo es de la clase ListSelectionEvent. Y tiene los siguientes métodos: boolean getvalueisadjusting() En general, el evento sóloo debe considerarse si este método devuelve false, sino quier decir, que se trata de un evento intermedio de ajuste. int getfirstindex() Permite obtener el primer índice seleccionado. int getlastindex() Permite obtener el último índice seleccionado. Michael Moossen Departamento de Informática. U.T.F.S.M. 175

188 6.6. COMPONENTES Por último, y lo más importante, es el modelo de datos de la lista, el cual se encapsula en la interfaz ListModel. Toda lista tiene un modelo de datos, que en realidad representa a los datos mismos, generalmente, es un arreglo con el detalle de los datos, pero también se puede usar un método que genere los datos. Ahora, se verán dos clases concretas que pueden ser utilizadas para manipular los datos: La clase DefaultListModel Esta clase implementa la interfaz ListModel mediante un simple vector de datos. Esta clase es utilizada por defecto, cuando se crea una lista con un arreglo o un vector de ítems. La clase AbstractListModel Esta clase es comúnmente usada para ser extendida, y sobrescribir sus métodos para generar los datos. Los métodos mínimos a sobrescribir son: Tablas Object elementat(int index) int getsize() Una tabla es una matriz bidimensional de datos, funcionalidad que encapsula la clase JTable. Sus principales constructores son: JTable(Object[][] data, Object[] colnames) JTable(Vector data, Vector colnames) JTable(TableModel model) Los dos primeros, permiten indicar los datos iniciales como una matrix o vector bidimensional, y además un vector de nombres de las columnas, en cambio, el último utiliza un modelo de datos, con la misma idea de la clase ListModel, pero para tablas. Una tabla puede ser tratada también como una lista, para lo cual se tienen métodos análogos a esta: void setselectionmode(int sm) ListSelectionModel getselectionmodel() Con respecto al modelo de datos de la tabla, el cual se encapsula en la interfaz TableModel. Generalmente, es un arreglo bidimensional con el detalle de los datos, pero también se puede usar un método que genere los datos. Ahora, al igual que con las listas, se verán dos clases concretas que pueden ser utilizadas para manipular los datos: La clase DefaultTableModel Esta clase implementa la interfaz TableModel mediante un simple vector bidimensional de datos. Esta clase es utilizada por defecto, cuando se crea una tabla con un arreglo o un vector bidimensional de ítems. La clase AbstractTableModel Esta clase es comúnmente usada para ser extendida, y sobrescribir sus métodos para generar los datos. Los métodos mínimos a sobrescribir son: Object getvalueat(int row, int column) public String getcolumnname(int column) int getrowcount() int getcolumncount() 176 Departamento de Informática. U.T.F.S.M. Michael Moossen

189 CAPÍTULO 6. INTERFACES GRÁFICAS Para manejar los eventos de una tabla existen dos métodos: La Díficil Este método es vía TableModelListener y TableModelEvent, parecido a como se vió con las listas. La Fácil La interfaz TableModel también ofrece una serie de métodos relacionados con el manejo de eventos que se pueden sobrescribir, para personalizar su comportamiento: void setvalueat(object val, int row, int col) Este método se ejecuta, cuando el usuario modifica el valor de la celda específicada, para lo cual la celda debe ser editable. void firetablecellupdated(int row, int col) Este método se gatilla, al haber un cambio en la celda específicada. boolean iscelleditable(int row, int col) Este método permite personalizar las celdas editables por el usuario Lo que faltó Por tiempo y espacio, este documento no contempla todos los componentes disponibles. Aquí se nombran los más importantes y se indican referencias de documentación. Contenedores Especializados JRootPane, JLayeredPane y Glass Pane Aplicaciones Multidocumento (MDI) JDesktopPane y JInternalFrame Otros Componentes Básicos Spinners: La clase JSpinner Barras de Progreso: Las clases JProgressBar y ProgressMonitor http: //java.sun.com/j2se/1.4/docs/api/javax/swing/progressmonitor.html Sliders: Michael Moossen Departamento de Informática. U.T.F.S.M. 177

190 6.7. LO QUE FALTÓ La clase JSlider Modelo de Documentos Controles de Texto Formateado Editores de Texto de formatos como HTML y RTF. Las clases JFormattedTextField, JEditorPane y JTextPane Personalización de Tablas Selección de Columnas Editores de campos personalizados Validación de campos Eventos en los encabezados Árboles Jerárquicos JTree, TreeNode, MutableTreeNode, TreeModel, TreeSelectionModel, TreeCellEditor y TreePath Departamento de Informática. U.T.F.S.M. Michael Moossen

191 Capítulo 7 Java 2 Enterprise Edition 7.1. Introducción Java 2 Enterprise Edition nace de la necesidad de estandarizar el desarrollo de aplicaciones distribuidas con Java, garantizando un buen rendimiento, alta escalabilidad, soporte de transacciones de alto nivel, servicios de seguridad básicos, acceso a bases de datos, administración del ciclo de vida de los objetos, reutilización fácil de componentes, etc. Permitiendo de esta manera que el desarrollador se concentre exclusivamente en la lógica del negocio de la aplicación a desarrollar. La definición oficial de J2EE es: J2EE es la especificación de una arquitectura de componentes para el desarrollo de aplicaciones coorporativas orientadas a objetos y distribuidas. Sun Microsystems 1998 Esto significa en la práctica: o también J2EE es el framework que todo el mundo usa para escribir soporte de transacciones, acceso a datos, seguridad y ejecución de componentes del lado del servidor. J2EE es un conjunto de patrones de diseño que especifican como desarrollar componenttes reutilizables para aplicaciones distribuidas. J2EE es una especificación y NO un producto, luego hay muchas aplicaciones de terceros que cumplen con la especificación J2EE Enterprise Java Bean Cuando se habla de un componente, se refiere a un Enterprise Java Bean (EJB), los cuales no tienen ninguna relación con los Java Beans, a pesar de la similitud de los nombres. Estos componentes se ejecutan dentro de contenedores EJB que normalmente son parte de un servidor EJB. Estos concentran la lógica del negocio de forma reutilizable y estándar, de forma tal de poder ser distribuidos y también poder interactuar con otros componentes, como servicios Web tipo SOAP o WSDL sin mayor esfuerzo Contenedor EJB Un contenedor EJB es la plataforma en la cual se ejecutan los EJBs, estos dan soporte y permiten manejar a un mayor nivel el acceso a bases de datos, a servidores de mail, a transacciones, a servicios de mensajería y seguridad, administración de persistencia, etc. 179

192 7.1. INTRODUCCIÓN También se habla de Servidores EJB, que además de ser un contenedor, ofrece el servicio de los EJBs a agentes externos mediante Java Naming and Directory Interface (JNDI), como por ejemplo a un servidor Web. Así tambien, se dice que una suite que ofrece tanto un servidor Web como un servidor de EJB, es un servidor de aplicaciones Arquitectura En el siguiente diagrama se ve claramente el modelo de cuatro capas en que está basada la especificación J2EE: Se tienen las cuatro capas bien definidas y separadas unas de otras: Presentación Esta capa es lo que el usuario ve, es estático(tonto) y puede ser por ejemplo HTML o una aplicación de escritorio. Lógica de la Presentación Esta es la capa que contiene la lógica de como presentar los datos al usuario, generalmente son páginas JSP o EJBs. Lógica del Negocio Está es la capa más importante de todas, que define la lógica del negocio de la aplicación, este es el campo de aplicación principal de los EJBs Datos Está es la última capa, que simplemente contiene los datos en algún RDBMS u otra forma de persistencia. Este esquema muestra también lo fácil que es distribuir los distintos componentes de cada capa: Aquí vemos que se tienen varios clientes, que pueden o no pasar por un control de seguridad, cuya carga se puede distribuir mediante una serie de servidores Web, los cuales pueden también o no necesitar cierta identificación para acceder a los EJB que también pueden estar distribuidos mediante una serie de servidores EJB los que finalmente acceden a los datos distribuidos también identificándose. 180 Departamento de Informática. U.T.F.S.M. Michael Moossen

Elementos léxicos del lenguaje de programación Java

Elementos léxicos del lenguaje de programación Java Elementos léxicos del lenguaje de programación Java Elementos léxicos del lenguaje de programación Java Palabras reservadas Identificadores Literales Operadores Delimitadores Comentarios Apéndices Operadores

Más detalles

www.aprendoencasa.com Curso Introducción JAVA Pág.: 1

www.aprendoencasa.com Curso Introducción JAVA Pág.: 1 www.aprendoencasa.com Curso Introducción JAVA Pág.: 1 Introducción Java es un lenguaje basado en la programación orientada a objetos (POO), este tipo de programación va más allá del tipo de programación

Más detalles

CONCEPTOS BASICOS DEL LENGUAJE JAVA

CONCEPTOS BASICOS DEL LENGUAJE JAVA CONCEPTOS BASICOS DEL LENGUAJE JAVA NOMENCLATURA GENERAL En Java se distinguen las letras mayúsculas y minúsculas. Las reglas del lenguaje respecto a los nombres de variables son muy amplias y permiten

Más detalles

Lo que necesitaremos para programar en Java, será un editor de texto o IDE y la JDK.

Lo que necesitaremos para programar en Java, será un editor de texto o IDE y la JDK. Introducción Java surgió en 1991 dentro de la empresa Sun Microsystems como un lenguaje de programación sencillo y universal destinado a electrodomésticos. La reducida potencia de cálculo y memoria de

Más detalles

Tema 2. El lenguaje de programación Java (Parte 1)

Tema 2. El lenguaje de programación Java (Parte 1) Programación en Java Tema 2. El lenguaje de programación Java (Parte 1) Luis Rodríguez Baena Facultad de Informática Elementos del lenguaje (I) El juego de caracteres. No utiliza ASCII, sino Unicode de

Más detalles

Práctica 3 mtp. metodoloxía e tecnoloxía da programación. Presentar detalles básicos sobre la sintaxis del lenguaje de programación Java.

Práctica 3 mtp. metodoloxía e tecnoloxía da programación. Presentar detalles básicos sobre la sintaxis del lenguaje de programación Java. Práctica 3 mtp metodoloxía e tecnoloxía da programación Objetivos: Duración: Presentar detalles básicos sobre la sintaxis del lenguaje de programación Java. 1 semana 1.- Identificadores Los identificadores

Más detalles

Gestor de aplicaciones Java. Esta herramienta es el intérprete de los archivos de clase generados por el javac (compilador).

Gestor de aplicaciones Java. Esta herramienta es el intérprete de los archivos de clase generados por el javac (compilador). CAPÍTULO 4 Requerimientos de software Este capítulo presenta las herramientas necesarias para la construcción y ejecución de programas en el lenguaje de programación JAVA, los requerimientos mínimos de

Más detalles

JavaScript. Contenidos. Introducción El lenguaje Validación de formularios. Programación en Internet 2005-2006. DLSI - Universidad de Alicante 1

JavaScript. Contenidos. Introducción El lenguaje Validación de formularios. Programación en Internet 2005-2006. DLSI - Universidad de Alicante 1 Departamento de Lenguajes y Sistemas Informáticos JavaScript Programación en Internet Curso 2005-2006 Contenidos Introducción El lenguaje Validación de formularios DLSI - Universidad de Alicante 1 Introducción

Más detalles

Repaso de las características más importantes de la programación Java y su adaptación a Android

Repaso de las características más importantes de la programación Java y su adaptación a Android Repaso de las características más importantes de la programación Java y su adaptación a Android 1. Entorno de programación en java 2. Variables y tipos de datos 3. Operaciones y operadores 4. Clases y

Más detalles

TEMA 2. Agenda. Fundamentos de JAVA

TEMA 2. Agenda. Fundamentos de JAVA TEMA 2 Fundamentos de JAVA V1.3 Manuel Pereira González Agenda Introducción Historia de Java Características Principales Hello World Tipos Operadores Control de Flujo E/S básica Atributos y Métodos Resumen

Más detalles

Identificadores, palabras reservadas, tipos de datos, operadores aritméticos y el sistema estándar de salida en Java

Identificadores, palabras reservadas, tipos de datos, operadores aritméticos y el sistema estándar de salida en Java Identificadores, palabras reservadas, tipos de datos, operadores aritméticos y el sistema estándar de salida en Java Identificadores Las variables se utilizan en programación para almacenar temporalmente

Más detalles

Objetivo de aprendizaje del tema

Objetivo de aprendizaje del tema Computación II Tema 3. Identificadores, palabras clave y tipos de datos Objetivo de aprendizaje del tema Al finalizar el tema serás capaz de: Distinguir i entre modificadores d válidos y no válidos. Enumerar

Más detalles

Fundamentos del Lenguaje de Programación Java

Fundamentos del Lenguaje de Programación Java Fundamentos del Lenguaje de Programación Java M.C. Jorge Eduardo Ibarra Esquer jorgeeie@uabc.mx El lenguaje Java Estándares de codificación Comentarios Programas con Java Identificadores Palabras clave

Más detalles

Taller de Programación Estructurada en Java Tema 2. Fundamentos de la programación orientada a objetos

Taller de Programación Estructurada en Java Tema 2. Fundamentos de la programación orientada a objetos Taller de Programación Estructurada en Java Tema 2. Fundamentos de la programación orientada a objetos Ingeniero en Computación José Alfredo Cobián Campos josealfredocobian@gmail.com Facultad de Ciencias

Más detalles

Introducción a la Programación en Java. Page 1

Introducción a la Programación en Java. Page 1 Introducción a la Programación en Java Page 1 Qué es Java? Java es un lenguaje de programación de propósito general, orientado a objetos que fue diseñado específicamente para tener tan pocas dependencias

Más detalles

Programa Java. El lenguaje de programación Java. Comentarios. Programa Java. Palabras reservadas. Identificadores

Programa Java. El lenguaje de programación Java. Comentarios. Programa Java. Palabras reservadas. Identificadores El lenguaje de programación Java Programa Java Un programa Java está formado por un conjunto de clases que interactúan entre sí La clase es la unidad básica de programación La ejecución depende de la clase

Más detalles

2. Estructura de un programa en Java

2. Estructura de un programa en Java 24 A. García-Beltrán y J.M. Arranz 2. Estructura de un programa en Java Objetivos: a) Describir la estructura del código fuente de una aplicación Java b) Presentar los conceptos de comentario y de identificador

Más detalles

Qué es Java? Introducción a Java. Lenguajes Orientados a Objetos. Qué es Java? Historia de Java. Objetivos de Java

Qué es Java? Introducción a Java. Lenguajes Orientados a Objetos. Qué es Java? Historia de Java. Objetivos de Java Qué es? Introducción a es Un lenguaje de programación Un entorno de desarrollo Un entorno de ejecución de aplicaciones Un entorno de despliegue de aplicaciones Utilizado para desarrollar, tanto applets

Más detalles

U.T.4.EL ENTORNO DE DESARROLLO

U.T.4.EL ENTORNO DE DESARROLLO U.T.4.EL ENTORNO DE DESARROLLO Lenguaje Java Estamos en unos días en los que cada vez más la informática invade más campos de nuestra vida, estando el ciudadano medio cada vez más familiarizado con términos

Más detalles

Prácticas: Introducción a la programación en Java. Informática (1º Ingeniería Civil) Curso 2011/2012

Prácticas: Introducción a la programación en Java. Informática (1º Ingeniería Civil) Curso 2011/2012 Prácticas: Introducción a la programación en Java Informática (1º Ingeniería Civil) Índice Introducción a Java y al entorno de desarrollo NetBeans Estructura de un programa Tipos de datos Operadores Sentencias

Más detalles

Algunas características de los lenguajes de programación orientados a objetos son:

Algunas características de los lenguajes de programación orientados a objetos son: Programación Orientada a Objetos (POO) La programación orientada a objetos es un paradigma de programación que, como su nombre lo indica, se basa en el uso de objetos estructuras de datos que consisten

Más detalles

Tema 1. Introducción a JAVA

Tema 1. Introducción a JAVA Tema 1. Introducción a JAVA Historia Características Plataforma Java Entorno de desarrollo Ejemplo: Hola mundo Estructura general de un programa Java 1 Historia de Java (i) Surge en 1991: Sun Microsystems

Más detalles

INTRODUCCIÓN A JAVA. Índice

INTRODUCCIÓN A JAVA. Índice INTRODUCCIÓN A JAVA Índice Qué es Java? La plataforma Java 2 La Máquina Virtual de Java Características principales Qué ventajas tengo como desarrollador? Bibliografía 2 1 Qué es Java? La tecnología Java

Más detalles

INF 473 Desarrollo de Aplicaciones en

INF 473 Desarrollo de Aplicaciones en INF 473 Desarrollo de Aplicaciones en Java Unidad II El Lenguaje de Programación Java Prof. José Miguel Rubio jose.rubio.l@ucv.cl jrubio@inf.ucv.cl PUCV Marzo 2008 1 Orígenes del Lenguaje Java 1991. James

Más detalles

Programación en Java. Temario. David Contreras Bárcena

Programación en Java. Temario. David Contreras Bárcena Programación en Java David Contreras Bárcena David Contreras Bárcena (ETSI) - Comillas 1 Temario 1. Introducción 1. Lenguaje java 2. Compilador SDK 1.4 3. Sintaxis 4. Tipos de datos 5. Estructuras de Control

Más detalles

JAVA 8 Los fundamentos del lenguaje Java (con ejercicios prácticos corregidos)

JAVA 8 Los fundamentos del lenguaje Java (con ejercicios prácticos corregidos) Presentación 1. Historia 11 1.1 Por qué Java? 11 1.2 Objetivos del diseño de Java 12 1.3 Auge de Java 13 2. Características de Java 14 2.1 El lenguaje de programación Java 14 2.1.1 Sencillo 15 2.1.2 Orientado

Más detalles

Introducción a Java LSUB. 15 de enero de 2015 GSYC

Introducción a Java LSUB. 15 de enero de 2015 GSYC Introducción a LSUB GSYC 15 de enero de 2015 (cc) 2014 Laboratorio de Sistemas, Algunos derechos reservados. Este trabajo se entrega bajo la licencia Creative Commons Reconocimiento - NoComercial - SinObraDerivada

Más detalles

Diplomado Java. Descripción. Objetivo. A quien está dirigido. Requisitos. Beneficios

Diplomado Java. Descripción. Objetivo. A quien está dirigido. Requisitos. Beneficios Diplomado Java Descripción El lenguaje de programación Java es uno de los más utilizados hoy en día. Su potencia, simplicidad, funcionalidad y capacidad hacen que este lenguaje sea una de las herramientas

Más detalles

James Gosling, creador de Java

James Gosling, creador de Java Lo que Java intenta hacer y lo hace con bastante éxito, es abarcar dominios diferentes. De esa forma le permite efectuar trabajos para de aplicaciones del lado del servidor, del lado del cliente, para

Más detalles

Java en 2 horas. Rodrigo Santamaría

Java en 2 horas. Rodrigo Santamaría + Java en 2 horas Rodrigo Santamaría + Generalidades 2 Desarrollado por Sun en 1995 Hereda mucha de la sintaxis de C (1972) Fuertemente tipado y orientado a objetos Aplicaciones compiladas a bytecode Gestión

Más detalles

Lenguaje C. Tipos de Datos Simples y Estructuras de Control

Lenguaje C. Tipos de Datos Simples y Estructuras de Control Lenguaje C Tipos de Datos Simples y Estructuras de Control Lenguaje C C es un lenguaje de programación creado en 1972 por Dennis M. Ritchie en los Laboratorios Bell como evolución del anterior lenguaje

Más detalles

Ubicación e historia

Ubicación e historia Introducción a Java Ubicación e historia http:// ://java.sun.com/ JDK (Java Development Kit) JDK 1.0, 1995 JDK 1.1, 1996. Modificación en manejo de eventos; nuevas bibliotecas (reflexión, métodos remotos,...)

Más detalles

mari_clau_18@hotmail.com Java es el lenguaje de programación que

mari_clau_18@hotmail.com Java es el lenguaje de programación que PROGRAMACIÓN ORIENTADA A OBJETOS CON JAVA Dra. Maricela Bravo mari_clau_18@hotmail.com JUSTIFICACIÓN Java es el lenguaje de programación que más impacto ha tenido en los últimos años, especialmente en

Más detalles

(volver a Tabla de Contenidos)

(volver a Tabla de Contenidos) Para escribir, compilar y ejecutar un programa en Java lo único que realmente se necesita y no viene incluido con el sistema operativo es el kit de desarrollo de Java, denominado SDK (Software Development

Más detalles

Centro de Capacitación en Tecnologías de la Información. Desarrollo de. diplomado

Centro de Capacitación en Tecnologías de la Información. Desarrollo de. diplomado Centro de Capacitación en Tecnologías de la Información Desarrollo de Objetivo Dotar al alumno de la metodología y los fundamentos de la programación en Java usando la plataforma J2SE (Java 2 Standard

Más detalles

1. Aplicaciones del J2SE SDK1.4.2 de Sun.

1. Aplicaciones del J2SE SDK1.4.2 de Sun. Dept Informatica Índice 1 Aplicaciones del J2SE SDK142 de Sun 1 11 javac 1 12 java 1 13 javadoc 2 14 Las que no se explican 3 2 Guía de estilo de Java 3 21 Clases 3 211 Nombres para las clases 3 212 Estructura

Más detalles

CURSO DE PROGRAMACIÓN EN JAVA J2EE 7 ÍNDICE

CURSO DE PROGRAMACIÓN EN JAVA J2EE 7 ÍNDICE CURSO DE PROGRAMACIÓN EN JAVA J2EE 7 ÍNDICE PRÓLOGO... 13 APECTOS BÁSICOS DE JAVA... 15 1.1. LA MÁQUINA VIRTUAL JAVA... 15 1.2. EDICIONES JAVA... 16 1.3. ESTRUCTURA DE UN PROGRAMA JAVA... 16 1.4. EL MÉTODO

Más detalles

Repaso desarrollo de software Parte #1. Jorge Iván Meza Martínez jimezam@gmail.com

Repaso desarrollo de software Parte #1. Jorge Iván Meza Martínez jimezam@gmail.com Repaso desarrollo de software Parte #1 Jorge Iván Meza Martínez jimezam@gmail.com Especialización en Gestión de Redes de Datos Universidad Nacional de Colombia Sede Manizales 1/55 Contenido Concepto desarrollo

Más detalles

Introducción a la Programación en Java

Introducción a la Programación en Java Contenido Introducción a la Programación en Java Franco Guidi Polanco Escuela de Ingeniería Industrial Pontificia Universidad Católica de Valparaíso, Chile fguidi@ucv.cl! Generalidades de Java! Elementos

Más detalles

Arquitectura y Lenguaje Java

Arquitectura y Lenguaje Java Arquitectura y Lenguaje Java 1 Introducción El lenguaje de programación Java así como su arquitectura se diseñaron para resolver problemas que se presentan en la programación moderna. Se inició como parte

Más detalles

Para leer la entrada de consola, lo primero que se hace es construir un Scanner que este asociado al flujo de entrada estándar System.

Para leer la entrada de consola, lo primero que se hace es construir un Scanner que este asociado al flujo de entrada estándar System. CICLO: 01/2010 Universidad Don Bosco Materia: Lenguaje de Programación III Contenido: 1-Lectura de Datos de entrada. 2-Introduccion a JOPTIONPANE. 3-Estructuras de Control. ->LECTURA DE DATOS DE ENTRADA

Más detalles

Revista Digital Universitaria. 10 de agosto 2004 Volumen 5 Número 7 ISSN: 1067-6079

Revista Digital Universitaria. 10 de agosto 2004 Volumen 5 Número 7 ISSN: 1067-6079 Revista Digital Universitaria 10 de agosto 2004 Volumen 5 Número 7 ISSN: 1067-6079 JAVA o L.I. Anabell Comas Becaria de la Coordinación de Publicaciones Digitales anabell@alethia.dgsca.unam.mx o http://www.revista.unam.mx/vol.7/num12/art104/art104.htm

Más detalles

UNIVERSIDAD DR. JOSE MATIAS DELGADO

UNIVERSIDAD DR. JOSE MATIAS DELGADO NOMBRE DE LA ASIGNATURA: PROGRAMACIÓN DE COMPUTADORAS 4 a. Generalidades. Número de Orden: Prerrequisito (s): 30 Código: PRC 4 PRC 3 Ciclo Académico: Área: VI Especializada U.V.: 4 Duración del Ciclo en

Más detalles

Iniciación a Java. 1.Introducción a Java 2.Programación Orientada a Objetos 3.Fundamentos del lenguaje Java

Iniciación a Java. 1.Introducción a Java 2.Programación Orientada a Objetos 3.Fundamentos del lenguaje Java Iniciación a Java 1.Introducción a Java 2.Programación Orientada a Objetos 3.Fundamentos del lenguaje Java 4.Trabajando con Objetos 5.Manejo de Clases, Métodos y Variables 6.Conceptos Avanzados de Java

Más detalles

Introducción al lenguaje de programación java

Introducción al lenguaje de programación java Introducción al lenguaje de programación java Algoritmia y Programación Slide 1 LENGUAJES DE PROGRAMACION Un lenguaje de programación es un idioma artificial diseñado y creado para expresar algoritmos

Más detalles

Bienvenidos a la presentación: Introducción a conceptos básicos de programación.

Bienvenidos a la presentación: Introducción a conceptos básicos de programación. Bienvenidos a la presentación: Introducción a conceptos básicos de programación. 1 Los programas de computadora son una serie de instrucciones que le dicen a una computadora qué hacer exactamente. Los

Más detalles

Tema: Introducción a Java y Netbeans

Tema: Introducción a Java y Netbeans 1 Tema: Introducción a Java y Netbeans Objetivo Especifico Conocer el uso básico del JDK para la compilación y ejecución de código java desde la linea de comando Conocer el entorno de desarrollo NetBeans

Más detalles

Introducción... 1 Qué es Java?... 1 Compilando a Bytecode... 1 Usando jgrasp Para Hacer el Trabajo Sucio... 5 El Entorno de jgrasp...

Introducción... 1 Qué es Java?... 1 Compilando a Bytecode... 1 Usando jgrasp Para Hacer el Trabajo Sucio... 5 El Entorno de jgrasp... Contenido Introducción... 1 Qué es Java?... 1 Compilando a Bytecode... 1 Usando jgrasp Para Hacer el Trabajo Sucio... 5 El Entorno de jgrasp... 5 Introducción Es tiempo de hablar en detalle de lo que significa

Más detalles

TÉCNICAS DE PROGRAMACIÓN Lenguaje Java. Introducción a Java Daniel Finol

TÉCNICAS DE PROGRAMACIÓN Lenguaje Java. Introducción a Java Daniel Finol TÉCNICAS DE PROGRAMACIÓN Lenguaje Java Introducción a Java Daniel Finol Qué es Java? Java: El lenguaje de programación Java. La Máquina Virtual de Java. La plataforma Java. 2 Qué es Java? El lenguaje de

Más detalles

ÍNDICE. PRÓLOGO... 21 Parte I... 22 Parte II... 23 Apéndices... 23

ÍNDICE. PRÓLOGO... 21 Parte I... 22 Parte II... 23 Apéndices... 23 ÍNDICE PRÓLOGO... 21 Parte I... 22 Parte II... 23 Apéndices... 23 CAPÍTULO 1. INTRODUCCIÓN A JAVA... 25 1.1 CARACTERÍSTICAS DE JAVA... 26 1.2 LA MÁQUINA VIRTUAL JAVA (JVM)... 27 1.3 EDICIONES JAVA... 28

Más detalles

Tema 1: y el lenguaje Java 1.Programación orientada a objetos 2.El lenguaje Java 3.Compilación, bytecode y JVMs 4.Entornos de desarrollo Java 5.Java vs otros lenguajes OO Programación orientada a objetos

Más detalles

Introducción al desarrollo de RIA's con Adobe Flex 3.0 Dia 4

Introducción al desarrollo de RIA's con Adobe Flex 3.0 Dia 4 Introducción al desarrollo de RIA's con Adobe Flex 3.0 Dia 4 by S. Muñoz-Gutiérrez stalinmunoz@yahoo.com, informes@grupolinda.org Grupo LINDA Facultad de Ingeniería UNAM México Octubre-Diciembre 2009 Identificando

Más detalles

CÁTEDRA DE LENGUAJE DE PROGRAMACIÓN JAVA 2014

CÁTEDRA DE LENGUAJE DE PROGRAMACIÓN JAVA 2014 CÁTEDRA DE LENGUAJE DE PROGRAMACIÓN JAVA 2014 Ings. Mario Bressano & Miguel Iwanow ENVÍO 01/2014 Introducción al Lenguaje Java Paquetes Un Paquete en Java es un contenedor de clases que permite agrupar

Más detalles

TECNOLOGÍAS DE DESARROLLO: JAVA

TECNOLOGÍAS DE DESARROLLO: JAVA Página 1 de 13 TECNOLOGÍAS DE DESARROLLO: JAVA Java es un lenguaje de programación de Sun Microsystems originalmente llamado "Oak", que fue concebido bajo la dirección de James Gosling y Bill Joy, quienes

Más detalles

UNIDAD II Elementos de la programación orientada a objetos LSC. Natalia Rodríguez Castellón Estudio de un lenguaje de alto nivel Java es un lenguaje de programación de alto nivel, se comenzó a desarrollar

Más detalles

Características generales del lenguaje Java. María a Consuelo Franky

Características generales del lenguaje Java. María a Consuelo Franky Características generales del lenguaje Java María a Consuelo Franky 1 Temario ❶ Historia del desarrollo de Java ❷ Ventajas sobre otros lenguajes O.O. ❸ applets vs. aplicaciones ❹ Vistazo global a las características

Más detalles

Primera Escuela de la Red Temática SVO. Madrid, 27-28 Noviembre, 2006 JAVA BÁSICO. Raúl Gutiérrez Sánchez LAEFF - INTA raul@laeff.inta.

Primera Escuela de la Red Temática SVO. Madrid, 27-28 Noviembre, 2006 JAVA BÁSICO. Raúl Gutiérrez Sánchez LAEFF - INTA raul@laeff.inta. Primera Escuela de la Red Temática SVO. Madrid, 27-28 Noviembre, 2006 JAVA BÁSICO LAEFF - INTA raul@laeff.inta.es Qué es Java? Java es un lenguaje de programación orientado a objetos desarrollado por Sun

Más detalles

Los caracteres de Java pueden agruparse en letras, dígitos, espacios en blanco, caracteres especiales, signos de puntuación y secuencias de escape.

Los caracteres de Java pueden agruparse en letras, dígitos, espacios en blanco, caracteres especiales, signos de puntuación y secuencias de escape. CARACTERES EN JAVA Los caracteres de Java pueden agruparse en letras, dígitos, espacios en blanco, caracteres especiales, signos de puntuación y secuencias de escape. Letra, Dígitos y Otros Estos caracteres

Más detalles

Programación Avanzada para Sistemas de Telecomunicación. Objetos y clases. J.C. Cruellas. Objetos y clases

Programación Avanzada para Sistemas de Telecomunicación. Objetos y clases. J.C. Cruellas. Objetos y clases Programación Avanzada para Sistemas de Telecomunicación Objetos y clases Juan Carlos Cruellas cruellas@ac.upc.es Objetos y clases Concepto de objeto. Concepto de clase. Clases, objetos y programas. Clases

Más detalles

INICIACIÓN A LA PROGRAMACIÓN LENGUAJE JAVA

INICIACIÓN A LA PROGRAMACIÓN LENGUAJE JAVA INICIACIÓN A LA PROGRAMACIÓN LENGUAJE JAVA Introducción Qué es programar? Idear y ordenar las acciones necesarias para realizar un proyecto (R.A.E) En nuestro contexto: Resolver problemas, Automatizar

Más detalles

Curso: Programación con JAVA SE Estándar Edition.

Curso: Programación con JAVA SE Estándar Edition. Curso: Programación con JAVA SE Estándar Edition. Código: 1062 Familia Profesional: Programación. Acreditación: Formación reconocida a través de vías no formales Modalidad: Distancia Duración: 150 horas

Más detalles

VARIABLES, CONSTANTES Y EXPRESIONES ASIGNACIÓN. TIPOS ELEMENTALES. PRECEDENCIA DE LOS ESTRUCTURAS DE CONTROL. CONDICIONAL E

VARIABLES, CONSTANTES Y EXPRESIONES ASIGNACIÓN. TIPOS ELEMENTALES. PRECEDENCIA DE LOS ESTRUCTURAS DE CONTROL. CONDICIONAL E Java Java es un lenguaje de programación presentado en 1995 enfocado a: soportar los fundamentos de la programación orientada a objetos. generar código independiente de la arquitectura de la computadora

Más detalles

Técnico Superior en Programación con Java SE Standard Edition

Técnico Superior en Programación con Java SE Standard Edition Código: M087_04 Técnico Superior en Programación con Java SE Standard Edition Modalidad: Distancia Duración: 120 horas Objetivos: Este pack de materiales formativos proporcionará al alumnado la base que

Más detalles

Java y Eclipse. Lenguajes y Entornos de Programación Libre

Java y Eclipse. Lenguajes y Entornos de Programación Libre Java y Eclipse Lenguajes y Entornos de Programación Libre El lenguaje Java Un poco de historia: 1990: James Gosling, responsable de una empresa filial creada por Sun Microsystems, empieza a diseñar Java

Más detalles

Datos parciales. Datos Parciales. La Programación estructurada se concentra en las acciones que controlan el flujo de datos.

Datos parciales. Datos Parciales. La Programación estructurada se concentra en las acciones que controlan el flujo de datos. Unidad I Conceptos Básicos de la Programación Orientada a Objetos 1.1 Paradigma de la Programación Orientada a Objetos Paradigma. Según el Diccionario de la Real Academia de la Lengua Española, paradigma

Más detalles

CURSO INTERMEDIO DE PROGRAMACIÓN EN ANDROID

CURSO INTERMEDIO DE PROGRAMACIÓN EN ANDROID CURSO INTERMEDIO DE PROGRAMACIÓN EN ANDROID 1. Entorno de desarrollo y primera aplicación 1. El mundo Android 1.1 Android y las versiones 1.2 Personalizaciones del sistema operativo. 1.3 Dispositivos Android

Más detalles

CAPÍTULO IV BREVE DESCRIPCIÓN DE LA INFRAESTRUCTURA DE CÓMPUTO VISUAL BASIC 6.0 PARA WINDOWS

CAPÍTULO IV BREVE DESCRIPCIÓN DE LA INFRAESTRUCTURA DE CÓMPUTO VISUAL BASIC 6.0 PARA WINDOWS CAPÍTULO IV BREVE DESCRIPCIÓN DE LA INFRAESTRUCTURA DE CÓMPUTO VISUAL BASIC 6.0 PARA WINDOWS 4.1 Antecedentes históricos El lenguaje de programación BASIC (Beginner's All purpose Symbolic Instruction Code)

Más detalles

Módulo 1 El lenguaje Java

Módulo 1 El lenguaje Java Módulo 1 El lenguaje 1.1 Presentación de es un lenguaje de programación desarrollado por la empresa Sun Microsystems en 1991 como parte de un proyecto secreto de investigación llamado Green Proyect, con

Más detalles

Java en 3 horas. Ampliación de Sistemas Operativos. Rodrigo Santamaría

Java en 3 horas. Ampliación de Sistemas Operativos. Rodrigo Santamaría Java en 3 horas Ampliación de Sistemas Operativos Rodrigo Santamaría Generalidades Desarrollado por Sun Hereda mucha de la sintaxis de C (1972) Fuertemente tipado y orientado a objetos Aplicaciones compiladas

Más detalles

Ejercicios de evaluación de fundamentos de programación en Java

Ejercicios de evaluación de fundamentos de programación en Java Ejercicios de evaluación de fundamentos de programación en Java Jorge Martínez Ladrón de Guevara Editorial EME ISBN 978-84-96285-40-8 Contenido 1. Introducción a Java... 1 Test de evaluación...1 Ejercicios...4

Más detalles

Tema 3 Elementos básicos de programación

Tema 3 Elementos básicos de programación Representación de Datos y Aplicaciones Tema 3 Elementos básicos de programación Natividad Martínez Madrid nati@it.uc3m.es Objetivos del tema 3 Conocer la estructura básica de un programa Java Comprender

Más detalles

Conociendo el ambiente de programación de Java. M. en C. Erika Vilches

Conociendo el ambiente de programación de Java. M. en C. Erika Vilches Conociendo el ambiente de programación de Java M. en C. Erika Vilches La variable PATH Una vez que se ha aceptado la licencia del JDK y que se ha instalado satisfactoriamente y antes de poder utilizarlo,

Más detalles

Java Inicial (20 horas)

Java Inicial (20 horas) Java Inicial (20 horas) 1 Temario 1. Programación Orientada a Objetos 2. Introducción y Sintaxis Java 3. Sentencias Control Flujo 4. POO en Java 5. Relaciones entre Objetos 6. Polimorfismo, abstracción

Más detalles

JAVA PARA PRINCIPIANTES

JAVA PARA PRINCIPIANTES UN POCO DE HISTORIA Java fue creado en 1991 por James Gosling de Sun Microsystems inicialmente llamado Oak que significa roble, esto debido a la cantidad de arboles que rodeaban el sitio donde este trabajaba.

Más detalles

INDICE DEL CURSO APRENDER PROGRAMACIÓN JAVA DESDE CERO. PROGRAMACIÓN ORIENTADA A OBJETOS (CU00601B)

INDICE DEL CURSO APRENDER PROGRAMACIÓN JAVA DESDE CERO. PROGRAMACIÓN ORIENTADA A OBJETOS (CU00601B) APRENDERAPROGRAMAR.COM INDICE DEL CURSO APRENDER PROGRAMACIÓN JAVA DESDE CERO. PROGRAMACIÓN ORIENTADA A OBJETOS (CU00601B) Sección: Cursos Categoría: Curso Aprender programación Java desde cero Fecha revisión:

Más detalles

Programación Interactiva Introducción a Java. Escuela de Ingeniería de Sistemas y Computación Facultad de Ingeniería Universidad del Valle

Programación Interactiva Introducción a Java. Escuela de Ingeniería de Sistemas y Computación Facultad de Ingeniería Universidad del Valle Programación Interactiva Introducción a Java Escuela de Ingeniería de Sistemas y Computación Facultad de Ingeniería Universidad del Valle 1 Qué es Java? Java es un lenguaje de programación de propósito

Más detalles

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA UNED Centro Asociado de Cádiz RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA 1. OBJETOS Cualquier elemento del programa es un objeto. Un programa es un conjunto de objetos que se comunican entre sí

Más detalles

INTELIGENCIA ARTIFICIAL 2015 TALLER RÁPIDO DE PROGRAMACIÓN EN JAVA

INTELIGENCIA ARTIFICIAL 2015 TALLER RÁPIDO DE PROGRAMACIÓN EN JAVA INTELIGENCIA ARTIFICIAL 2015 TALLER RÁPIDO DE PROGRAMACIÓN EN JAVA Fuente: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html Por qué Java? TIOBE Index for March 2015 Fuente: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

Más detalles

Notas técnicas de JAVA Nro. 7 Tip Breve

Notas técnicas de JAVA Nro. 7 Tip Breve Notas técnicas de JAVA Nro. 7 Tip Breve (Lo nuevo, lo escondido, o simplemente lo de siempre pero bien explicado) Tema: JAVA Basics: Diferencias conceptuales entre JavaBeans y Enterprise JavaBeans (EJB)

Más detalles

Preliminares. Tipos de variables y Expresiones

Preliminares. Tipos de variables y Expresiones Preliminares. Tipos de variables y Expresiones Felipe Osorio Instituto de Estadística Pontificia Universidad Católica de Valparaíso Marzo 5, 2015 1 / 20 Preliminares Computadoras desarrollan tareas a un

Más detalles

PROGRAMACIÓN ORIENTADA A OBJETOS (L40629) Sabino Miranda-Jiménez

PROGRAMACIÓN ORIENTADA A OBJETOS (L40629) Sabino Miranda-Jiménez PROGRAMACIÓN ORIENTADA A OBJETOS (L40629) Sabino Miranda-Jiménez Paradigmas de programación 2 Paradigmas de programación Paradigma de programación estructurada Enfatiza la separación datos de un programa

Más detalles

Programación básica C++

Programación básica C++ Programación en Lenguaje C++ Programación básica C++ 6.3.1 Declaración de variables Introducción 6 Las variables sirven para identificar un determinado valor. Es importante tener en cuenta, que una variable

Más detalles

Carlos A. Fernández Java. Una Introducción. U.T.M. 1

Carlos A. Fernández Java. Una Introducción. U.T.M. 1 Carlos A. Fernández Java. Una Introducción. U.T.M. 1 Java: origen Diseñado dentro de Sun Microsystems por James Gosling El nombre original fue Oak Originalmente diseñado para usarse dentro de dispositivos

Más detalles

1 ELEMENTOS BASICOS DEL LENGUAJE

1 ELEMENTOS BASICOS DEL LENGUAJE 1 ELEMENTOS SICOS DEL LENGUJE Contenido: Variables su declaracion y asignacion Caracteres en java Secuencias de escape y comentarios Tipos de datos Operadores aritméticos, relacionales y lógicos Entrada

Más detalles

Seminario de Java. Contenido

Seminario de Java. Contenido Seminario de Java Programación Orientada a Objetos Curso 2006/2007 Contenido 1. Introducción 2. Primeros pasos con Java. El entorno Eclipse 3. La sintaxis del lenguaje Java 4. Clases y objetos 5. Cadenas

Más detalles

Diplomado Programming Java (JSE & JEE) with Oracle 10g

Diplomado Programming Java (JSE & JEE) with Oracle 10g Diplomado Programming Java (JSE & JEE) with Oracle 10g Descripción: Nuestro diplomado ofrece un entrenamiento INTEGRAL y el más completo si estás interesado en realmente aprender Java y aplicarlo en tecnologías

Más detalles

PRÓLOGO... XVII CAPÍTULO 1. FASES EN EL DESARROLLO DE UN PROGRAMA...

PRÓLOGO... XVII CAPÍTULO 1. FASES EN EL DESARROLLO DE UN PROGRAMA... CONTENIDO PRÓLOGO... XVII CAPÍTULO 1. FASES EN EL DESARROLLO DE UN PROGRAMA... 1 QUÉ ES UN PROGRAMA... 1 QUÉ ES Visual Basic.NET... 2 REALIZACIÓN DE UN PROGRAMA EN Visual Basic.NET... 3 Cómo crear un programa...

Más detalles

1 Estructura básica de un programa C++

1 Estructura básica de un programa C++ Elementos básicos de un lenguaje de alto nivel: C++ CONTENIDOS 1. Estructura básica de un programa C++. 2. Tipos de datos simples. 3. Constantes y variables en C++. Declaración. 4. Operadores y expresiones.

Más detalles

Sensor de Temperatura utilizando el Starter Kit Javelin Stamp. Realizado por: Bertha Palomeque A. Rodrigo Barzola J.

Sensor de Temperatura utilizando el Starter Kit Javelin Stamp. Realizado por: Bertha Palomeque A. Rodrigo Barzola J. Sensor de Temperatura utilizando el Starter Kit Javelin Stamp Realizado por: Bertha Palomeque A. Rodrigo Barzola J. INTRODUCCION DIFERENCIAS EJEMPLOS JAVA Orientado a Objetos Multiplataforma Programar

Más detalles

Introducción al lenguaje JAVA

Introducción al lenguaje JAVA Universidad Autónoma de Tlaxcala Introducción al lenguaje JAVA M.C.. José Juan Hernández ndez Mora Segunda Sesión 1. Arreglos 2. Matrices 3. Clases en Java 4. Clases de Usuario en Java 5. Objetos definidos

Más detalles

1 HILOS (THREADS) EN JAVA

1 HILOS (THREADS) EN JAVA 1 HILOS (THREADS) EN JAVA 1.1QUÉ ES UN THREAD La Máquina Virtual Java (JVM) es un sistema multihilo. Es decir, es capaz de ejecutar varios hilos de ejecución simultáneamente. La JVM gestiona todos los

Más detalles

2.1 La interfaz de JDBC para el programador de aplicaciones... 3. 2.2 La interfaz JDBC para los controladores JDBC... 5

2.1 La interfaz de JDBC para el programador de aplicaciones... 3. 2.2 La interfaz JDBC para los controladores JDBC... 5 $SpQGLFH& &RQWURODGRUHV-'%& $%'& ( )+*-,/.1032/2465/(7%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%98 8:%;*-32)@0 *A.>BCEDGFH%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%3%EI

Más detalles

INDICE Programación Introducción Capitulo 21 BASIC Capitulo 22. COBOL Capitulo 23 DELPHI Capitulo 24. FORTRAN Capitulo 25.

INDICE Programación Introducción Capitulo 21 BASIC Capitulo 22. COBOL Capitulo 23 DELPHI Capitulo 24. FORTRAN Capitulo 25. INDICE Programación Introducción 706 Capitulo 21 BASIC 711 Introducción 711 Sintaxis 713 Procedimientos y control de flujo 713 Tipos de datos 714 Disponibilidad y variantes del dialecto 714 Capitulo 22.

Más detalles

JSP Básico. Índice. 2 Traducción de los JSP a servlets...2. 6 Acciones...8. 1 Introducción a JSP... 2

JSP Básico. Índice. 2 Traducción de los JSP a servlets...2. 6 Acciones...8. 1 Introducción a JSP... 2 Índice 1 Introducción a JSP... 2 2 Traducción de los JSP a servlets...2 3 Elementos de JSP...3 4 Inserción de código en páginas JSP... 4 4.1 Expresiones...4 4.2 Scriptlets...4 4.3 Declaraciones... 4 4.4

Más detalles

Java Avanzado. Guía 1 7. Java Avanzado Facultad de Ingeniería. Escuela de computación.

Java Avanzado. Guía 1 7. Java Avanzado Facultad de Ingeniería. Escuela de computación. Java Avanzado. Guía 1 7 Java Avanzado Facultad de Ingeniería. Escuela de computación. Java Avanzado. Guía 1 3 Introducción Este manual ha sido elaborado para orientar al estudiante de Java Avanzado en

Más detalles

PL/SQL. Con PL/SQL vamos a poder programar las unidades de programa de la base de datos Oracle:

PL/SQL. Con PL/SQL vamos a poder programar las unidades de programa de la base de datos Oracle: PL/SQL (Procedural Language/Structured Query Language) PL/SQL es el lenguaje de programación que proporciona Oracle para extender el SQL estándar con otro tipo de instrucciones y elementos propios de los

Más detalles

Programación en Java. Hola Jesus. Primera clase del curso de Java

Programación en Java. Hola Jesus. Primera clase del curso de Java Programación en Java Un programa o aplicación Java se compone de un conjunto de clases que contienen variables de diversos tipos utilizadas para almacenar datos, y métodos que implementan código capaz

Más detalles

Formación a distancia de EXPERTO EN PROGRAMACIÓN EN JAVA

Formación a distancia de EXPERTO EN PROGRAMACIÓN EN JAVA Instituto de Formación Profesional CBTech Estudie desde su hogar y obtenga un certificado universitario Formación a distancia de EXPERTO EN PROGRAMACIÓN EN JAVA 1 Introducción al Lenguaje JAVA Integrado

Más detalles

El Lenguaje de Programación Java. Laboratorio 01

El Lenguaje de Programación Java. Laboratorio 01 Laboratorio 01 I. E M P E Z A R A T R A B A J A R C O N J A V A El Software Básico de Desarrollo (SDK) de Sun Microsystem Para escribir en Java hacen falta los programas que realizan el precompilado y

Más detalles