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.