sábado, 15 de septiembre de 2018

El lenguaje Java


Introducción


Java es un lenguaje de programación de propósito general, concurrente, orientado a objetos, que fue diseñado específicamente para tener tan pocas dependencias de implementación como fuera posible. Su intención es permitir que los desarrolladores de aplicaciones escriban el programa una vez y lo ejecuten en cualquier dispositivo (conocido en inglés como WORA, o "write once, run anywhere"), lo que quiere decir que el código que es ejecutado en una plataforma no tiene que ser recompilado para correr en otra. Java es, a partir de 2012, uno de los lenguajes de programación más populares en uso, particularmente para aplicaciones de cliente-servidor de web, con unos diez millones de usuarios reportados.
Historia del lenguaje

El lenguaje JavaTM fue creado por Sun Microsystems Inc. en un proceso por etapas que arranca en 1990, año en el que Sun creó un grupo de trabajo, liderado por James Gosling, para desarrollar un sistema para controlar electrodomésticos e incluso PDAs o Asistentes Personales (pequeños ordenadores) que, además, permitiera la conexión a redes de ordenadores. Se pretendía crear un hardware polivalente, con un Sistema Operativo eficiente (SunOS) y un lenguaje de desarrollo denominado Oak (roble), el precursor de Java. El proyecto finalizó en 1992 y resultó un completo fracaso debido al excesivo coste del producto, con relación a alternativas similares, tras lo cual el grupo se disolvió.

Por entonces aparece Mosaic y la World Wide Web. Después de la disolución del grupo de trabajo, únicamente quedaba del proyecto el lenguaje Oak. Gracias a una acertada decisión de distribuir libremente el lenguaje por la Red de Redes y al auge y la facilidad de acceso a Internet, propiciado por la WWW, el lenguaje se popularizó y se consiguió que una gran cantidad de programadores lo depurasen y terminasen de perfilar la forma y usos del mismo. A partir de este momento, el lenguaje se difunde a una velocidad vertiginosa, añadiéndosele numerosas clases y funcionalidad para TCP/IP. El nombre del lenguaje tuvo que ser cambiado ya que existía otro llamado Oak. El nombre “Java” surgió en una de las sesiones de

“brainstorming” celebradas por el equipo de desarrollo del lenguaje. Buscaban un nombre que evocara la esencia de la tecnología (viveza, animación, rapidez, interactividad...). Java fue elegido de entre muchísimas propuestas. No es un acrónimo, sino únicamente algo humeante, caliente y que a muchos programadores les gusta beber en grandes cantidades: una taza de café (Java en argot Inglés americano2). De esta forma, Sun lanzó las primeras versiones de Java a principios de 1995. Desde entonces, Sun ha sabido manejar inteligentemente el éxito obtenido por su lenguaje, concediéndose licencias a cualquiera sin ningún problema, fomentando su uso entre la comunidad informática y extendiendo las especificaciones y funcionalidad del lenguaje.
¿Qué es Java?

Java es un lenguaje de desarrollo de propósito general, y como tal es válido para realizar todo tipo de aplicaciones profesionales.

Entonces, ¿Es simplemente otro lenguaje más? Definitivamente no. Incluye una combinación de características que lo hacen único y está siendo adoptado por multitud de fabricantes como herramienta básica para el desarrollo de aplicaciones comerciales de gran repercusión.

¿Qué lo hace distinto de los demás lenguajes?


Una de las características más importantes es que los programas “ejecutables”, creados por el compilador de Java, son independientes de la arquitectura. Se ejecutan indistintamente en una gran variedad de equipos con diferentes microprocesadores y sistemas operativos.

· De momento, es público. Puede conseguirse un JDK (Java Developer's Kit) o Kit de desarrollo de aplicaciones Java gratis. No se sabe si en un futuro seguirá siéndolo.

· Permite escribir Applets (pequeños programas que se insertan en una página HTML) y se ejecutan en el ordenador local.

· Se pueden escribir aplicaciones para intrarredes, aplicaciones cliente/servidor, aplicaciones distribuidas en redes locales y en Internet.

· Es fácil de aprender y está bien estructurado.

· Las aplicaciones son fiables. Puede controlarse su seguridad frente al acceso a recursos del sistema y es capaz de gestionar permisos y criptografía. También, según Sun, la seguridad frente a virus a través de redes locales e Internet está garantizada. Aunque al igual que ha ocurrido con otras tecnologías y aplicaciones, se han descubierto, y posteriormente subsanado, “agujeros” en la seguridad de Java.



¿Es fácil de aprender?


Sí.

Para el colectivo de programadores que conocen la programación orientada a objetos, el cambio a Java puede ser realmente sencillo. Es un lenguaje bien estructurado, sin punteros y sin necesidad de tener que controlar la asignación de memoria a estructuras de datos u objetos.

Para los programadores en C++ también es sencillo el cambio, ya que la sintaxis es prácticamente la misma que en este lenguaje.

Para todo aquel que no conozca la programación orientada a objetos, este lenguaje es ideal para aprender todos sus conceptos, ya que en cada paso de su aprendizaje se va comprobando que las cosas se hacen en la forma natural de hacerlas, sin sorpresas ni comportamientos extraños de los programas. A medida que se va aprendiendo, se va fomentando en el programador, y sin esfuerzo, un buen estilo de programación orientada a objetos. En realidad, no puede ser de otra forma, ya que Java impide “hacer cosas extrañas” y, además, no permite “abandonar” la programación orientada a objetos, como ocurre con otros lenguajes de programación. Esto es bastante conveniente, de lo contrario, un programador que está aprendiendo puede sentir la tentación de “volver” a lo que conoce (la programación tradicional).

A medida que se van comprobando las ventajas de la programación orientada a objetos, para aquellos que las desconocen, y la facilidad y naturalidad del lenguaje Java, éste va atrapando a quien se acerca a él, y después de algún tiempo trabajando con Java, hay pocos programadores que no lo consideren como “su favorito”.



Características del lenguaje


· Es intrínsecamente orientado a objetos.

· Funciona perfectamente en red.

· Aprovecha características de la mayoría de los lenguajes modernos evitando sus inconvenientes. En particular los del C++.

· Tiene una gran funcionalidad gracias a sus librerías (clases).

· NO tiene punteros manejables por el programador, aunque los maneja interna y transparentemente.

· El manejo de la memoria no es un problema, la gestiona el propio lenguaje y no el programador.

· Genera aplicaciones con pocos errores posibles.

· Incorpora Multi-Threading (para permitir la ejecución de tareas concurrentes dentro de un mismo programa).

El lenguaje Java puede considerarse como una evolución del C++. La sintaxis es parecida a la de este lenguaje, por lo que en este libro se hará referencia a dicho lenguaje frecuentemente. A pesar de que puede considerarse como una evolución del C++ no acarrea los inconvenientes del mismo, ya que Java fue diseñado “partiendo de cero”, es decir, no necesitaba ser compatible con versiones anteriores de ningún lenguaje como ocurre con C++ y C.

Gracias a que fue diseñado “partiendo de cero” ha conseguido convertirse en un lenguaje orientado a objetos puro, limpio y práctico. No permite programar mediante otra técnica que no sea la programación orientada a objetos (POO en adelante) y, una vez superado el aprendizaje de la programación orientada a objetos, es realmente sencillo aprender Java.

¿El lenguaje es Compilado o Interpretado? Ni una cosa ni la otra. Aunque estrictamente hablando es interpretado, necesita de un proceso previo de compilación. Una vez “compilado” el programa, se crea un fichero que almacena lo que se denomina bytecodes o j_code (pseudocódigo prácticamente al nivel de código máquina). Para ejecutarlo, es necesario un “intérprete”, la JVM (Java Virtual Machine) máquina virtual Java. De esta forma, es posible compilar el programa en una estación UNIX y ejecutarlo en otra con Windows95 utilizando la máquina virtual Java para Windows95. Esta JVM se encarga de leer los bytecodes y traducirlos a instrucciones ejecutables directamente en un determinado microprocesador, de una forma bastante eficiente.

Que el programa deba ser “interpretado” no hace que sea poco eficiente en cuanto a velocidad, ya que la interpretación se hace prácticamente al nivel de código máquina. Por ejemplo, es mucho más rápido que cualquier otro programa interpretado como por ejemplo Visual Basic, aunque es más lento que el mismo programa escrito en C++.

Esta deficiencia en cuanto a la velocidad, puede ser aminorada por los compiladores Just-In-

Time (JIT). Un compilador JIT transforma los bytecodes de un programa o un applet en código nativo de la plataforma donde se ejecuta, por lo que es más rápido.

Suelen ser incorporados por los navegadores, como Netscape o

Internet Explorer.
El lenguaje Java es robusto. Las aplicaciones creadas en este lenguaje son susceptibles de contener pocos errores, principalmente porque la gestión de memoria y punteros es realizada por el propio lenguaje y no por el programador. Bien es sabido que la mayoría de los errores en las aplicaciones vienen producidos por fallos en la gestión de punteros o la asignación y liberación de memoria. Además, el lenguaje contiene estructuras para la detección de excepciones (errores de ejecución previstos) y permite obligar al programador a escribir código fiable mediante la declaración de excepciones posibles para una determinada clase reutilizable.
La Máquina Virtual Java (JVM)
La máquina virtual Java es la idea revolucionaria del lenguaje. Es la entidad que proporciona la independencia de plataforma para los programas Java “compilados” en byte-code.
Un mismo programa fuente compilado en distintas plataformas o sistemas operativos, genera el mismo fichero en byte-code. Esto es lógico, ya que se supone que el compilador de Java traduce el fichero fuente a código ejecutable por una máquina que únicamente existe en forma virtual (aunque se trabaja en la construcción de microprocesadores que ejecuten directamente el byte-code).
Evidentemente, si un mismo programa en byte-code puede ser ejecutado en distintas plataformas es porque existe un traductor de ese byte-code a código nativo de la máquina sobre la que se ejecuta. Esta tarea es realizada por la JVM.
Existe una versión distinta de esta JVM para cada plataforma. Esta JVM se carga en memoria y va traduciendo “al vuelo”, los byte-codes a código máquina. La JVM no ocupa mucho espacio en memoria, piénsese que fue diseñada para poder ejecutarse sobre pequeños electrodomésticos como teléfonos, televisores, etc.


JavaScript

Atención: No hay que confundir Java con JavaScript.
JavaScript es una variación del lenguaje Java. Desarrollado por Netscape y Sun, fue diseñado para simplificar la creación de contenidos interactivos en páginas web sin necesidad de tener que programar applets en Java. En lugar de ello se utilizan técnicas más sencillas mediante el lenguaje JavaScript que es más flexible, aunque mucho menos potente.
El lenguaje JavaScript es totalmente interpretado por el navegador. El código fuente se incluye como parte de la página web en HTML y es el navegador el encargado de “interpretar” el código fuente.
¿No podría hacerse en Java? Sí, pero no de forma tan sencilla como con
JavaScript. Este lenguaje fue pensado para acercar Java a programadores inexpertos y creadores de contenidos HTML sin conocimientos avanzados de programación.
Diferencias entre Java y JavaScript:
Java es compilado, mientras que JavaScript es totalmente interpretado.
Java es orientado a objetos. JavaScript utiliza objetos, pero no permite la programación orientada a objetos.
En JavaScript no es necesario declarar las variables y en Java sí.
En JavaScript, las comprobaciones de validez de referencias a objetos se realizan en tiempo de ejecución, mientras que en Java se realiza en tiempo de compilación.
JavaScript tiene un número limitado de tipos de datos y clases.
Los applets Java se transmiten como código aparte de la página Web.
En JavaScript, los applets se transmiten conjuntamente con la página web (embebidos en ella).

El entorno de desarrollo JDK

La herramienta básica para empezar a desarrollar aplicaciones o applets en Java es el JDK (Java Developer’s Kit) o Kit de Desarrollo Java, que consiste, básicamente, en un compilador y un intérprete (JVM) para la línea de comandos. No dispone de un entorno de desarrollo integrado (IDE), pero es suficiente para aprender el lenguaje y desarrollar pequeñas aplicaciones.

Expresiones en Java

Las expresiones son un conjunto de elementos o tokens junto con literales que son evaluados para devolver un resultado. Los tokens son elemento más pequeño de un programa que es significativo, e interpretado o entendido por el compilador, en Java los tokens se dividen en cinco categorías que son:
Identificadores: Son las representaciones que se les da a los nombres que se asignan a las variables, clases, paquetes, métodos y constantes en el código de Java para que el compilador los identifique y el programador pueda entenderlos. En Java los identificadores pueden diferenciar entre mayúsculas o minúsculas por ser case sensitive, por lo que la variable cuyo nombre sea “Mivariable”, no es igual a “mivariable”, ya que Java identifica estas como variables diferentes por el case sensitive, también se puede utilizar números, o el signo “_” para asignar un identificador.
Palabras claves: Son los identificadores reservados por java para cumplir con un objetivo específico en el código y el compilador, se usan de forma limitada y en casos específicos. Las palabras claves que usa Java son las siguientes:
Las palabras que se encuentran en negrilla, son palabras claves para Java aunque actualmente no se utilicen en la versión de Java, pero se pretenden integrar en las siguientes versiones de Java.
Literales y constantes: Los literales son sintaxis para asignar valores a una variable, es decir el valor que puede tomar una variable, también es un valor constante que puede ser de tipo numérico. Las constantes son variables que tienen un valor fijo y no puede ser modificado en el trascurso de la ejecución del código, estas se declaran por medio de los modificadores final y static.
final static double pi= 3.1416;
Operadores: Son los que nos indican una evaluación que se aplica a un objeto o un dato, sobre un identificador o constante. Un ejemplo de operadores puede ser la suma, resta o multiplicación.
Separadores: Se utilizan para indicarle al compilador de Java donde se ubican los elementos del código, los separadores que admite Java son: { },:;
También el compilador de Java identifica y elimina los comentarios, retornos de carros espacios vacíos y de tabulación a la hora de compilar por lo que no son considerados parte de un token.
Las expresiones pueden ser una combinación en secuencia de variables, operadores y métodos. Las expresiones son utilizadas para realizar cálculos, para asignar valores a variables, o para controlar la ejecución del flujo del programa.

Estructuras de Datos en Java

 Alta, adicionar un nuevo valor a la estructura. 
 Baja, borrar un valor de la estructura. 
 Búsqueda, encontrar un determinado valor en la estructura para ser realizar una operación con este valor, en forma SECUENCIAL o BINARIO (siempre y cuando los datos estén ordenados). 
Otras operaciones que se pueden realizar son:
 Ordenamiento, de los elementos pertenecientes a la estructura.
  Apareo, dadas dos estructuras originar una nueva ordenada y que contenga a las apareadas. 
Cada estructura ofrece ventajas y desventajas en relación a la simplicidad y eficiencia para la realización de cada operación. De esta forma, la elección de la estructura de datos apropiada para cada problema depende de factores como las frecuencias y el orden en que se realiza cada operación sobre los datos.
Algunas estructuras de datos utilizadas en Java son:
 Arrays (Arreglos) o Vectores  o Matrices
 Listas Enlazadas o Listas simples o Listas dobles o Listas Circulares
 Pilas
 Colas
 Árboles o Árboles binarios o Árboles Multicamino
 Conjuntos
 Grafos
 Montículos

Operadores en Java

Son las expresiones de Java que tras realizar una operación devuelven un resultado. Según el número de operandos que maneje un operador, puede ser de dos tipos: unario o binario.
Los operadores unarios son aquellos que solo necesitan un operando para devolver un valor. 
Los operadores binarios son aquellos que necesitan dos o más operandos para devolver un valor.
La siguiente tabla clasifica los operadores utilizados en Java según su nivel de precedencia, siendo arriba el nivel más alto:

Sentencias en Java

Las sentencias son una representación de una secuencia de acciones que se realizan en Java. La clave fundamental de las sentencias es su punto final que indica que ha finalizado la sentencia y puede continuar con la siguiente, el indicador utilizado es el signo de punto y coma (;). En Java contamos con sentencias que pueden ser de asignación, de bucles, de salto y condicionales. Las sentencias se conforman comúnmente por una instancia y un operador, un ejemplo es la sentencia de asignación que se conforma por una instancia de una variable, el signo de asignación y una expresión, un ejemplo es:
int variable = 12+2;
Las sentencias de asignación son aquellas en las que se asigna un valor a una variable o constante. Las sentencias condicionales son las que expresan una condición para definir el flujo de ejecución del programa, entre ellas tenemos if-else y switch. Las sentencias de bucles se encargar de realizar una acción cierta cantidad de tiempo dado, o hasta que se cumpla con una condición, entre ellas tenemos el while, do-while, y for. Las sentencias de salto llevan al compilador a un punto específico del programa o hacia la siguiente sentencia de ejecución, entre ellas tenemos break, continue, y return.

Conversión de tipos

En algunos casos suele ser necesario convertir un tipo de dato a otro, esto se le conoce como conversión de tipos, modelado, o tipado, así de esta forma poder realizar las operaciones necesarias sobre el valor que se desea convertir. Se debe tener en cuenta el tipo de dato que se va a convertir, ya que si se convierte un dato que tenga una cantidad menor de bit al anterior este tendrá perdida de información, un ejemplo de tipado puede ser un número long que se desea convertir a int, el compilador eliminara los primeros 32bit del long para ajustarlo al int ya que el int es de 32bit y el long de 64. Si la conversión se realiza a un tipo de datos de menos bit a un tipo de datos con mayor bit, la conversión se realiza automáticamente llamada conversión implícita, pero si se realiza de un tipo de datos con mayor bit a menor bit se tiene que realizar una conversión explícita, la cual se realiza con un casting, al usar este método se obliga a realizar la conversión por lo cual puede haber perdida de datos en la conversión. Para realizar una conversión explícita se tiene que poner el tipo de dato que se desea realizar la conversión entre paréntesis, luego el valor o la variable que se desea convertir. Un ejemplo de conversión de tipo explícito puede ser:
int numero1 = 32;
byte numero2;
numero2 = (byte) numero1;
Un ejemplo de una conversión de tipo implícita puede ser:
int numero1 = 32;
long numero2;
numero2 = numero1;

La siguiente tabla muestra los tipos de datos que se pueden realizar una conversión implícita desde el dato origen, hasta el dato destino que es el dato en el que se va a convertir.

El recolector de basura en Java

En Java el problema fugas de memoria se evita en gran medida gracias a la recolección de basura (o automatic garbage collector). El programador determina cuándo se crean los objetos y el entorno en tiempo de ejecución de Java (Java runtime) es el responsable de gestionar el ciclo de vida de los objetos. El programa, u otros objetos, pueden tener localizado un objeto mediante una referencia a éste. Cuando no quedan referencias a un objeto, el recolector de basura de Java borra el objeto, liberando así la memoria que ocupaba previniendo posibles fugas (ejemplo: un objeto creado y únicamente usado dentro de un método sólo tiene entidad dentro de éste; al salir del método el objeto es eliminado). Aun así, es posible que se produzcan fugas de memoria si el código almacena referencias a objetos que ya no son necesarios; es decir, pueden aún ocurrir, pero en un nivel conceptual superior. En definitiva, el recolector de basura de Java permite una fácil creación y eliminación de objetos y mayor seguridad.

Independencia de la plataforma

La segunda característica, la independencia de la plataforma, significa que programas escritos en el lenguaje Java pueden ejecutarse igualmente en cualquier tipo de hardware. Este es el significado de ser capaz de escribir un programa una vez y que pueda ejecutarse en cualquier dispositivo, tal como reza el axioma de Java, "write once, run anywhere".
Para ello, se compila el código fuente escrito en lenguaje Java, para generar un código conocido como “bytecode” (específicamente Java bytecode), instrucciones máquina simplificadas específicas de la plataforma Java. Esta pieza está “a medio camino” entre el código fuente y el código máquina que entiende el dispositivo destino. El bytecode es ejecutado entonces en la máquina virtual (JVM), un programa escrito en código nativo de la plataforma destino (que es el que entiende su hardware), que interpreta y ejecuta el código. Además, se suministran bibliotecas adicionales para acceder a las características de cada dispositivo (como los gráficos, ejecución mediante hebras o threads, la interfaz de red) de forma unificada. Se debe tener presente que, aunque hay una etapa explícita de compilación, el bytecode generado es interpretado o convertido a instrucciones máquina del código nativo por el compilador JIT (Just In Time).
Hay implementaciones del compilador de Java que convierten el código fuente directamente en código objeto nativo, como GCJ. Esto elimina la etapa intermedia donde se genera el bytecode, pero la salida de este tipo de compiladores sólo puede ejecutarse en un tipo de arquitectura.
La licencia sobre Java de Sun insiste en que todas las implementaciones sean “compatibles”. Esto dio lugar a una disputa legal entre Microsoft y Sun, cuando este último alegó que la implementación de Microsoft no daba soporte a las interfaces RMI y JNI además de haber añadido características ‘’dependientes’’ de su plataforma. Sun demandó a Microsoft y ganó por daños y perjuicios (unos 20 millones de dólares), así como una orden judicial forzando el acatamiento de la licencia de Sun. Como respuesta, Microsoft no ofrece Java con su versión de sistema operativo, y en recientes versiones de Windows, su navegador Internet Explorer no admite la ejecución de applets sin un conector (o plugin) aparte. Sin embargo, Sun y otras fuentes ofrecen versiones gratuitas para distintas versiones de Windows.
Las primeras implementaciones del lenguaje usaban una máquina virtual interpretada para conseguir la portabilidad. Sin embargo, el resultado eran programas que se ejecutaban comparativamente más lentos que aquellos escritos en C o C++. Esto hizo que Java se ganase una reputación de lento en rendimiento. Las implementaciones recientes de la JVM dan lugar a programas que se ejecutan considerablemente más rápido que las versiones antiguas, empleando diversas técnicas, aunque sigue siendo mucho más lentos que otros lenguajes.
La primera de estas técnicas es simplemente compilar directamente en código nativo como hacen los compiladores tradicionales, eliminando la etapa del bytecode. Esto da lugar a un gran rendimiento en la ejecución, pero tapa el camino a la portabilidad. Otra técnica, conocida como compilación JIT (Just In Time, o "compilación al vuelo"), convierte el bytecode a código nativo cuando se ejecuta la aplicación. Otras máquinas virtuales más sofisticadas usan una "recompilación dinámica" en la que la VM es capaz de analizar el comportamiento del programa en ejecución y recompila y optimiza las partes críticas. La recompilación dinámica puede lograr mayor grado de optimización que la compilación tradicional (o estática), ya que puede basar su trabajo en el conocimiento que de primera mano tiene sobre el entorno de ejecución y el conjunto de clases cargadas en memoria. La compilación JIT y la recompilación dinámica permiten a los programas Java aprovechar la velocidad de ejecución del código nativo sin por ello perder la ventaja de la portabilidad en ambos.
La portabilidad es técnicamente difícil de lograr, y el éxito de Java en ese campo ha sido dispar. Aunque es de hecho posible escribir programas para la plataforma Java que actúen de forma correcta en múltiples plataformas de distinta arquitectura, el gran número de estas con pequeños errores o inconsistencias llevan a que a veces se parodie el eslogan de Sun, "Write once, run anywhere" como "Write once, debug everywhere" (o “Escríbelo una vez, ejecútalo en cualquier parte” por “Escríbelo una vez, depúralo en todas partes”).
El concepto de independencia de la plataforma de Java cuenta, sin embargo, con un gran éxito en las aplicaciones en el entorno del servidor, como los Servicios Web, los Servlets, los Java Beans, así como en sistemas empotrados basados en OSGi, usando entornos Java empotrados.