Buscar
Social
Ofertas laborales ES
sábado
sep012001

Novedades del JSDK 1.4


Novedades de J2SE 1.4




Como seguramente todos sabeis ya, Sun tiene casi a punto la versión 1.4 de Java 2 Standard Edition (J2SE), ya podeis trabajar con la versión beta si quereis, por ejemplo. Sun se puso una "obligación" de sacar cada 18 meses (para dar tiempo a que la gente asimile las novedades) una "gran versión" (las pares, 1.2, 1.4, ...), que introducen grandes cambios en el lenguaje, y dedicar el tiempo intermedio a las mejoras puntuales y resolución de problemas, con nuevas versiones (1.2.2, 1.3, ...) que mejoren algún aspectos concretos deficientes. Por si no lo sabeis, los 18 meses desde la salida de Java2, Java 1.2 se han cumplido.



He dicho "Sun tiene a punto" pero también habría podido decir "la comunidad Java tiene a punto". Sun sabe que no tiene el poder de Microsoft para "marcar estandares", asi que se tiene que arropar de todas las empresas del sector posibles (lease IBM, Compaq, Epson, etc), y de todos los desarolladores del mundo, sabe que solo implicando el mayor número posible de partes puede "hacer algo". Este era el objetivo tras la aparición de la Java Developer Connection (JDC) y el Java Community Proccess (JCP).



La JDC no es más que una especie de agrupación (de libre acceso) para todas las personas o empresas interesadas en el desarrollo con y de Java. Alli se liberan las primeras versiones de las APIs para probarlas, se presentan articulos y tutoriales sobre Java, etc. Más o menos, todo desarollador de Java debería estar apuntado, sobre todo por es gratis. El JPC es, mas o menos, para parte "seria" de la JDC. En el JCP, dividio en varios grupos de expertos enfocados a distintos temas, se debaten cosas sobre la estructura y definición de Java, sus problemas o limitaciones, sus fallos, sus deficiencias, donde ahora mismo se decide realmente la evolución de Java, aunque para la mayoría de nosotros, todo esto de influir en el desarrollo de Java no pasará, en el mejor de los casos, de enviar un correo electrónico del estilo "IŽve found a bug in..." o "Why not ... in next releases?". Como inciativa no esta mal, pero todavía Sun deberia hacer algo más, lo más deseado sería eliminar algunas de las restricciones más comprometidas de su licencia, o incluso declarar Java opensource. Quizás algún día, personalmente me parece una buena idea para su supervivencia.



En fin, que me lio. Lo que quería decir era que por primera vez se puede decir que esta nueva versión de Java es fruto de "la comunidad java" y no solo de Sun. Desde un principio se ha escuchado a todas las partes, a los desarrolladores particulares y a las grandes empresas, con el fín de delimitar los problemas "más urgentes" y darles una solución. Se podría decir que el TOP-5 de las peticiones de los usuarios ha resultado ser el siguiente:




  • Java se usa más en aplicaciones gráficas en el cliente de lo que se pueda pensar. Todos sabemos que aunque las interfaces de usuario de Java son realmente ricas en funcionalidad, su rendimiento no es el mejor del mundo.
  • Java debe mejorar en los grandes servidores (mainframes y supercomputadores), puesto que la multiplataforma es un aspecto muy a tener en cuenta para esos servidores.
  • A veces se ha sacrificado la calidad por la cantidadvo la disponibilidad, lo cual no debería suceder.
  • Se debe mejorar el rendimiento y la escabilidad en general.
  • Se deben mantener versiones de calidad tanto en el cliente como en el servidor. Da la impresión de que el los últimos tiempos Sun se ha dedicado "únicamente" a fortalecer el servidor.



Estos cinco factores son los que han regido las decisiones a tomar a la hora de preparar esta versión, los que decidían que era urgente y que podía esperar. Finalmente, lo que se ha decidido era que los objetivos concretos a mejorar eran el nivel de confianza, la disponibilidad, los servicios, la escalabilidad, el rendimiento y el despliegue de las aplicaciones Java. Esto suena simplemente a "queremos mejorar todo lo posible", es cierto, pero a fin de cuentas esi siempre es asi, hay que mejorar todo lo que se pueda.



Pero basta ya de palabreria y pasemos a las aportaciones reales que hacer Java 1.4 al lenguaje y su API. Puede parecer que no son demasiadas, pero eso solo es debido a que la mayoria de los cambios se han centrado en mejorar el rendimiento de lo existente más que en incluir grandes novedades.






La maquina virtual.



Entre los cambios más destacados en la máquina virtual de Java esta el soporte para arquitecturas de 64 bits, en un principio SPARC y para Intel Itanium esta en camino.



La implementación del recolector de basura también ha mejorado en gran medida, sobre todo en los grandes servidores con grandes cantidades de memoria, y se dice que ahora las mejoras que aportaba HotSpot al rendimiento de la maquina virtual estan incluidas en la JVM estandar, sin que halla que descargarse un nuevo paquete. Se espera con ello que el rendimiento intrínseco de la maqina virtual crezca solo.






Java gráfico.



Como se ha mencionado, Java es mas empleado en aplicaciones cliente de lo que se podría esperar, y es que el "caramelo" de la multiplataforma es muy apetecible. Es por ello que Java 1.4 tenía que tener una parte muy enfocada a la mejora de su rendimiento, y un factor muy importante es el referente a la presentación gráfica al usuario.



Se han producido muchos cambios en todas las clases gráficas, Java2D, AWT y Swing. No os asusteis que no teneis que aprenderos millones de nuevos métodos para cada clase, la mayoría de los cambios son internos, en su implementación enfocados en mejorar el rendimiento.



Java2D se ha reescrito en gran parte y se ha optimizado todo lo posible para el trabajo real, dejandose de lado ciertos aspectos más enfocados "a la galería" que a la realidad. Se hablan de cifras de mejora del rendimiento impresionantes, de un 40% respecto a la versión 1.3 y de un 100% respecto a la 1.2. Es posible que los usuarios de Linux no se acaben de creer el cambio, ya que toda lacomunicación con el servidor X ha sido reescrita y mejorada de forma más que evidente, se habla de multiplicar por 5 o incluso más el rendimiento. Por alguna razón, la maquina vistual de Java se comunicaba con el servidor X por medio de imagenes de bits (bitmaps), ahora lo hace directamente por medio de comandos X.



La impresión en Java es en realidad un trabajo gráfico, hay que dibujar la página antes de imprimirla (podeis ver el capitulo refernte a impresion en el proyecto de "traducción de Swing" de esta página). Este API también se hamejorado de forma ostensible, obteniendose ahora no solo un mejor rendimiento, si no también un mayor cotnrol sobre la página y todo el proceso de impresión.



También se ha reescrito enteramente el sistema de control del foco en AWT, una de las partes que nunca ha trabajado realmente bien en Java. La reescritura ha sido completa, así que a la espera de encontrar nuevos bugs esta "impoluta".



En lo referente a Swing, a la par de las mejoras de rendimiento, el problema de look&feel presente en los JFileChooser en Windows se considera resuelto, y se han añadido nuevos componentes gráficos para enriquecer nuestras interfaces de usuario, entre ellos JSpinner, JProgressBar, JTAbbedPanel, etc.



Otras mejoras que se podrián definir como dentro de "java gráfico" son las mejoras en el tratamiento de ficheros con formato gráfico, un mejor soporte de las fuentes True-Type, soporte para las "imprescindibles" ruedas del ratón.






Conectividad.



Uno de los aspectos centrales de la conectividad en los últimos tiempos es XML, y por supuesto Java tiene que prestar la atención que esta "fiebre" requiere convirtiendose en un aspecto de los más "mimados" por Java. La nueva J2SE tiene soporte completo para las APIs SAX 1.0 y 2.0, para DOM 1.0 y 2.0, para XSLT, todo incluido en el núcleo por medio de JAXP. La idea era incluir nuevas APis, como podrían ser JATO (http://jato.org) y JDOM (http://www.jdom.org), pero se las considera aún inmaduras, por lo que se aplaza su inclusión.



Después de las guerras entre Sun y Microsoft y de que el desarrollo de aplpet halla caido a mínimos debido a los problemas entre navegadores, el nuevo Java Plugin ofrece también númerosas mejoras. Ofreciendo ahora mucha mayor interacion con el browser (se puede acceder a su DOM al estilo de Javascript) y con el usuario (mostando un indicador del proceso de carga del applet en cada momento).



También se añade soporte para IPv6, se autriza por primera vez la distribución fuera de EEUU del API de seguridad (anteriormente prohibida por las leyes estadounidenses al considerarse poco menos que secreto de estado), la inclusión de servicios de DNS dentro del JNDI, un nuevo JDBC 3.0 totalmente integrado en el núcleo de Java, un practicamente completo soporte de CORBA (incluidos POA -portable object adaptor- y CosNaming para implementaciones persistentes), y soporte para el Java Web Start también en el núcleo del lenguaje.






Otras novedades.




Mejorar lo existente siempre es bueno, pero por supuesto al lenguaje le faltaban cosas. Algunas de las que se han incluido en esta nueva versión son:




  • Soporte total a Unicode 3.0, con nuevos juegos de caracteres, como por ejemplo el Hindi, si alguno sabe hablarlo ;-).
  • Un debugger avanzado (JDPA) que permite, entre otras cosas, el cambio de clases en tiempo de ejecución.
  • I/O enfocada a mejorar el rendimiento del buffer, a hacerlo más escalable, y a las tan deseadas expresiones regulares.
  • API de registro de las operaciones del sistema (log).
  • Inclusión de asertos simples en el núcleo como cambio en el lenguaje.
  • Soporte a las excepciones en cadena, lo que hará más sencillo llegar a la fuente de las mismas.






Conclusión.




Como ya se ha dicho, esta nuevo J2SE 1.4 se ha centrado más en la mejora del rendimiento que en las grandes novedades. El rendimiento es uno de los factores más importantes para la aceptación de cualquier cosa, por lo que si Java mejora en ese aspecto, junto con lo ya conseguido en los ámbitos de servidores y multiplataforma, podremos afirmar realmente que Java ha llegado a la madurez.



Habra que conformarse por ahora con las versiones beta (se espera aún otra antes de la versión final) hasta la llegada a finales de verano o pincipios de otoño de la versión 1.4 para saber si realmente el rendimiento mejora hasta el límite del que se habla.



PS: si alguién ha oido hablar de "un Java llamado Merlin", que sepa que es el nombre de la versión 1.4 en tiempo de desarrollo. La 1.5 se llama "Tiger". Ya sabemos como funcionan estos americanos ;-).
















Alberto Molpeceres es ahora mismo desarrollador de aplicaciones en ámbito cliente/servidor para la empresa T-Systems - debis Systemhaus en Munich (Alemania).

Cuando no está trabajando o "metiendo caña" al resto de los integrantes
de javaHispano intenta pasear con su novia, buscar la desaparecida
lógica del idioma alemán o intenta olvidar la pesadilla que es buscar piso en Munich.



Para cualquier duda o tirón de orejas, e-mail a:
al_ARROBA_javahispano.com




sábado
sep012001

Comunicacrión Applet-Servlet y tunneling


Comunicacion Applet-Servlet y tunneling





Todos sabemos como hacer una aplicación web en los que el usuario manda un formulario HTML y el servidor lo procesa por medio de Servlets o páginas JSP (sí!, ya sé que son "lo mismo"), pero quizás alguno ya haya tenido el problema de que eso no es siempre suficiente, no es lo suficentemente interactivo para algunas aplicaciones que requieren más dinamismo y funcionalidad en el lado del cliente, como por ejemplo un chat u otro tipo de aplicaciones, digámoslo así, distribuídas.



Si hablamos de aplicaciones distribuídas con clientes "ricos" (en el sentido de la funcionalidad) todos pensamos en RMI o quizás EJB, en comunicar una aplicación Java con el servidor por medio del API que ofrece Java para proceso distribuído, que aunque sea disfrazado de EJB (con mucha funcionalidad añadida), es RMI. Pero, ¿qué ocurre? Que ningún administrador de una red medianamente grande te va a abrir un puerto en su cortafuegos. La gente está demasiado histérica (seguramente con razón) con la seguridad, así que en algunas ocasiones esta solución no es posible.



Así que, si necesitamos una interface rica en el lado del cliente y usar el único puerto que los administradores de red suelen dejar "tranquilo" (el de HTTP, normalmente el 80), ¿qué nos queda? Pues sí, APPLETS!. Son "famosos" los applets que ejercen la labor de "menú" de la página web, sin ir más lejos, en un tiempo muy muy lejano, antes de ser la web preciosa que es ahora, javaHispano tuvo uno. Así que ya sabemos que un applet se puede comunicar con un servidor web para hacer peticiones de páginas HTML. Y si podemos mostrar páginas web.... ¿por qué no hacer peticiones a un servlet?.
Además, usando un applet evitamos una de las limitaciones de un formulario HTML, que es que solo pueden hacer peticiones a un servidor y a un recurso Web. Desde un applet podremos hacer, con los mismos datos, peticiones a varios servlets distintos de nuestro servidor, que a su vez se podrán comunicar con otros servidores o servlets, de forma que podemos, con una sola peticion, enviar los datos a cientos de sitios



El applet puede ser todo lo rico o pobre que necesitemos, manejar toda la información que necesitemos, y realizar todos los cálculos que queramos, exactamente igual que cualquier aplicación Java. Lo único que tenemos que aprender es cómo hacer que el applet transmita las peticiones al servidor, y por supuesto cómo hacer que recoja las respuestas.



Antes de empezar, un aviso. Como es lógico, todas estas operaciones que tratan con conexiones son susceptibles de lanzar excepciones. La mayoria de las líneas de código mostradas aquí deberían ir encerradas en una sentencia try/catch, pero las he omitido todas para no hacerlo más largo de lo necesario. En realidad vosotros tendréis que poner unas cuantas de ellas :-).



Aunque se explican mínimante algunos conceptos del protocolo HTTP, como la diferencia de envio de la petición por medio del método GET o POST, se presuponen ciertos conocimientos del protocolo HTTP, conceptos como cabeceras, tipos de documentos, cookies y demás. Aqui no serán explicados, como mucho se indicará como usarlas en Java, y solo del lado del applet, del lado cliente. Dejo para otro/s artículo/s y quizás otra/s persona/s (se aceptan volutarios) las explicación de dichos conceptos y un uso más avanzado y extensivo de las posibilidades que tienen los servlets, ese no es el objetivo de este artículo.





Peticiones a un servlet por medio de GET




Como sabéis (o deberíais saber si estais metidos en estas cosas), en una petición HTTP por el método GET, la información viaja en la URL, es decir, en la dirección del navegador, algo así como:



http://www.javahispano.com/servlet/MiServlet?param1=valor1¶m2=valor2


Como podéis ver, después de la direccion del recurso, en nuestro caso el servlet "peticion" (http://www.javahispano.com/servlet/MiServlet), ponemos el símbolo "?" y después todos los parametros de la petición en pares "nombre=valor" separados por el símbolo "&".



Así que si queremos hacer una petición al servidor, a un servlet desde nuestro Applet, simplemente tenemos que escribir:



try
{
/*
baseURL es la dirección del servlet
ejemplo: http://www.javahispano.com/servlet/MiServlet.
Y parametros son los datos adosados,
ejemplo: param1=valor1¶m2=valor2
*/
URL direccion = new URL (baseURL + "?" + parametros);
}
catch (MalformedURLException mue)
{
. . .
}


Esta forma de actuar nos devuelve un "fichero HTML" que podríamos mostrar con el método showDocument del applet:



miApplet.getAppletContext().showDocument(direccion);


El único problema que se nos puede plantear es el de codificar los parámetros. Al utilizar el protocolo HTTP tenemos que asegurarnos de que el servidor web nos entienda, por lo que, uno por uno, hay que codificar los parámetros de la petición para eliminar los espacios en blanco y los caracteres no alfanuméricos, no aceptados por el protocolo HTTP. Lo haremos por medio del método estático "encode" de la clase URLEncoder:



String parametros = "param1" + "=" + URLEncoder (valor1) +
"&" + "param2" + "=" + URLEncoder (valor2);


Hay que tener cuidado, ya que no vale hacer simplemente



String parametros = URLEncoder ("param1=" + valor1 + "¶m2=" + valor2);


Es tentador, pero no funcionará, porque entonces nos codificará también los símbolos "=" y "&", así que ya no tendremos la URL que nosotros queremos. Un poco más de trabajo, pero no es grave.





Tunneling



Pero eso de mostrar la página en el navegador muchas veces no es lo que buscamos. Puede ser, pero muchas veces no. Es por eso que tenemos que buscar una manera de recoger esa información que nos envía el servidor y procesarla según nuestras necesidades. A fin de cuentas, ya hemos dicho que una de las razones para usar esta comunicación applet/servlet es la de conseguir una comunicación "avanzada" usando el protocolo HTTP y que no produzca pánico en un administrador del sistema por tener que abrir un puerto en el cortafuegos.



Así que lo que tenemos que utilizar es uno de eso terminos de moda, el "tunneling", que no tengo ni idea de cómo traducir al castellano, aunque sea una palabra relativamente de moda. Es lo malo de vivir "en otro idioma", acabas olvidando ciertas palabras "raras", y no sabes cómo traducir cosas como "usability", porque me han dicho que "usabilidad" no existe :-(, aunque reconozco que suena muy mal. En fin, disculpad el desvarío, solo es para pedir perdón si a veces "le doy patadas al diccionario". El caso es que el llamado "tunneling", quizás traducible como "hacer el embudo" o algo parecido, significa transmitir información que sigue un protocolo por medio de otro. En nuestro caso se llama "HTTP tunneling" ya que usamos el protocolo HTTP para transmitir la información binaria.



Principalmente, lo que tenemos que hacer es capturar el stream o flujo por el que el servidor nos envía los datos y procesarlos de la manera adecuada. El proceso en sí no es difícil, lo que más "destreza" requiere es el procesado de la información, como siempre. El proceso a seguir es el siguiente:



/*
Primero creamos la URL para la conexión.
Tiene sentido construir la dirección de esta forma
tan "complicada" puesto que el applet solo puede
establecer conexiones con su servidor, y así, al
construir la dirección dinámicamente, no tenemos
que retocar el código al irnos a otro servidor.
En todo caso, lo siguiente sería válido:
URL direccion = new URL ("http://www.javahispano.com/servlet/MiServlet");
*/
URL pagina = miApplet.getCodeBase();
String protocolo = pagina.getProtocol();
String servidor = pagina.getHost();
int puerto = pagina.getPort();
int servlet = "/servlet/MiServlet";
URL direccion = new URL (protocolo, servidor, puerto, servlet);

/*
Después creamos una URLConnection con la dirección dada
*/
URLConnection conexion = direccion.openConnection();

/*
Lo siguiente es decirle al navegador que no use su
cache para esta conexión, porque si lo hace vamos a
tener un página estatica, y para eso no nos metemos
en estos líos ;-).
*/
conexion.setUseCaches (false);

/*
Ahora añadimos todas las cabeceras de HTTP que necesitemos,
Cookies, contenido, autorizacion, etc. con el método:
conexion.setRequestProperty ("cabecera", "valor");
Consultar la especificación de HTTP para más detalles

Por ejemplo, para decir que preferentemente hablamos español:
*/
conexion.setRequestProperty ("Accept-Language", "es");
. . .

/*
Despues capturamos el flujo por el que nos llega la respuesta
de la forma habitual, igual que hacemos con ficheros, sockets, etc.
*/
InputStreamReader stream = new InputStreamReader (conexion.getInputStream());
BufferedReader entrada = new BufferedReader(stream);

/*
Procesamos la información de la forma adecuada, según se
trate de datos ASCII o binarios.
Por ejemplo, si tratamos con datos ASCII leemos línea a línea:
*/
String linea;
while ((linea = entrada.readLine())!=null)
{
// procesamiento de la linea
. . .
}

/*
Y finalmente cerramos la conexión.
*/
entrada.close();


¿Sencillo?. Al menos no complicado, no más que cualquier operación de entrada y salida con ficheros, ¿no?.






Más difícil todavía: transmitiendo objetos



Pero si hemos abierto una conexión con el servidor, ¿por qué limitarnos a un sencillo texto ASCII o datos binarios?, ¿qué nos puede impedir enviar objetos por medio de la serialización?. Realmente nada, solo hay que crear un ObjectInputStream en lugar de uno normal y luego hacer el "Cast" de los objetos:



ObjectInputStream entrada = new ObjectInputStream (conexion.getInputStream());
MiClaseSerializable mcs = (MiClaseSerializable) entrada.readObject();
. . .


Siento deciros que una explicación sobre la serialización se escapa del objetivo de este artículo, pero como siempre lo dejo pendiente para que alguien se anime a escribirlo y publicarlo aquí.



Está claro que para poder recibir objetos primero los tiene que enviar el servidor de la forma adecuada. Este envío hay que indicarlo en las cabeceras de la respuesta (no olvidar que seguimos trabajando sobre HTTP). En el método de servicio del Servlet (doPost o doGet, según sea nuestra petición por medio de GET o POST), teniendo la respuesta en el objeto "response" de la clase HTTPServletResponse que esos métodos reciben como parámetro, deberíamos hacer:



/*
Primero especificamos el tipo del contenido de la respuesta
*/
response.setContentType ("application/x-java-serialized-object");

/*
Abrimos el flujo de salida para enviar objetos
*/
ObjectOutputStream salida = new ObjectOutputStream(reponse.getOutputStream());

/*
Escribimos los objetos que queramos enviar
*/
salida.writeObject (mcs);
. . .

/*
Y finalmente, nos aseguramos de que envíe el contenido
forzando el vaciado del buffer.
No hace falta cerrar el flujo de salida. Eso es algo de lo que
se encargará el contenedor de servlets, de la misma forma que
se encarga de muchas cosas.
*/
salida.flush();





Peticiones a un servlet por medio de POST



Pero nos hemos dejado lo mejor para el final, como siempre. Hemos visto cómo enviar datos sencillos, cómo recibir paginas web, cómo procesar los resultados que nos manda el servidor (ya sean ASCII, datos binarios u objetos), pero nos falta lo mejor, la "comunicación total", es decir, cómo enviar objetos al servidor. Si alguno sabe algo de HTML o de Servlets, notará que hasta ahora no hemos hablado de la otra forma, el otro método, de enviar datos de un formulario al servidor, el método POST. Ahora llega.



Como hemos dicho antes, usndo el método GET se envían los parametros de la petición dentro de la direccion URL. En el método POST no es así, y seremos nosotros los que tengamos que abrir una conexión con el servidor para enviar los datos. En el método POST la información de nuestra petición viaja en un flujo de datos toda junta (datos de la cabecera, cookies, parametros, etc), y será el servidor quién tenga que procesarla y separarla en partes. No es preocupeis que lo suele hacer muy bien.



Antes de que se me olvide, tengo que decir que si utilizamos el método POST, nuestro applet no podrá mostrar la página web de la respuesta (si ése fuera el caso) como hicimos en el primer ejemplo. No podemos asociar los datos necesarios del método POST con el constructor de una URL para recibir la respuesta, así que será obligatorio que procesemos la respuesta nosotros mismos. Pero como hemos dicho, eso no deberia ser un problema, puesto que si usamos applets y servlets, normalmente será porque queremos hacer algo más que mostrar páginas HTML.



El proceso es algo más complicado que en el caso de GET, pero también las posibilidades son mayores, así que no es cuestión de quejarse por tener un poco de trabajo más. Los pasos que son iguales a los seguidos al explicar el Tunneling no los repetiré, los tenéis unas líneas más arriba, así que no será problema. Lo siento pero soy un poco vago ;-).



/*
Primero construímos la dirección URL,
despues abrimos la conexión URLConnection,
y le decimos al navegador que no use cache
*/
. . .

/*
Ahora hay que informar al navegador de que queremos
enviar más información, no simplemente recibirla
*/
conexion.setDoOutput(true);

/*
El siguiente paso es crear un flujo de bytes que será
lo que se envíe al servidor. No tengáis miedo, no tenemos que
escribir byte a byte!! ;-).
Esto es necesario porque al enviar los datos hay que indicar
el tamaño exacto, y es mejor contarlo así que manualmente.
El parámetro (512) es el tamaño inicial del buffer. INICIAL, si
hace falta crecerá.
*/
ByteArrayOutputStream bs = new ByteArrayOutputStream(512);

/*
Para no tener que escribir byte a byte, asociamos a ese
flujo de bytes uno de un nivel superior. Según necesitemos
enviar objetos o texto ASCII será ObjectOutputStream o
PrintWriter, o el que queramos.
Despues escribimos los datos necesarios, y forzamos el vaciado
del buffer.
*/
ObjectOutputStream salida = new ObjectOutputStream (bs, true);
salida.writeObject (miClaseSerializable);
. . .
salida.flush();

/*
El siguiente paso es el de ajustar las cabeceras de la petición
HTTP. Como hemos explicado al principio, podemos especificar
todas las que necesitemos, pero en este caso, al menos serán
las relativas al envio de información, es decir, el tamaño y el
tipo de la información enviada.
*/
conexion.setRequestProperty ("Content-Length", String.valueOf (bs.size()));
conexion.setRequestProperty ("Content-Type", "application/x-java-serialized-object");
. . .

/*
Ahora enviamos realmente la información que hemos guardado
en el ByteArrayOutputStream a modo de buffer
*/
bs.writeTo (conexion.getOutputStream());

/*
Ya solo nos queda abrir un flujo de entrada para recibir
la respuesta del servidor. Exactamente como hemos visto
antes, asi que no lo repito.
*/
. . .





Conclusión.



Como habéis podido comprobar, la comunicación entre applet y servlet y el tunneling no son difíciles, al menos no más que cualquier conexión de I/O en Java. Supongo que muchos os habréis quejado de los difícil que es leer un fichero en Java, pero seguro que os habrá gustado saber que el mismo proceso es el que se sigue para realizar conexiones a través de la red, por lo que ya podéis hacer dos cosas por el precio de una ;-).
















Alberto Molpeceres es ahora mismo desarrollador de aplicaciones en ámbito cliente/servidor para la empresa T-Systems - debis Systemhaus en Munich (Alemania).

Cuando no está trabajando o "metiendo caña" al resto de los integrantes
de javaHispano intenta pasear con su novia, buscar la desaparecida
lógica del idioma alemán o intenta olvidar la pesadilla que es buscar piso en Munich.



Para cualquier duda o tirón de orejas, e-mail a:
al_ARROBA_javahispano.org




sábado
sep012001

Uso de paquetes en Java


Paquetes en Java


Fecha de creación: 3.9.2001

Revisión 1.1 (27.10.2001)

Emili Miedes de Elías
emiedes@iti.upv.es

Copyright (c) 2002, Emili Miedes de Elías. Este documento puede ser distribuido solo bajo los términos y condiciones de la licencia de Documentación de javaHispano v1.0 o posterior (la última versión se encuentra en /licencias/).



Introducción



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 paquetes. 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 al principio del fichero fuente.



Por ejemplo:


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, o bien éste o bien otro, para mantener mínimamente organizadas nuestras 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.


Uso de otros paquetes



Para usar clases de otros paquetes, no hay más que utilizar sentencias import, antes de la declaración de la clase.



Por ejemplo:


import java.lang.*;
import java.util.Vector;
import java.rmi.server.*;




Con el primer import le estamos indicando al compilador que vamos a importar 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 incluyamos una línea como la de arriba).
Con el segundo import, indicamos que sólo queremos importar la clase Vector del subpaquete java.util.
Con el tercer import, importamos todas las clases del paquete java.rmi.server.



Es posible utilizar una clase sin que esté incluída en un import, siempre y cuando utilicemos 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 anterior línea se puede reducir a


Vector vector = new Vector(100, 10);



Estructura jerárquica de directorios



Los ficheros .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 éste, debe haber un subdirectorio llamado prueba2, en el que se sitúan las clases del paquete prueba.prueba2.
Así, cada (sub)paquete se corresponde con un (sub)directorio, y viceversa.



No existen restricciones en cuanto a dónde 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, podemos situar el directorio prueba en el directorio raíz de nuestro á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 éste 'primer nivel' donde están situadas 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\prueba2.


Ejecución de clases



A la hora de ejecutar una aplicación Java, hay que tener en cuenta algunas cosas. En primer lugar, hay que indicar el nombre completo de la clase a ejecutar.



Por ejemplo, si tenemos 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 tal cual, 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 estamos 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




Puede ser que en nuestro árbol de directorios, tengamos todas las clases juntas a partir de un único directorio codebase, o que las tengamos separadas en varios. Puede ser que tengamos 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 de nuestro 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 podremos 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 la orden


java com.ibm.manager.Manager



pueda ser ejecutada desde cualquier directorio.
(No es posible suprimir com, com.ibm o com.ibm.manager, puesto que forman parte del nombre completo de la clase).



Conclusión



El uso de paquetes en Java nos proporciona ventajas en cuanto a encapsulación, reusabilidad, independencia de espacios de nombres, etc. Únicamente se exige respetar la estructura jerárquica de los directorios, tanto para almacenar los ficheros .class como a la hora de hacer las invocaciones a las aplicaciones.



Recursos

Acerca del autor

Emili Miedes de Elías

Emili trabaja en el Instituto Tecnológico de Informática (Universidad Politécnica de Valencia) desarrollando en Java. En sus ratos libres, colabora con javaHispano y desarrolla software opensource (buscar por emiedes en SourceForge).

sábado
sep012001

Conversrión de tipos en java


Conversión de tipos en Java.


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. Es
muy probable que muchos de vosotros no le veáis a ésto mucha
ciencia o mucha dificultad pero también es verdad que muchos hacemos
la conversión automáticamente sin un cierto rigor que nos
respalde y cuando el compilador de Java nos da error seguimos intentándolo
por el método del "ensayo y error" ;-).

Pues para aquellos que aun no lo tienen claro o simplemente para tenerlo
todo recogido en este documento y no tener que ir a desempolvar aquel viejo
manual o libro con el que empezastes a hacer tus primeros pinitos en Java
aquí va este artículo. Además, esta es la sección
de los que empezamos en ésto del Java y la idea es allanar el terreno
en lo posible a todo aquél que comienza y afianzar ciertos conceptos
antes de pasar a cosas mayores.

Veamos a continuación, cómo hace la conversión
de tipos Java:



Conversión de tipos automática.


Si al hacer la conversión de un tipo a otro se dan las 2 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 máquina virtual de Java tiene toda la información
necesaria para realizar la coversión sin necesidad de "ayuda" externa.
A ésto se le conoce como conversión implícita
y cuando hablamos 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 no 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 el siguiente apartado), el compilador nos
da un error diciéndonos algo así como "la variable tal necesita
un cast explícito". Pero tranquilo, no es que no podamos realizar
tal conversión sino que la JVM necesita que le demos una información
adicional: el tipo al que vamos a convertir la variable fuente. Pues bien,
a esta conversión explícita es a lo que se le llama
hacer un casting de tipos. Cuando se trata de tipos numéricos
a este tipo de conversión se le llama narrowing compresion
que se podría traducir como "conversión por estrechamiento"
dado que estás "recortando" la variable para poderla meter 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 queremos hacer la conversión
explícita del valor asociado.

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 perdemos información. En el ejemplo verás 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 a mi modo de ver uno de los puntos donde ésto
de la conversión nos puede dar la lata si no lo sabemos pero que
una vez sabido es una de las muchas ventajas que Java tiene frente a otros
lenguajes. El caso es que cuando la máquina virtual de Java 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
ésto también puede llevar al error de que hagamos una conversión
de tipos incompatibles y, al no hacer el casting correspondiente, la compilación
cascará. Mejor que explicarlo con letra lo vemos en el siguiente
ejemplo y verás que sencillo es recordarlo para no volver a meter
la pata.

Caso 1:

byte a = 40;
byte b = 50;
int c = a * b;//el compilador no dará ningún error.

Caso 2:
byte a = 25;
a = a*2; //ERROR: Explicit cast needed to convert int to byte.

Ahora te preguntarás, ¿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 meterlo en un tipo byte tendremos que hacer una conversión
explícita. En el caso 1 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 evaluamos
expresiones aquí van otros detalles más que hay que 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, recuerda que cuando un método nos devuelve Object,
dado que esta clase es la super-clase de todas, entonces podremos y tenemos
que hacer un casting explícito al tipo de la variable que nos interesa.
Por ejemplo, si tenemos un vector en el que vamos introduciendo elementos
del tipo MiClase, para poderlos recuperar habría que hacer:


Miclase var = (MiClase) vector.get(index);
//get devuelve Object y hacemos el cast a MiClase para que los tipos sean compatibles.



El ejemplo.


Bueno, tampoco es que sea un ejemplo, simplemente son una serie de casos
prácticos para concluir este artículo.

class ConversionTipos {
public static void main(String args[]) {
byte b;
int i = 257;
double d = 323.142;
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 o short 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);
}
}















Leo Suarez es un Ingeniero Superior de Telecomunicaciones por la
Universidad de Las Palmas de Gran Canaria con todas sus letras, que no
son pocas, y trabaja como tal en Santa Cruz de Tenerife.
Cuando no
está saltando de isla en isla o escribiendo para javaHispano aprovecha
para disfrutar con la novia y los amigos del estupendo clima de las islas afortunadas.

Para cualquier duda o tirón de orejas, e-mail a:
leo_ARROBA_javahispano.com




sábado
sep012001

Diario de un principiante (Capítulo 3)


Métodos y Clases en Java: Parte I.


Tras haber visto cómo empezar a programar en Java ahora vamos a
profundizar en los conceptos propios de la POO en Java.Y, parece lógico
empezar por la unidad básica de programación en Java, las
clases
. Cualquier concepto a implementar en Java debe estar encapsulado
dentro de una clase. Ya sentamos en el ejemplo del capítulo anterior
las bases de algunos de los conceptos que veremos pero durante los próximos
artículos los vamos a tratar en detalle.



Fundamentos.


Quizás, el concepto más importante
que debemos extraer de la definición de una clase es que ésta
define un nuevo tipo de datos. Una vez definido, este nuevo tipo de datos
lo podemos emplear para crear objetos de ese tipo, es decir, es posible
declarar elementos de esta clase de manera similar a como se declaran las
variables del lenguaje. Así, una clase es una plantilla para múltiples
objetos con características similares y un objeto es una instancia
(o ejemplar) de dicha clase, o sea, mientras la clase es la representación
abstracta de un objeto, la instancia o ejemplar de dicha clase es su representación
física, la que tiene sentido real.


Una clase, normalmente, la constituyen los siguientes
componentes:




  • Atributos: son los datos y variables, que al definirse
    dentro de la clase se llaman variables instancia de la clase. Se llama
    así porque cada instancia de la clase, es decir, cada objeto particular
    que se cree contiene su propia copia de estas variables.



  • Métodos: son las funciones de la clase que
    actuan sobre las variables instancia de ella y definen su comportamiento.


Ambos siempre se definen dentro de la clase (en Java
no existen métodos y variables globales como en C). Al conjunto
de ambos se le conoce como miembros de la clase, así que
cuando leas los términos variables miembro o métodos miembro
de la clase tal, no es más que los componentes que la constituyen.



Declaración de objetos: el operador new.


Para obtener una instancia o ejemplar de la clase se siguen dos pasos:


  • Declaramos una variable del tipo de la clase. Ésta
    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.



  • Obtener una copia física, real de la clase,
    es decir, crear el objeto. Para ello, empleamos 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
    guardaremos en la variable que creamos en el paso anterior. Para
    los que han trabajado con C/C++ ésta variable haría las veces
    de puntero.


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 ésta o a un constructor por defecto de Java si la clase carece
de él.

Bueno, y para no ser pesado, te repito lo siguiente
por última vez pero es importante que lo aprendas de memoria porque
es el concepto base para entender mejor la POO.














ClaseCrea la estructura lógica que define la relación entre
sus miembros.
ObjetoEs la instancia de la clase. Tiene una realidad física,
esto es, ocupa espacio en memoria.



Los métodos.


Como dijimos antes, con ellos definimos lo que queremos
que la clase haga. Otra de las ventajas de Java es la flexibilidad y potencia
que les proporciona, como iremos viendo. Pero veamos en este apartado sus
fundamentos.


La expresión general de la declaración
de un método es la siguiente:


acceso tipo nombreMetodo (parámetros opcionales) {
//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_del_parámetro
- identificador_del_parámetro. Esta línea es lo que se conoce
como el header del método.


Salvo los métodos static 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 er un objeto o un array).
Se puede devolver también una referencia a un objeto por medio de
un nombre de interface. El objeto devuelto debe pertenecer a una clase
que implemente esa interface. 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 super-clase.


Los métodos pueden definir variables locales.
Su visibilidad llega desde la definición al final del bloque en
el que han sido definidas. No hace falta inicializarlas en el punto que
se definien, pero el compilador no permite utilizarlas sin haberles dado
un valor. 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 ficheros de funciones
compiladas normalmente en lenguajes distintos de Java. Ésta es la
forma de poder utilizar conjuntamente funciones realizadas en otros lenguajes
desde código escrito en Java.



Paso de argumentos a métodos.


En Java los argumentos de los tipos primitivos se
pasan siempre por valor. El método recibe una copia del argumento
actual. Si se modifica esta copia, el argumento 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 argumento 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
argumentos a otrs métodos (en C/C++ se pueden pasar punteros a función
como argumentos). 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 (es decir, un
objeto de la clase) o una referencia a otro objeto, ese objeto puede encadenarse
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:


"Java hispano".trim().substring(1,4)

es correcta porque trim() devuelve String y
substring() también. Así nos evitamos tener que hacer:



String s = "Java hispano";
String s2 = s.trim();
String s3 = s2.substring(1,4);

La secuencia de ejecución cuando encadenamos varias llamadas a métodos es de
izquierda a derecha.


El operador return.


Ya comentamos anteriormente que la llamada a un método
podría acabar con la devolución de un valor por parte de
ésta. Éste valor puede ser de un tipo primitivo, o como veremos
en el próximo artículo una referencia a un objeto. En cualquier
caso, de la devolución de este valor se encarga el operador return.


Constructores


Un punto clave de la 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.
Asímismo, si creamos 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 nos ofrece el constructor
de la clase.

Un constructor es un método que se llama
automáticamente cada vez que se crea un bojeto 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, éste 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), ésto 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 argumentos apropiados entre paréntesis. De esta forma, un constructor
sólo tiene que inicializar por sí mismo las variables no
heredadas.



Destrucción de objetos (liberación
de memoria):


Aunque los objetos son dinámicamente asignados
mediante el operador new, 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 caracerística
de Java se le llama garbage collection (recogida 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.

El colector de basura nunca se sabe cuando se
va a activar (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 solo
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 memorias sino de liberar recursos como el manejador de un fichero, una
conexión de red o memoria reservada para funciones nativas. Hay
que tener en cuenta que el colector 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), sin
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 colector 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 la referencia.



El próximo capítulo.


En el siguiente artículo os seguiré contando aspectos más
avanzados del manejo de clases en Java. En éste no os pongo ningún
ejemplo porque con el del capítulo anterior se recogen muchas de
las cosas explicadas aquí.














Leo Suarez es un Ingeniero Superior de Telecomunicaciones por la
Universidad de Las Palmas de Gran Canaria con todas sus letras, que no
son pocas, y trabaja como tal en Santa Cruz de Tenerife.
Cuando no
está saltando de isla en isla o escribiendo para javaHispano aprovecha
para disfrutar con la novia y los amigos del estupendo clima de las islas afortunadas.

Para cualquier duda o tirón de orejas, e-mail a:
leo_ARROBA_javahispano.com