Site hosted by Angelfire.com: Build your free website today!

Cliente/Servidor en Web: la era de los objetos de Java

Introducción a Java

Internet es la aplicación por excelencia que acelerará el uso de objetos, a los que ha transformado en una oportunidad de mercado masivo que atrae a los mejores y más brillantes desarrolladores de todo el mundo. 
Donald DePalma
Forrester Research

Cambio de Paradigma

La aparición de Java obedece a un cambio de paradigma: de Cliente/Servidor (C/S) hacia NCA (Network Computer Architecture). La arquitectura cliente/servidor que ha caracterizado a las redes de los últimos años ha sufrido un cambio con la aparición de Java. Y aunque la arquitectura siga siendo Cliente/Servidor, el manejo del software ha cambiado y ha dado entrada a toda una nueva forma de aplicaciones.

Java es el primer paso hacia una Web de Objetos. Java no es suficiente por si mismo necesita tener una complementación con una infraestructura de objetos distribuidos, esto nos sirve para dar la entrada a un nuevo tema, más allá de Java. 

Java introduce un modelo completamente nuevo de interacción de cliente/servidor en el Web. Nos permite generar pequeños programas como componentes llamados applets, que pueden descargarse en un visualizador compatible con Java. Los applets nos permiten distribuir contenido ejecutable a lo largo del Web junto con los datos. 

Desventaja del Paradigma Cliente/Servidor Anterior

  • La administración y actualización de los clientes de la red es muy costosa.
  • Existe limitada interoperabilidad entre las subredes de la organización debido principalmente a incompatibilidades de hardware.
  • La incompatibilidad de las máquinas impide el acceso universal a las aplicaciones.
  • La actualización de la versión de sistema operativo es traumática entre más grande sea la red.

La carga del Desarrollador de Software

El trabajo del desarrollador de software a medida que iban evolucionando los programas no se ha hecho más sencillo. En los últimos años se ha visto el crecimiento de arquitecturas de hardware incompatibles, cada una soportando múltiples arquitecturas de hardware, con cada plataforma operando con uno o más interfaces gráficas de usuario incompatibles. Ahora se supone que usted debe competir con todo esto y hacer que su aplicación trabaje en un ambiente Cliente-Servidor distribuido. El crecimiento de Internet, el World-Wide Web, y el "comercio electrónico" ha introducido nuevas dimensiones de complejidad dentro del proceso de desarrollo.

Usted está luchando todavía con los mismos problemas de antes; las nuevas técnicas orientadas a objetos que están de moda parecen haber añadido alguno problemas adicionales sin haber resuelto los anteriores problemas.

Java da respuesta a algunos problemas

Las respuestas que Java presenta al mundo son los siguientes: 
  • Su ambiente de programación es orientado a objetos, aunque es aún muy simple.
  • Su ciclo de desarrollo es mucho más rápido porque Java es interpretado. El ciclo compilar-linkar-probar-crash-depuración es obsoleto - ahora usted simplemente compila y corre.
  • Sus aplicaciones son portables a través de múltiples plataformas. Escriba sus aplicaciones una vez, y usted nunca necesitará portarlas - ellas correran sin modificación en sistemas operativos y arquitecturas de hardware múltiples.
  • Sus aplicaciones son robustas porque el sistema run-time de Java le maneja la memoria.
  • Sus aplicaciones gráficas interactivas tienen alto rendimiento porque múltiples hilos concurrentes de actividad en su aplicación son soportadas por la construcción multithreading dentro del lenguaje Java y la plataforma run-time.
  • Sus aplicaciones son adaptables a ambientes cambiantes porque usted puede dinámicamente bajar módulos de código de cualquier parte en la red.
  • Sus usuarios finales pueden confiar que sus aplicaciones son seguras, aún dado que están bajando código de todas partes en internet; el sistema run-time de Java tiene protección integrada en contra de virus e intromisiones.
La plataforma de Lenguaje de Programación Java da un lenguaje de programación orientado a objetos, simple, de alto rendimiento, interpretado y portable.

1.1 INICIOS DEL PROYECTO DE LENGUAJE JAVA

Java está diseñado para vencer los retos de desarrollo de aplicaciones en el contexto de ambientes distribuidos en redes heterogéneas. El más grande de todos estos retos es la entrega segura de aplicaciones que consuman el mínimo de recursos del sistema, puedan correr en cualquier plataforma de hardware y software, y puedan ser extendidas dinámicamente.

Java se originó como parte de un proyecto de investigación para desarrollar software avanzado para una amplia variedad de dispositivos de red y sistemas embebidos. El objetivo fue desarrollar una plataforma operativa de tiempo real, distribuida, portable, confiable y pequeña. Cuando el proyecto comenzó, C++ fue el lenguaje escogido. Pero con el tiempo las dificultades encontradas con C++ crecieron al punto donde los problemas podían ser mejor resueltos creando toda una nueva plataforma de lenguaje. Las decisiones de diseño y arquitectura salieron de una variedad de lenguajes tales como Eiffel, SmallTalk, Objective C, y Cedar/Mesa. El resultado es una plataforma de lenguaje que ha probado ser ideal para desarrollar aplicaciones de usuario final basadas en red, distribuidas y seguras en ambientes que van desde dispositivos embebidos-red hasta el World-Wide Web y el desktop.

1.2 OBJETIVOS DE DISEÑO EN JAVA

Los requerimientos de diseño de Java son determinados por la naturaleza de los ambientes de computación en los cuales el software debe ser entregado.

El crecimiento masivo de Internet y del World-Wide Web lleva a una forma completamente nueva de ver el desarrollo y la distribución de software. Para vivir en el mundo del comercio y distribución electrónicos, Java debe habilitar el desarrollo de aplicaciones altamente robustas, seguras y de alto rendimiento en plataformas múltiples y redes distribuidas, hetereogéneas.

La operación en plataformas múltiples en redes hetereogéneas invalida los esquemas tradicionales de distribución binaria, release, actualización, patch, etc. Para sobrevivir en esta jungla, Java debe ser neutral en arquitectura, portable y dinámicamente adaptable.

El sistema de Java que emergió para lograr estas necesidades es simple, de forma que pueda ser fácilmente programado por la mayoría de los desarrolladores; es familiar, de manera que los actuales desarrolladores puedan aprender Java fácilmente; es orientado a objetos, para tomar ventaja de las metodologías modernas de desarrollo de software y para tener cabida dentro de las aplicaciones Cliente/Servidor; tiene multihilamiento (multithreaded), para lograr alto rendimiento en aplicaciones que necesitan hacer actividades múltiples concurrentes, tales como multimedia; y es interpretado, para máxima portabilidad y capacidades dinámicas.

Vamos ahora a examinar en más detalle las palabras clave de lo anterior:

1.2.1 Simple, Orientado a Objectos y Familiar
1.2.2 Robusto y Seguro
1.2.3 Neutral en Arquitectura y Portable
1.2.4 Alto Rendimiento
1.2.5 Interpretado, usa multihilamiento y es Dinámico

1.2.1 Simple, Orientado a Objectos y Familiar

La características primarias de Java incluyen un lenguaje simple que puede ser programado sin entrenamiento de programador extensivo mientras que está siendo puesto a tono de la prácticas de software actuales. Los conceptos fundamentales de Java son captados rápidamente, los programadores pueden ser productivos desde el mismo principio.

Java es diseñado para ser orientado a objetos desde sus bases. La tecnología de objetos ha encontrado finalmente su entrada en la corriente principal de la programación luego de un período de gestación de treinta años. Las necesidades de sistemas basados en Cliente/Servidor coinciden con las prácticas de paso de mensajes y encapsulamiento del software orientado a objetos. Para funcionar dentro de ambientes basados en red cada vez más complejos, los sistemas de programación debe adoptar conceptos orientados a objetos. Java da una plataforma de desarrollo basada en objetos limpia y eficiente.

Los programadores que usan Java pueden accesar la librería existentes de objetos probados que dan funcionalidad yendo desde los tipos de datos básicos a través de I/O y las interfaces de red hasta cajas de herramientas de interfaz de usuario gráfico. Estas librerías pueden ser extendidas para dar un nuevo comportamiento.

Aún dado que C++ fue rechazado como un lenguaje de implementación, es un lenguaje familiar pues mantiene su similaridad con el C++ tanto como es posible.  Las complejidades innecesarias del C++ se han retirado.

1.2.2 Robusto y Seguro

Java está diseñado para crear software altamente confiable. Hace chequeo muy amplio al tiempo de compilación, seguido por un segundo nivel de chequeo al momento de correr. Las características del lenguaje guían a los programadores hacia hábitos de programación confiables.

El modelo de manejo de memoria es extremadamente simple: los objetos son creados con un operador new. No hay variables tipo apuntador definidas por el programador, no hay aritmética de apuntadores y hay recolección automática de basura. Este modelo de manejo simple de memoria elimina todas las clases de errores de programación que agobian a los programadores de C  y C++. Usted puede desarrollar código en lenguaje Java con confianza de que el sistema encontrará muchos errores rápidamente y que los problemas principales no permanecerán dormidos hasta que su código de producción ha sido enviado al usuario.

Java está diseñado para operar en ambientes distribuidos, lo que significa que la seguridad es de importancia monumental. Con características de  seguridad diseñadas dentro del lenguaje y el sistema de Run-time, Java lle permite construir aplicaciones que no pueden ser invadidas desde afuera. En el ambiente de red, la aplicaciones escritas en Java son seguras con respecto a intrusión de código no autorizado intentando ver detrás de bambalinas y la creación de virus o invasión de sistemas de archivos.

1.2.3 Neutral en Arquitectura y Portable

Java está diseñado para soportar aplicaciones que serán entregadas en ambientes de red hetereogéneos. En tales ambientes, las aplicaciones deben ser capaces de ejecutarse en una variedad de arquitecturas de hardware. Dentro de esta variedad de plataformas de hardware, las aplicaciones deben ejecutarse encima de una variedad de sistemas operativos e interoperar con interfaces de lenguaje de programación múltiples. Para acomodarse a la diversidad de ambientes operativos, el compilador Java genera bytecodes- un formato intermedio neutral en arquitectura diseñado para transportar código eficientemente a plataformas de hardware y software múltiples. La naturaleza interpretada de Java resuelve el problema de distribución binaria y el problema de versión; el mismo bytecodes de Lenguaje Java correrá en cualquier plataforma.

La neutralidad de Arquitectura es sólo una parte de un sistema realmente portable. Java va más allá en portabilidad siendo estricto en su definición de lenguaje básico. Java coloca un estaca en la tierra y especifica los tamaños de sus tipos de datos básicos y el comportamiento de sus operadores aritméticos. Sus programas son los mismos en cualquier plataforma - no hay incompatibilidades de datos a través de las arquitecturas de hardware y software.

La neutralidad de arquitectura y la plataforma de lenguaje portable de Java es conocido como la Máquina Virtual de Java. Es la especificación de una máquina abstracta para la cual los compiladores de lenguaje Java pueden generar código. Las implementaciones específicas de la Máquina Virtual de Java para plataformas de hardware y software específicos entonces logran la realización completa de máquina virtual. La Máquina Virtual de Java está basada primariamente en la definición del estándar de la industria de especificación de interfaz POSIX de un interfaz de sistema portable. La implementación de la Máquina Virtual de Java en nuevas arquitecturas es una tarea relativamente recta siempre que la plataforma objeto cumpla con los requerimientos básicos tales como soporte de multithreading.

1.2.4 Alto Rendimiento

El rendimiento es siempre una consideración. Java logra mayor rendimiento adoptando un esquema por el cual el interpretador puede correr con velocidad total sin necesitar chequear el ambiente de run-time. El recolector de basura automático corre como un hilo en background con baja prioridad, asegurando una alta probabilidad de que la memoria esté disponible cuando se requiera, llevando a un mejor rendimiento. Las aplicaciones que requieren grandes cantidades de poder computacional pueden ser diseñadas de forma que las secciones de cálculos intensivos puedan ser reescritas en código de máquina nativo si lo requiere y con la interfaz correspondiente a la plataforma de Java. En general, los usuarios perciben que las aplicaciones interactivas respondan rápidamente aún dado que son interpretadas.

1.2.5 Interpretado, Con multihilamiento y Dinámico

El interpretador de Java puede ejecutar bytecodes de Java directamente en cualquier máquina para la cual el interpretador y el sistema run-time haya sido transportado. En una plataforma interpretada tal como el sistema Java, la fase de linkado de un programa es simple, incremental, y de peso liviano. Usted se beneficia de desarrollo, prototipos, experimentación, y desarrollo rápido mucho más rápido en contra de los ciclos de compilación, linkado y prueba de peso pesado.

Las aplicaciones modernas basadas en red, tales como el HotJava World-Wide Web, típicamente necesitan hacer varias cosas al mismo tiempo. Un usuario trabajando con HotJava puede correr varias animaciones concurrentemente mientras que hace download de una imagen y está haciendo scroll en la página. La capacidad de multithreading de Java da los medios para construir aplicaciones con muchos hilos concurrentes de actividad. Multithreading por eso resulta en un alto mayor de interactividad para el usuario final.

Java soporta multithreading a nivel de lenguaje con la adición de primitivas de sincronización sofisticadas: la librería de lenguaje da la clase Thread, y el sistema de run-time da primitivas de monitor y condition lock. A nivel de librería, más aún, las librerías de sistema de alto nivel de Java han sido escritas para ser seguras en cuanto a threads: la funcionalidad dada por las librerías está disponible sin conflictos para múltiples hilos de ejecución concurrentes.

Mientras que el compilador de Java es estricto en su chequeo estático al momento de compilación, el lenguaje y el sistema de run-time en sus etapas de linkaje. Las clases son linkadas sólo si se necesitan. Los nuevos módulos de código pueden ser linkados según demanda desde una variedad de fuentes, aún desde fuentes a través de una red. En el caso del browser HotJava y aplicaciones similares, el código ejecutable interactivo puede ser cargado desde cualquier lugar, que habilita la actualización transparente de aplicaciones. El resultado serán servicios en línea que evolucionesn constantemente; ellos pueden permanener innovadores y frescos, atender más clientes e incitar el crecimiento del comercio electrónico en la Internet.


1.3 LA PLATAFORMA JAVA - UNA NUEVA APROXIMACION A COMPUTACION DISTRIBUIDA

Las características discutidas, tomadas individualmente,anteriormente pueden ser encontradas en una variedad de plataformas de desarrollo de software. Lo que es completamente nuevo es la manera en la cual Java y su sistema run-time las han combinado para producir un sistema de programación flexible y poderoso.

Desarrollar sus aplicaciones usando Java tendrá  un software que es portable a través de múltiples arquitecturas de máquinas, sistemas operativos, e interfaces gráficas de usuarios, que es seguro y de alto rendimiento.
 

JAVA-SIMPLE Y FAMILIAR

Usted sabe que ha alcanzado la perfeccion en el diseño,
no cuando usted no tiene nada que añadir,
sino cuando no tiene nada más que quitar.

Antoine de Saint Exupery.


Java presenta un nuevo punto de vista en la evolución de lenguajes de programación - la creación de un lenguaje pequeño y simple que es lo suficientemente amplio para asumir una amplia variedad de desarrollo de aplicaciones de software. Aunque Java es superficialmente similar a C y C++, Java ganó su simplicidad del recorte sistemático de características de sus predecesores. Este capítulo discute dos de las características de diseño primario de Java, que son, su simpleza (de la remoción de características) y familiar (porque parece a C y C++).

Objetivos de diseño

La simplicidad es uno de los objetivos más importantes de diseño en Java. La simplicidad y el recorte de muchas "características" de dudoso valor de sus ancestros C y C++ mantiene a Java relativamente pequeño y reduce la carga de los programadores al producir aplicaciones confiables.

Otro objetivo de diseño mayor es que Java sea familiar para la mayoría de los programadores. por eso, Java "se ve como " C++. Los programadores familiarizados con C, Objective C, C++, Eiffel, Ada, y lenguajes relacionados deberían encontrar la curva de aprendizaje de Java muy corta - en el orden de un par de semanas.

Para ilustrar los aspectos simples y familiares a Java mostramos el programa primero, el programa más simple que usted puede escribir y que haga algo. Aquí está primero implementado en Java.

class primero {
   static public void main(String args[]) {
        System.out.println("Hola Juanito!");
   }
 }

Este ejemplo declara una clase llamada primero.

Dentro de la clase primero, declaramos un método sencillo llamado main() que a su vez contiene una invocación de método simple para desplegar la cadena "Hola Juanito!" en la salida standard, que en el DOS es simplemente la pantalla. La frase que imprime "Hola Juanito!" lo hace así invocando el método println del objeto out. El Objeto Out es una variable de clase en la clase System que hace operaciones de salida en archivos. Eso es todo lo que hay en   primero.
 
Montaje de la Primera aplicación en Java:
  • Cree un archivo llamado primero.java y coloque el código anteriormente mencionado.
  • Usted debe tener el JDK en alguna versión posterior a la 1.1. (download del JDK 1.2.1)
  • Copiar el archivo primero.java a C:\JDK1.2.1\BIN\primero.java.
  • Entrar a MS-DOS
  • Cambiarse al directorio C:\JDK1.2.1\BIN\o el correspondiente
  • Escribir la linea de comando: javac primero.java
  • Si hay algún error usar el editor del DOS para hacer modificaciones del código.
  • Luego de que compile sin errores debe dar el siguiente comando: java primero
  • En la pantalla debe aparecer un letrero de "Hola Juanito".
  • Listo, eso es todo para correr su primera aplicación de Java.

2.1 CARACTERISTICAS PRINCIPALES DEL LENGUAJE JAVA

Java sigue al C++ en algún grado, lo que trae los beneficios de ser familiar a muchos programadores. Esta sección describe las características esenciales de Java y apunta donde el lenguaje diverge de sus ancestros C y C++.

2.1.1Tipos de Datos Primitivos

Todo lo demás aparte de los tipos de datos discutidos aquí, es un objeto para Java. Aún los tipos de datos primarios pueden ser encapsulados dentro de objetos dados a una librería si es requerido. Java sigue C y C++ muy cercanamente en su conjunto de tipos de datos básicos, con un par de excepciones menores. Hay sólo tres grupos de tipos de datos primitivos, que son tipos numéricos, tipos buleanos y arreglos.

Tipos de Datos Numéricos

 
Los tipos numéricos de enteros son byte de 8-bit, short de 16-bit, int de 32-bit, y long de 64-bit. El tipo de dato de byte de 8-bit en Java ha reemplazado al antiguo char de C y C++. Java da una interpretación diferente al tipo de dato char.

No hay un especificador de tipo unsigned para datos enteros en Java.

Los tipos numéricos reales (Realnumeric) son el float de 32-bit y el double 64-bit. Los tipos numéricos reales y sus operaciones matemáticas son como las definidas por la especificación IEEE 754. Un valor literal de punto flotante, como 23.79, es considerado double por default, usted debe explicitamente hacer un cast para convertirlo en float si usted quiere asignarlo a una variable float.

Tipos de Datos Caracter El dato caracter de Java es diferente del C tradicional. El tipo de dato Char de Java define un caracter UniCode de 16-bit. Los caracteres UniCode son valores de 16-bit sin signo que definen un código de caracter en el rango de 0 a 65,535. Si usted escribe una declaración como la siguiente:

 char myChar = 'Q';

usted obtiene un tipo UniCode (valor de 16-bit sin signo) inicializado al valor UniCode del caracter Q. Al adoptar el standard de caracteres UniCode para su tipo de caracteres, las aplicaciones de lenguaje Java son fáciles de internacionalizar y localizar, expandiendo grandemente el mercado de aplicaciones por todo el mundo.

Tipo de datos buleanos

 
Java añadió un tipo de datos Buleano como tipo primitivo de dato, tácitamente ratificando la práctica de programación de C y C++ existente, donde los desarrolladores definen las palabras clave TRUE y FALSE o YES y NO o construcciones similares. Una variable buleana Java asume el valor de verdadero o falso. Un buleano de Java es un tipo de dato distinto; a diferencia de la práctica comun de C, un tipo de buleano de Java no puede ser convertido a ningún tipo numérico.

2.1.2 Operadores Aritméticos y Relacionales

Todos los operadores familiares de C y C++ se aplican. Java no tiene tipo unsigned, de forma que el operador >>> ha sido añadido al lenguaje para indicar un corrimiento a la derecha unsigned (lógica). Java también usa el operador + para concatenación de cadenas; la concatenación es cubierta abajo en la discusión de string.

2.1.3 Arrays

En contraste con C y C++, los arreglos de lenguaje Java son objetos de lenguaje de primera clase. Un arreglo en Java es un objeto real con una representación en run-time. Usted puede declarar y alocar arreglos de cualquier tipo, y puede alocar arrays de arrays para obtener arrays multi-dimensionales.

Usted declara un arreglo que, digamos, de TPunto (una clase declarada en otro lado) con una declaración como esta:

 TPunto arregloDePuntos[];

Este código dice que arregloDePuntos es un arreglo no inicializado de TPuntos. En su momento, el único espacio alocado para arregloDePuntos es un handle de referencia. En algún otro momento usted debe alocar la cantidad de almacenaje que necesite, así:

arregloDePuntos = new TPunto[10];

Para alocar un arreglo de 10 referencias a TPuntos que son inicializados a la referencia de Null. Note que esta alocación de un array no aloca realmente ningún objeto de clase TPunto por usted; usted tendrá que alocar también los objetos TPunto, algo así:

int i;

for (i = 0; i < 10; i++) {

   arregloDePuntos[i] = new TPunto();

   }

El acceso a los elementos de arregloDePuntos pueden ser hechos a través del indexamiento normal de C, pero todos los accesos de arrays son chequeados para asegurar que sus índices están dentro del rango del arreglo. Se generará una excepción si el índice está por fuera de los límites del array.

La longitud de un arreglo es guardado en la variable de instancia length del array específico: arregloDePuntos.length contiene el número de elementos. Por ejemplo, el fragmento de código:

 howMany = arregloDePuntos.length;

asignará el valor 10 a la variable howMany.

La noción C de un apuntador a un array de elementos de memoria se ha marchado, y con él, la aritmética de apuntadores arbitrarios que lleva a un código no confiable en C. Ya no se podrá usted salir por fuera de un array, posiblemente llenando de basura la memoria y llevando al conocido síndrome de "crash-retardado" posteriormente, donde una violación de acceso de memoria se manifestaba horas y días más tarde. Los programadores pueden estar confiados del chequeo de arreglos en Java llevar a código más confiable y robusto.

2.1.4 Strings

Los Strings son objetos de Java, no pseudo arreglos de caracteres como en C. Hay realmente dos clases de objetos: la clase String que es para objetos read-only. La clase StringBuffer es para objetos de cadena que usted desea modificar.

El compilador Java entiende que una cadena de caracteres entre comillas dobles debe ser instanciada como un objeto String. por eso la declaración:

String hello = "Hola Juanito!";

Esta cadena es instanciada en representación UniCode.

Java ha extendido el significado del operador + para indicar concatenación de cadenas por eso usted podrá escribir instrucciones como:
 

   System.out.println("Hay " + num + " caracteres en el archivo.");

Este fragmento de código concatena la cadena "There are " con el resultado de convertir el valor numérico de num a cadena, y concatena esta con la cadena " characters in the file.". Entonces el resultado son las concatenaciones hechas en la salida estandar.

Los objetos String dan un método length() para obtener el número de caracteres en la cadena.
 

2.1.5 Multi-Level Break

Java no tiene la instrucción goto. Para romper o continuar loops o cases múltiples anidados, usted puede colocar labels y luego romper para continuar el bloque nombrado por el label. Aquí hay un pequeño fragmento de la Clase String integrada dentro de Java:

test: for (int i = fromIndex; i + max1 <= max2; i++) {

   if (charAt(i) == c0) {

   for (int k = 1; k<max1; k++) {

   if (charAt(i+k) != str.charAt(k)) {

   continue test;

   }
         } /* fin del loop interno */

   }
 } /* fin del loop externo */
 

La instrucción continue test está dentro de un for anidado dentro de otro loop. Referenciando a este label, la frase continue le pasa el control a la instrucción más externa. En C tradicional, las frases continue pueden sólo continuar con el bloque anidado más inmediatamente, para continuar con los bloques más externos, los programadores habían tradicionalmente ya sea usado variables booleanas cuyo único propósito es determinar si el bloque más externo debe continuar o salir también; alternativamente, los programadores han (mal)usado la instrucción goto para salir de bloque anidados. El uso de bloques con label en Java lleva a considerable simplificación en el esfuerzo de programación y a mayor reducción en el mantenimiento.

La noción de los bloques con label se remonta a mediados de 1970s, pero no ha llegado a nada en ningún lenguaje de programación moderno. Perl es otro lenguaje de programación  moderno que implementa el concepto de bloques con labels. El Next label y Last Label de Perl son equivalentes al label continue y las sentencias de break en Java.

2.1.6 Manejo de Memoria y recolección de Basura

Los programadores C y C++ están actualmente acostumbrados a los problemas de manejar memoria explícitamente: alocación y liberación de memoria; y mantener control de qué memoria puede ser liberada y cuando. El manejo de memoria explícito ha probado ser una abundante fuente de bugs, crashes, caídas de memoria y pobre rendimiento.

Java quita completamente la carga del manejo de memoria del programador. Los apuntadores estilo C, la aritmética de apuntadores, malloc, y free no existen. La recolección automática de basura es una parte integral de Java y su sistema de run-time. Mientras Java tiene un operador new para alocar memoria para objetos, no tiene una función free explícita. Una vez usted ha alocado un objeto, el sistema run-time hace seguimiento del estado de los objetos y automáticamente reclama memoria cuando los objetos no se requieren más, liberando memoria para uso futuro.

El modelo de manejo de memoria está basado en objetos y referencias a objetos. Java no tiene apuntadores. En lugar de ello, todas las referencias a objeto almacenado alocado, que en la práctica significa todas las referencias a un objeto, son a través de "handles" simbólicos. El administrador de memoria de Java hace seguimiento de las referencias a los objetos. Cuando un objeto no tiene más referencias, el objeto es un candidato para recolección de basura.

El modelo de localización de memoria y la recolección automática de basura hace su tarea de programación más fácil, elimina toda una clase entera de bugs, y en general da mejor rendimiento del que usted obtiene a través del manejo explícito de memoria. Aquí hay un fragmento de código que ilustra cuando la recolección de basura ocurre:

class ReverseString {
   public static String reverseIt(String source) {
   int i, len = source.length();
   StringBuffer dest = new StringBuffer(len);

   for (i = (len - 1); i >= 0; i--) {
       dest.appendChar(source.charAt(i));
   }
   return dest.toString();
   }
 }

La variable dest es usada como una referencia de objeto temporal durante la ejecución del método reverseIt. Cuando dest sale de ámbito (el método reverseIt vuelve), la referencia a ese objeto se ha ido y es entonces candidato para recolección de basura.

2.1.7 El recolector de basura de Background

El recolector de basura de Java logra alto rendimiento tomando ventaja de la naturaleza de un comportamiento del usuario cuando está interactuando con las aplicaciones de software tales como el browser HotJava. El usuario típico de aplicaciones típicamente interactivas tiene muchas pausas cuando está mirando la escena en frente de él o pensando que va a hacer ahora. El sistema run-time de Java toma ventaja de estos períodos muertos y corre el recolector de basura en un hilo de baja prioridad cuando ninguna otro hilamientoo o thread está compitiendo por los ciclos de CPU. El recolector de basura reune y compacta la memoria no usada, incrementando la probabilidad de que los recursos de memoria adecuados estén disponibles cuando se necesite durante periodos de uso interactivo pesado.

Este uso de un hilo (thread) para correr el recolector de basura es sólo uno de los muchos ejemplos de sinergía que uno obtiene de las capacidades multithreading integradas de Java - otro problema irresoluble de otra forma, es resuelto de una forma elegante y simple.

2.1.8 Sincronización de Thread Integrada

Java soporta multithreading, a nivel de lenguaje (sintáctico) y por medio de soporte de su sistema de run-time y los objetos de thread. Mientras que otros sistemas han dado facilidades para multithreading (usualmente por medio de librerías de "proceso de peso liviano"), la construcción de multithreading dentro del lenguaje en sí da al programador una herramienta mucho más fácil y poderosa para crear fácilmente clases con multithreading seguras.

2.2 CARACTERISTICAS REMOVIDAS DESDE C y C++

Hasta ahora nos hemos concentrado en las características principales de Java. Esta sección discute las características removidas desde C y C++ en la evolución de Java.

El primer paso fue eliminar la redundancia de C y C++. De muchas maneras, el lenguaje C evolución en una colección de características que se sobrelapaban, dando demasiadas formas de decir la misma cosa, mientras en muchos casos no se daban las características necesarias. C++, en un intento de adicionar "clases en C", simplemente añadió más redundancia mientras retenía muchos de los problemas inherentes de C.

2.2.1 Ya no más Typedefs, Defines, o Preprocessor

El código fuente escrito en Java es simple. no hay preprocessor, no hay #define y capacidades relacionadas, no hay typedef, y al estar ausentes esas características, ya no se necesitan los archivos header. En lugar de archivos header, los archivos de fuente de lenguaje Java dan la declaración de otras clases y sus métodos.

Un problema mayúsculo con C y C++ es la cantidad de contexto que usted necesita para entender el código de otro programador: usted tiene que leer todos los archivos de header relacionados, todos los #defines relacionados, y todos los typedefs relacionados antes de que usted pueda aún incluso empezar a analizar un programa. En esencia, la programación con #defines y typedefs hace que cada programador invente un nuevo lenguaje de programación que es incomprensible para cualquier otro diferente a su autor, botando a la basura los objetivos de una buena práctica de programación.

En Java, usted obtiene los efectos de los #define usando constantes. Usted logra los efectos de los typedef al declarar clases- after all, una clase que efectivamente crea un nuevo tipo. Usted no necesita archivos header porque el compilador de Java compila la definición de clases de una forma binaria que retiene toda la información de tipo a través del linkaje.

Al quitar todo este bagaje, Java se vuelve notablemente libre con respecto al contexto. Los programadores pueden leer y entender el código y más importantemente, modificar y reusar el código más rápida y fácilmente.

2.2.2 No hay más Structures o Unions

Java no tiene structures o unions como tipos de datos complejos. Usted no necesita structures y unions cuando usted tiene las clases; usted puede lograr el mismo efecto simplemente declarando una clase con las variables de instancia apropiadas.

El fragmento de código abajo declara una clase llamada TPunto.

class TPunto extends Object {

   double x;

   double y;

   // methods to access the instance variables

   }

El siguente fragmento de código declara una clase llamada TRectangle, que usa objetos en la clase TPunto como variables de instancia.

   class TRectangle extends Object {

   TPunto lowerLeft;
   TPunto upperRight;

   // methods to access the instance variables

   }

En C usted definiría estas clases y estructuras. En Java usted simplemente declara clases. Usted puede hacer las variables de instancia tan privada o tan públicas como usted desee, dependiendo de cuanto usted desea esconder los detalles de la implementación de otros objetos.

2.2.3 No hay Enums

Java no tiene tipos enum.  Usted puede obtener algo similar a enum declarando una clase cuya única razón es mantener constante. Usted podría usar esta característica en algo como esto:

class Direction extends Object {

   public static final int North = 1;
   public static final int South = 2;
   public static final int East = 3;
   public static final int West = 4;

   }

Usted se puede referir a la constante South, por ejemplo, usando la notación Direction.South.

Usando clases para contener constantes de esta forma da una mayor ventaja sobre los tipos enum de C. En C (y C++), los nombres definidos en enum deben ser únicos: si usted tiene un enum llamado HotColors que contiene Red y Yellow, usted no puede usar esos nombres en ningún otro enum. Usted no podía, por ejemplo, definir otro enum llamado TrafficLightColors también conteine Red y Yellow.

Usando la técnica de usar clases para contener constantes en Java, usted puede usar los mismos nombres en clases diferentes, puesto que esos nombres son calificados por el nombre de la clase que lo contiene. De nuestro anterior ejemplo, usted podría querer crear otra clase llamada CompassRose:

class CompassRose extends Object {
   North = 1;
   NorthEast = 2;

   public static final int East = 3;
   public static final int SouthEast = 4;
   public static final int South = 5;
   public static final int SouthWest = 6;
   public static final int West = 7;
   public static final int NorthWest = 8;

   }

No hay ambigüedad porque el nombre de la clase conteniente actua como un cualificador para las constantes. En el segundo ejemplo, usted usaría la notación CompassRose.NorthWest para accesar el valor correspondiente. Java le da efectivamente el concepto de enums cualificados, todo dentro de los mecanismos de clase existentes.

2.2.4 No hay más funciones

Java no tiene funciones. La programación orientada a objetos reemplaza a los estilos funcional y procedural. Mezclar los dos estilos simplemente lleva a confusiones y diluye la pureza de lenguaje orientado a objetos. Cualquier cosa que usted pueda hacer con una función usted puede hacerlo simplemente definiendo una clase y creando métodos para esa clase. Considere la clase TPunto desde arriba. Hemo añadidos métodos públicos para asignar y asignar las variables de instancias:

class TPunto extends Object {
   double x;
   double y;

   public void setX(double x) {
     this.x = x;
   }

   public void setY(double y) {
     this.y = y;
   }

   public double x() {
     return x;
   }

   public double y() {
     return y;
   }
 }

Si las variables de instancia x e y son privadas para esta clase, el único medio para accesarlos es por medio de métodos públicos de la clase. Aquí está como usted usaría objetos de la clase TPunto desde adentro, digamos, de un objeto de la clase TRectangle:

class TRectangle extends Object {

   TPunto lowerLeft;
   TPunto upperRight;

   public void setEmptyRect() {
     lowerLeft.setX(0.0);
     lowerLeft.setY(0.0);
     upperRight.setX(0.0);
     upperRight.setY(0.0);
   }
 }

No podemos decir que las funciones y los procedimientos sean inherentemente erróneos. Pero dadas las clases y los métodos, estamos ahora cada vez más cerca de una única forma de expresar una tarea dada. Al eliminar las funciones, su trabajo como programador se ve inmensamente simplificado: usted trabaja sólo con clases y sus métodos.

2.2.5 No más Herencia Múltiple

La herencia múltiple - y todos los problemas que genera - fue descartada de Java. Las características deseables de la herencia múltiple son dadas por interfaces - conceptualmente similares a los protocolos de Objective C.
 

Una interfaz no es una definición de una clase. Más bien, es una definición de un conjunto de métodos que una o más clases implementarán. Un factor importante de las interfaces es que declaran solamente métodos y constantes. Las variables no pueden ser definidas en interfaces.

2.2.6 No hay más sentencias Goto

Java no tiene sentencias goto (sin embargo, sigue siendo palabra reservada). Los estudios ilustraron que el goto es (mal)usado más a menudo porque simplemente está allí. Al eliminar el goto lleva a una simplificación del lenguaje - no hay reglas acerca de los efectos de un goto en medio de un ciclo for, por ejemplo. Los estudios en aproximadamente 100.000 líneas de código en C determinó que aproximadamente el 90 por ciento de las sentencias goto fueron usadas para obtener el efecto de un break en loops anidados. Como se mencionó arriba, en multi-level break, los continue quitaron la mayoría de las necesidades para las sentencias goto.

2.2.7 No hay más sobrecarga de Operadores

No hay medios dados por el cual los programadores pueden sobrecargar los operadores aritméticos estandar. Una vez más, los efectos de la sobrecarga de operador pueden ser logrados  fácilmente declarando una clase, con las variables de instancia y los métodos apropiados para manipular esas variables. La eliminación de sobrecarga de operadores lleva a gran simplificación de código.

2.2.8 No hay coerción automática

Java prohibe las coerciones automáticas estilo C y C++. Si usted desea forzar un elemento de datos de un tipo a un tipo de datos y esto implica una pérdida de precisión, usted debe hacerlo explícitamente usando un cast. Considere este fragmento de código:
 

   int myInt;
   double myFloat = 3.14159;

   myInt = myFloat;
 

La asignación de myFloat a myInt resultaría en un error del compilador indicando una posible pérdida de precisión y que usted debe usar un cast explícito. Por esto usted debería re-escribir el fragmento de código como:

      int myInt;

   double myFloat = 3.14159;
 

   myInt = (int)myFloat;
 

2.2.9 No Más Apuntadores

La mayoría de los estudios están de acuerdo en que los apuntadores son una de las características primarias que hace que los programadores metan bugs dentro de su código. Dado que las estructuras se han ido, y los arrays y strings son objetos, la necesidad de apuntadores para estas formas ya no existe. Por ello, Java no tiene tipos de dato apuntador. Cualquier tarea que requiriera arrays, structures y apuntadores en C puede ser hecho más fácil y confiablemente declarando objetos y arreglos de objetos. En lugar de la compleja manipulación de apuntadores en apuntadores de arreglos, usted accesa arrays por sus índices aritméticos. El sistema run-time de Java chequea todos los arreglos indexando para asegurar que los índices están dentro de los límites del array.

Usted no tendrá más apuntadores columpiándose y llenando la memoria de basura por apuntadores incorrectos, porque no hay apuntadores en Java.

2.2.10 Otras Ventajas

Aquí podemos hablar de otras ventajas desde el punto de vista sintáctico que lo hacen un lenguaje superior.
+ Se pueden tener varios procedimientos que hagan lo mismo con el mismo nombre y diferenciables por argumentos. Una ventaja indiscutible del C.
+ El manejo de new al estilo de Delphi que es mucho más claro y directo.
+ El manejo de las excepciones al estilo Delphi es definitivamente un acierto.
+ La posibilidad de sumar cadenas como en Pascal, más la característica de Basic de poder sumar valores numéricos sin preocuparse si se le hace el IntToSTr.
+ La eliminación de los apuntadores nos quitó un gran número de dolores de cabeza.
 

Las desventajas que tiene son:
+ La sensibilidad a mayúsculas. Esta es una características que continúa dándole a los programas una apariencia hostil. Y es un factor que colabora con errores irrastreables a simple vista. Estos errores son los primero en aparecer con el cansancio del desarrollador.
+ El manejo de los switches y cases. En Delphi (pascal) son aún mucho más poderosos.

2.3 RESUMEN

Para resumir este capítulo, Java es:

+ Simple.- el número de construcciones de lenguaje que usted necesita para entender y hacer su trabajo es mínimo.
+ Familiar.- Java es parecido al C y C++ pero descarta las complejidades agobiantes de esos lenguajes.

Ahora que usted ha visto como se simplificó Java al quitar características de sus predecesores, lea el siguiente capítulo.

Basado en el Documento de Sun - Java Language Environment
 

Tutorial de JavaBeans