Download Programación Web III - Instituto Superior Tecnológico San Ignacio

Document related concepts
no text concepts found
Transcript
Manual de
Programación Web III
V CICLO COMPUTACIÓN E INFORMÁTICA
Instituto de Educación Superior
“San Ignacio de Monterrico”
Indice
1. Introduccion a java
2
2. Sintaxis del lenguaje java
14
3. Programacion orientada a objetos con java
50
4. Excepciones
77
5. Colecciones.
99
6. Conectandose a base de datos.
129
7. Diseño gui con awt y swing.
162
8. Sockets
179
Formando Emprendedores De Calidad Para Un Mundo Empresarial 2
Instituto de Educación Superior
“San Ignacio de Monterrico”
PROGRAMACIÓN WEB III
INTRODUCCION A JAVA
Historia
Java surgió en 1991 cuando un grupo de ingenieros de Sun Microsystems
trataron de diseñar un nuevo lenguaje de programación destinado a
electrodomésticos. La reducida potencia de cálculo y memoria de los
electrodomésticos llevó a desarrollar un lenguaje sencillo capaz de generar
código de tamaño muy reducido.
Debido a la existencia de distintos tipos de CPUs y a los continuos cambios, era
importante conseguir una herramienta independiente del tipo de CPU utilizada.
Desarrollaron un código “neutro” que no dependía del tipo de electrodoméstico, el
cual se ejecutaba sobre una “máquina hipotética o virtual” denominada Java
Virtual Machine (JVM). Era la JVM quien interpretaba el código neutro
convirtiéndolo a código particular de la CPU utilizada. Esto permitía lo que luego
se ha convertido en el principal lema del lenguaje: “Write Once, Run Everywhere”.
A pesar de los esfuerzos realizados por sus creadores, ninguna empresa de
electrodomésticos se interesó por el nuevo lenguaje.
Como lenguaje de programación para computadores, Java se introdujo a finales
de 1995. La clave fue la incorporación de un intérprete Java en la versión 2.0 del
programa Netscape Navigator, produciendo una verdadera revolución en Internet.
Java 1.1 apareció a principios de 1997, mejorando sustancialmente la primera
versión del lenguaje. Java 1.2, más tarde rebautizado como Java 2, nació a
finales de 1998.
Al programar en Java no se parte de cero. Cualquier aplicación que se desarrolle
“se apoya” en un gran número de clases preexistentes. Algunas de ellas hechas
por el
propio usuario, otras pueden ser comerciales, pero siempre hay un
número muy importante de clases que forman parte del propio lenguaje (el API o
Application Programming Interface de Java). Java incorpora en el propio lenguaje
muchos aspectos que en cualquier otro lenguaje son extensiones propiedad de
Formando Emprendedores De Calidad Para Un Mundo Empresarial 3
Instituto de Educación Superior
“San Ignacio de Monterrico”
empresas de software o fabricantes de ordenadores (threads, ejecución remota,
componentes, seguridad, acceso a bases de datos, etc.). Por eso Java es un
lenguaje de programación ideal porque incorpora todos estos conceptos de un
modo estándar, mucho más sencillo y claro que con las citadas extensiones de
otros lenguajes. Esto es consecuencia
de
haber sido diseñado más
recientemente y por un único equipo.
El principal objetivo del lenguaje Java es llegar a ser el “nexo universal” que
conecte a los usuarios con la información, esté ésta situada en el ordenador local,
en un servidor de Web, en una base de datos o en cualquier otro lugar.
En conclusión java es un lenguaje originalmente desarrollado por un grupo de
ingenieros de Sun, utilizado por Netscape posteriormente como base para
Javascript. Si bien su uso se destaca en el Web, sirve para crear todo tipo de
aplicaciones (locales, intranet o Internet).
¿Qué es java?
Java es un lenguaje de programación muy completo. La compañía Sun
describe el lenguaje Java como “simple, orientado a objetos, distribuido,
interpretado, robusto, seguro, de arquitectura neutra, portable, de altas
prestaciones, multitarea y dinámico”.
Los programas desarrollados en Java presentan diversas ventajas frente a los
desarrollados en otros lenguajes como C/C++. La ejecución de programas en
Java tiene muchas posibilidades: ejecución como aplicación independiente
(Stand-alone Application), ejecución como applet, ejecución como servlet, etc. Un
applet es una aplicación especial que se ejecuta dentro de un navegador o
browser (por ejemplo Netscape Navigator o Internet Explorer) al cargar una
página HTML desde un servidor Web. El applet se descarga desde el servidor y
no requiere instalación en el ordenador donde se encuentra el browser. Un
servlet es una aplicación sin interface gráfica que se ejecuta en un servidor de
Internet. La ejecución como aplicación independiente es análoga a los programas
desarrollados con otros lenguajes.
Formando Emprendedores De Calidad Para Un Mundo Empresarial 4
Instituto de Educación Superior
“San Ignacio de Monterrico”
Además de incorporar la ejecución como Applet, Java permite fácilmente el
desarrollo
tanto
de
arquitecturas
cliente-servidor
como
de
aplicaciones
distribuidas, consistentes en crear aplicaciones capaces de conectarse a otros
ordenadores y ejecutar tareas en varios ordenadores simultáneamente,
repartiendo por lo tanto el trabajo. Aunque también otros lenguajes de
programación permiten crear aplicaciones de este tipo, Java incorpora en su
propio API estas funcionalidades.
Características:
o Simple. Ofrece la funcionalidad de un lenguaje potente pero sin las
características menos usadas y más confusas de éstos. El ejemplo más
claro puede ser el recolector de basura, que evita que andemos
preocupándonos de liberar memoria. Otro ejemplo podría ser la supresión
de los punteros.
o Lenguaje de objetos.
¿Por qué "de" objetos y no "orientado a" objetos? Por que, al contrario de
otros lenguajes como C++, no es un lenguaje modificado para poder
trabajar con objetos sino que es un lenguaje creado para trabajar con
objetos desde cero. De hecho, TODO lo que hay en Java son objetos.
Java soporta las tres características básicas de la orientación a objetos:
encapsulación, herencia y polimorfismo.
o Distribuido. Proporciona las librerías y herramientas necesarias para que
las aplicaciones puedan ser distribuidas. Se ha desarrollado con extensas
capacidades de interconexión a red y soporta varios protocolos de red.
o Robusto.
Realiza
variadas
comprobaciones
tanto
en
tiempo
de
compilación como de ejecución. Entre ellas podemos mencionar la
comprobación de tipos y la comprobación de límites de arrays.
o Portable. Esto no se refiere a la independencia de la plataforma, si no a la
portabilidad en cuanto a desarrollo. Por ejemplo, los enteros son siempre
enteros de 32 bits en complemento a 2, con independencia de la
plataforma.
Formando Emprendedores De Calidad Para Un Mundo Empresarial 5
Instituto de Educación Superior
“San Ignacio de Monterrico”
o Multiplataforma. Como mencionamos antes, no es necesario recompilar
las aplicaciones Java para los distintos sistemas en que van a ser
explotadas.
o Multihilo. Permite múltiples hilos de ejecución, es decir, muchas
actividades simultáneas dentro del mismo programa. Las ventajas de esto
son un mejor rendimiento interactivo (el usuario no percibe tanto la
ocupación de la máquina) y un mejor comportamiento en tiempo real
(aunque sea algo muy limitado por el sistema operativo).
o Dinámico. Cuando una aplicación se lanza, no se cargan todas las
librerías que requiere, sino que la carga es bajo demanda. Las librerías
nuevas o actualizadas no paralizarán las aplicaciones en funcionamiento.
Plataformas de desarrollo
El software de Java contempla 3 plataformas para el desarrollo de aplicaciones:
o Java SE (Java Standard Edition)
Es el conjunto de herramientas software que permite el desarrollo y la
ejecución de programas Java destinados al lado cliente. Se le suele llamar
SDK (Software Development Kit) o Kit de Desarrollo de Programas. Es
gratuito y de libre distribución. Se puede descargar desde la página oficial
de Sun Microsystems relacionada con Java http://java.sun.com/.
Dentro de la plataforma Java SE, se incluye el compilador y la JVM (Java
Virtual Machine) o Máquina virtual de Java. También se la conoce como
Intérprete de Java. Cada plataforma tiene su propia versión. En la página
de Java dentro de Sun puede descargarse el Java SE para Windows,
Linux, Solaris, etc. Si se trabaja con sistemas operativos menos corrientes
como MacOS de Apple, AIX de IBM, HP-UX de Hewlett-Packard, etc.
o Java EE (Java Enterprise Edition)
Plataforma del lenguaje Java destinada al desarrollo de aplicaciones
empresariales estructuradas típicamente en tres capas: capa de
presentación de datos, capa de lógica de negocio y capa de datos
Formando Emprendedores De Calidad Para Un Mundo Empresarial 6
Instituto de Educación Superior
“San Ignacio de Monterrico”
persistentes (bases de datos). Se compone de un conjunto de estándares
y bibliotecas Java que permiten la creación de las aplicaciones
empresariales anteriormente citadas. Esta plataforma se utiliza sobre todo
para programación en servidores. Sin conocer lo fundamental de Java SE,
resulta inviable introducirse en Java EE.
o Java ME (Java Micro Edition)
Plataforma del lenguaje Java destinada al desarrollo de aplicaciones para
pequeños dispositivos móviles de memoria limitada, poca capacidad de
procesamiento y con interfaces gráficas limitadas. Típicamente teléfonos
móviles, PDAs (Personal Assistent Digital), Pockets PCs, televisiones,
relojes, sistemas de ayuda para automóviles, tarjetas, etc. Como en Java
EE, la base para programar mediante Java ME, es Java SE.
El Entorno De Desarrollo De Java
Existen distintos programas comerciales que permiten desarrollar código Java. La
compañía Sun, creadora de Java, distribuye gratuitamente el Kit de Desarrollo de
Java (JDK). Se trata de un conjunto de programas y librerías que permiten
desarrollar, compilar y ejecutar programas en Java.
Incorpora además la posibilidad de ejecutar parcialmente el programa,
deteniendo la ejecución en el punto deseado y estudiando en cada momento el
valor de cada una de las variables (con el denominado Debugger). Cualquier
programador con un mínimo de experiencia sabe que una parte muy importante
(muchas veces la mayor parte) del tiempo destinado a la elaboración de un
programa se destina a la detección y corrección de errores. Existe también una
versión reducida del JDK, denominada JRE (Java Runtime Environment)
destinada únicamente a ejecutar código Java (no permite compilar).
Los IDEs (Integrated Development Environment), tal y como su nombre indica,
son entornos de desarrollo integrados. En un mismo programa es posible escribir
el código Java, compilarlo y ejecutarlo sin tener que cambiar de aplicación.
Algunos incluyen una herramienta para realizar Debug gráficamente, frente a la
versión que incorpora el JDK basada en la utilización de una consola
Formando Emprendedores De Calidad Para Un Mundo Empresarial 7
Instituto de Educación Superior
“San Ignacio de Monterrico”
(denominada habitualmente ventana de comandos de MS-DOS, en Windows
NT/95/98) bastante difícil y pesada de utilizar. Estos entornos integrados permiten
desarrollar las aplicaciones de forma mucho más rápida, incorporando en muchos
casos librerías con componentes ya desarrollados, los cuales se incorporan al
proyecto o programa. Como inconvenientes se pueden señalar algunos fallos de
compatibilidad entre plataformas, y ficheros resultantes de mayor tamaño que los
basados en clases estándar.
Algunos IDEs que podemos citar son: Netbeans, Jdeveloper, Eclipse y JCreator
entre otros.
Bytecodes
Un programa C o C++ es totalmente ejecutable y eso hace que no sea
independiente de la plataforma y que su tamaño normalmente se dispare ya que
dentro del código final hay que incluir las librerías de la plataforma.
Proceso de compilación de un programa C++
Los programas Java no son ejecutables, no se compilan como los programas en
C o C++.
En su lugar son interpretados por una aplicación conocida como la máquina
virtual de Java (JVM). Gracias a ello no tienen porque incluir todo el código y
librerías propias de cada sistema.
Formando Emprendedores De Calidad Para Un Mundo Empresarial 8
Instituto de Educación Superior
“San Ignacio de Monterrico”
Previamente el código fuente en Java se tiene que precompilar generando un
código (que no es directamente ejecutable) previo conocido como bytecode o Jcode. Ese código (generado normalmente en archivos con extensión class) es el
que es ejecutado por la máquina virtual de Java que interpreta las instrucciones
de los bytecodes, ejecutando el código de la aplicación.
El bytecode se puede ejecutar en cualquier plataforma, lo único que se requiere
es que esa plataforma posea un intérprete adecuado (la máquina virtual de esa
plataforma).
La Java Virtual Machine
La existencia de distintos tipos de procesadores y ordenadores llevó a los
ingenieros de Sun a la conclusión de que era muy importante conseguir un
software que no dependiera del tipo de procesador utilizado. Se planteó la
necesidad de conseguir un código capaz de ejecutarse en cualquier tipo de
máquina. Una vez compilado no debería ser necesaria ninguna modificación por
el hecho de cambiar de procesador o de ejecutarlo en otra máquina. La clave
consistió en desarrollar un código “neutro” el cual estuviera preparado para ser
ejecutado sobre una “máquina hipotética o virtual”, denominada Java Virtual
Machina (JVM). Es esta JVM quien interpreta este código neutro convirtiéndolo a
código particular de la CPU utilizada. Se evita tener que realizar un programa
diferente para cada CPU o plataforma.
La JVM es el intérprete de Java. Ejecuta los “bytecodes” (ficheros compilados con
extensión *.class) creados por el compilador de Java (javac.exe). Tiene
numerosas opciones entre las que destaca la posibilidad de utilizar el
denominado JIT (Just-In-Time Compiler), que puede mejorar entre 10 y 20 veces
la velocidad de ejecución de un programa. La JVM, además es un programa muy
pequeño y que se distribuye gratuitamente para prácticamente todos los sistemas
operativos. A este método de ejecución de programas en tiempo real se le llama
Just in Time (JIT).
Formando Emprendedores De Calidad Para Un Mundo Empresarial 9
Instituto de Educación Superior
“San Ignacio de Monterrico”
Proceso de compilación de un programa Java
Compilación y ejecución de código java
Hay que entender que Java es estricto en cuanto a la interpretación de la
programación orientada a objetos. Así, se sobrentiende que un archivo java crea
una (y sólo) clase. Por eso al compilar se dice que lo que se está compilando es
una clase.
javac
La compilación del código java se realiza mediante el programa javac
incluido en el software de desarrollo de java. La forma de compilar es
(desde la línea de comandos):
javac archivo.java
El resultado de esto es un archivo con el mismo nombre que el archivo
java pero con la extensión class. Esto ya es el archivo con el código en
forma de bytecodes. Es decir con el código precompilado.
java
Si la clase es ejecutable (sólo lo son si contienen el método main), el
código se puede interpretar usando el programa java del kit de desarrollo.
Sintaxis:
java archivo.class
El compilador Java es una de las herramientas de desarrollo incluidas en
el JDK. Realiza un análisis de sintaxis del código escrito en los ficheros
fuente de Java (con extensión *.java). Si no encuentra errores en el código
Formando Emprendedores De Calidad Para Un Mundo Empresarial
10
Instituto de Educación Superior
“San Ignacio de Monterrico”
genera los ficheros compilados (con extensión *.class). En otro caso
muestra la línea o líneas erróneas. En el JDK de Sun dicho compilador se
llama javac.exe. Tiene numerosas opciones, algunas de las cuales varían
de una versión a otra. Se aconseja consultar la documentación de la
versión del JDK utilizada para obtener una información detallada de las
distintas posibilidades.
La compilación del código java se realiza mediante el programa javac
incluido en el software de desarrollo de java. La forma de compilar es
(desde la línea de comandos):
javac archivo.java
El resultado de esto es un archivo con el mismo nombre que el archivo
java pero con la extensión class. Esto ya es el archivo con el código en
forma de bytecodes. Es decir con el código precompilado.
Si la clase es ejecutable (sólo lo son si contienen el método main), el
código se puede interpretar usando el programa java del kit de desarrollo.
Sintaxis:
java archivo.class
Estos comandos hay que escribirlos desde la línea de comandos de en la
carpeta en la que se encuentre el programa. Pero antes hay que
asegurarse de que los programas del kit de desarrollo son accesibles
desde cualquier carpeta del sistema. Para ello hay que comprobar que la
carpeta con los ejecutables del kit de desarrollo está incluida en la variable
de entorno path.
Environment
JAVA
.java
javac
Java Byte-code
java
Java VM
.class
Compilación de un programa Java
Formando Emprendedores De Calidad Para Un Mundo Empresarial
11
Instituto de Educación Superior
“San Ignacio de Monterrico”
Estos comandos hay que escribirlos desde la línea de comandos en la
carpeta en la que se encuentre el programa. Pero antes hay que
asegurarse de que los programas del kit de desarrollo son accesibles
desde cualquier carpeta del sistema. Para ello hay que comprobar que la
carpeta con los ejecutables del kit de desarrollo está incluida en la variable
de entorno path.
Esto lo podemos comprobar escribiendo path en la línea de comandos. Si
la carpeta del kit de desarrollo no está incluida, habrá que hacerlo. Para
ello en Windows 2000, 2003 o XP:
1. Pulsar el botón derecho sobre Mi PC y elegir Propiedades
2. Ir al apartado Opciones avanzadas
3. Hacer clic sobre el botón Variables de entorno
4. Añadir a la lista de la variable PATH la ruta a la carpeta con los
programas del JDK.
5. Añadir la variable CLASSPATH si es que no existe y el valor a
asignarle seria un punto.
Ejemplo de contenido de la variable PATH:
PATH= C:\Archivos de programa\Java\jdk1.6.0_06\bin;
En negrita está señalada la ruta a la carpeta de ejecutables (carpeta bin)
del kit de desarrollo. Está carpeta varía según la instalación del JDK
Ejemplo de contenido de la variable CLASSPATH:
CLASSPATH = .;
Javadoc
Javadoc es una herramienta muy interesante del kit de desarrollo de Java
para
generar
automáticamente
documentación
Java.
Genera
documentación para paquetes completos o para archivos java. Su sintaxis
básica es:
javadoc archivo.java o paquete
Formando Emprendedores De Calidad Para Un Mundo Empresarial
12
Instituto de Educación Superior
“San Ignacio de Monterrico”
El funcionamiento es el siguiente. Los comentarios que comienzan con los
códigos /** se llaman comentarios de documento y serán utilizados por los
programas de generación de documentación javadoc.
Tipos de archivos
•
java: Es la extensión de los archivos fuente del lenguaje java, ejem.
bienvenido.java, Miapplet.java.
•
class: El resultado de compilar un archivo fuente java es un archivo en bytecode con extensión class, ejem. bienvenido.class, Miapplet.class. El comando
usado para compilar los arhivos con extensión java es javac y para su
ejecución el comando java que vienen incluidos en el JDK (Java Development
Kit) de Sun Microsystems.
•
jar/zip: Las clases desarrolladas en java suelen por lo general guardarse en
estos tipos de archivos comprimidos.
Ejercicio
//archivo con el nombre Bienvenido.java
class Bienvenido {
public static void main(String [] args) {
System.out.println("Bienvenido a Java!");
}
}
Grabar el archivo en la carpeta Demos01 con el nombre Bienvenido.java
luego compilar y ejecutar el programa anterior, con los programas javac y
java respectivamente.
javac Bienvenido.java
java Bienvenido
Línea 1: es un simple comentario de tipo línea en el que hemos colocado
el nombre del fichero. El compilador ignorará todo lo que va desde los
caracteres “//” hasta el final de la línea. Los comentarios son de mucha
utilidad, tanto para otras personas que tengan que revisar el código, como
para nosotros mismos en futuras revisiones.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
13
Instituto de Educación Superior
“San Ignacio de Monterrico”
Línea 2: declara el nombre de la clase. Usamos la palabra reservada class
seguida del nombre que queremos darle a nuestra clase, en este caso es
“Bienvenido”. Ya sabemos que Java es un lenguaje orientado a objetos,
por tanto, nuestro programa ha de ser definido como una clase. Por
convención, las clases Java se definen con la primera letra en
mayúsculas.
Línea 3: declara el método principal (main) utilizando el modificador de
acceso public (acceso publico), el modificador static y el tipo que retorna
es void (nada) y como parámetro recibe un arreglo de Strings.
Linea 4: se invoca al método println(param) que Escribe el valor de
param y luego se produce un cambio de línea que equivale a pulsar
retornar, al final de toda sentencia se debe colocar el punto y coma (;) que
indica el fin de la sentencia.
Linea 5 y 6: Se termina el cuerpo del método principal y de la clase.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
14
Instituto de Educación Superior
“San Ignacio de Monterrico”
SINTAXIS DEL LENGUAJE JAVA
Consideraciones
Todo el código fuente Java se escriben en documentos de texto con extensión
.java. Al ser un lenguaje para Internet, la codificación de texto debía permitir a
todos los programadores de cualquier idioma escribir ese código. Eso significa
que Java es compatible con la codificación Unicode.
En la práctica significa que los programadores que usen lenguajes distintos del
inglés no tendrán problemas para escribir símbolos de su idioma. Y esto se
puede extender para nombres de clase, variables, etc.
La codificación Unicode2 usa 16 bits (2 bytes por carácter) e incluye la mayoría
de los códigos del mundo.
•
Los archivos con código fuente en Java deben guardarse con la
extensión .java. Como se ha comentado cualquier editor de texto se puede
usar para codificar un archivo.
•
En java el código es sensitivo (case sensitive) es decir existe diferencias
entre mayúsculas y minúsculas.
•
Cuando se programa en Java, se coloca todo el código en métodos.
•
Cada línea de código debe terminar con punto y coma (;) que indica el fin
de la sentencia.
•
Los comentarios; si son de una línea deben comenzar con: “//” y si ocupan
más de una línea deben comenzar con “/*” y terminar con “*/”
•
A veces se marcan bloques de código, los cuales comienza con { y
terminan con }
Formando Emprendedores De Calidad Para Un Mundo Empresarial
15
Instituto de Educación Superior
“San Ignacio de Monterrico”
Ejercicio
public class app004
{
public static void main(String[] args)
{
//Comentario de una línea
System.out.println(“¡Programacion Web III!”);
/*
Comentarios de Varias Lineas
Sidem
Carlos Enrique
*/
}
}
Este código escribe “¡Programacion Web III!” en la pantalla. El archivo
debería llamarse app004.java ya que esa es la clase pública. El resto
define el método main que es el que se ejecutará al lanzarse la aplicación.
Ese método utiliza la instrucción que escribe en pantalla; y también se
utilizan los comentarios de una y varias líneas.
Comentarios
En java al igual que en otros lenguajes existen los comentarios y estos pueden
ser considerados como un caso especial dentro de los elementos de la sintaxis
del lenguaje, ya que aunque estos sean reconocidos por el compilador, éste los
ignora.
En Java existen tres tipos de comentarios.
//
Comentario de una línea.
/*
Comentario de
Varias líneas.
*/
/**
Comentario de Documentación
Formando Emprendedores De Calidad Para Un Mundo Empresarial
16
Instituto de Educación Superior
“San Ignacio de Monterrico”
*
Esta aplicación realiza lo siguiente: …
*
@autor Carlos Durand Flores
*
@version 1.0
*/
Los dos primeros tipos de comentarios son los que todo programador conoce y
se utilizan del mismo modo. Los comentarios de documentación, indican que ese
comentario ha de ser colocado en la documentación que se genera
automáticamente cuando se utiliza la herramienta de Java, javadoc. Dichos
comentarios sirven como descripción del elemento declarado permitiendo generar
una documentación de nuestras clases escrita al mismo tiempo que se genera el
código.
Los comentarios javadoc comienzan con el símbolo /** y terminan con */ Cada
línea javadoc se inicia con un símbolo de asterisco. Dentro se puede incluir
cualquier texto. Incluso se pueden utilizar códigos HTML para que al generar la
documentación se tenga en cuenta el código HTML indicado.
En este tipo de comentario para documentación, se permite la introducción de
algunos tokens, palabras clave o etiquetas especiales que harán que la
información que les sigue aparezca de forma diferente al resto en la
documentación, estos comienzan con el símbolo @ y pueden ser:
o @author. Tras esa palabra se indica el autor del documento.
o @version. Tras lo cual sigue el número de versión de la aplicación.
o @see. Tras esta palabra se indica una referencia a otro código Java
relacionado con éste.
o @since. Indica desde cuándo esta disponible este código.
o @deprecated. Palabra a la que no sigue ningún otro texto en la línea y
que indica que esta clase o método esta obsoleta u obsoleto.
o @throws. Indica las excepciones que pueden lanzarse en ese código.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
17
Instituto de Educación Superior
“San Ignacio de Monterrico”
o @param. Palabra a la que le sigue texto qué describe a los parámetros
que requiere el código para su utilización (el código en este caso es un
método de clase). Cada parámetro se coloca en una etiqueta @param
distinta, por lo que puede haber varios @param. Para el mismo método.
o @return. Tras esta palabra se describe los valores que devuelve el
código (el código en este caso es un método de clase)
El código javadoc hay que colocarle en tres sitios distintos dentro del código java
de la aplicación:
1. Al principio del código de la clase (antes de cualquier código Java). En
esta zona se colocan comentarios generales sobre la clase o interfaz que
se crea mediante el código Java. Dentro de estos comentarios se pueden
utilizar las etiquetas: @author, @version, @see, @since y @deprecated
2. Delante de cada método. Los métodos describen las cosas que puede
realizar una clase. Delante de cada método los comentarios javadoc se
usan para describir al método en concreto. Además de los comentarios, en
esta zona se pueden incluir las etiquetas: @see, @param, @exception,
@return, @since y @deprecated
3. Delante de cada atributo. Se describe para qué sirve cada atributo en cada
clase. Puede poseer las etiquetas: @since y @deprecated
Ejercicio
/** Esto es un comentario para probar el javadoc
* este texto aparecerá en el archivo HTML generado.
* <strong>Realizado en agosto 2008</strong>
*
* @author Carlos Durand
* @version 1.0
*/
Formando Emprendedores De Calidad Para Un Mundo Empresarial
18
Instituto de Educación Superior
“San Ignacio de Monterrico”
public class app005 {
//Este comentario no aparecerá en el javadoc
/** Este método contiene el código ejecutable de la clase
*
* @param args Lista de argumentos de la línea de comandos
* @return void
*/
public static void main(String args[]){
System.out.println("¡Mi aplicación app005! ");
}
}
Tras ejecutar la aplicación javadoc, aparece como resultado una página
web donde aparecerá la documentación de nuestra clase implementada..
Bloques y ámbitos
Bloques
Al igual que C, Java utiliza las llaves ({} la primera de inicio y la otra de fin
de bloque) para determinar los bloques dentro de un programa. Todo lo
que se encuentra entre estas dos llaves se considera un bloque. Los
bloques son parte de la sintaxis del lenguaje.
Los bloques pueden y suelen anidarse; utilizándose la sangría para
clarificar el contenido de un bloque. De este modo, el bloque más externo
se sitúa al margen izquierdo del fichero de código fuente, y cada vez que
se anida un bloque se indenta (sangra) el texto que lo contienen un
número determinado de columnas, normalmente tres o cuatro.
El sangrado no tiene ninguna utilidad para el compilador, pero sin ella la
lectura del código por parte del programador es casi imposible.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
19
Instituto de Educación Superior
“San Ignacio de Monterrico”
Ámbitos
Los bloques además, definen los ámbitos de las variables. El ámbito se
refiere a la longevidad de las variables.
Una variable existe sólo dentro del bloque donde ha sido declarada,
eliminándola el compilador una vez que se sale de dicho bloque. Esto es
cierto para todos los tipos de datos de Java. Si el dato es un objeto, y su
clase tiene un destructor asociado (distinto al destructor por defecto, que
es interno y Java lo maneja por sí solo) este es invocado al salir la variable
de ámbito.
Cuando una variable sale de ámbito, es eliminada y la memoria que ésta
ocupaba es liberada por el recolector de basura (garbage collector).
Identificadores
Los identificadores sirven para nombrar variables, propiedades, métodos,
funciones, clases, interfaces y objetos; o cualquier entidad que el programador
necesite identificar para poder usar. Estos identificadores sirven de mucho al
programador, si éste les da sentido.
En Java, un identificador comienza con una letra, un subrayado (_) o un símbolo
de dólar ($). Los siguientes caracteres pueden ser letras o dígitos. No existe una
longitud máxima y se distinguen las mayúsculas de las minúsculas, de este modo
el compilador puede identificarlos unívocamente.
Por ejemplo, si vamos a implementar una clase para manejo del fichero
de
clientes, sería recomendable que la misma se llamara algo así como
ClientManager, o ManejadordeCliente, no tendría sentido, aunque es totalmente
válido llamarla Aj23.
Existe una serie de palabras reservadas las cuales tienen un significado
especial para Java y por lo tanto no se pueden utilizar como nombres
identificadores. Dichas palabras son:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
20
Instituto de Educación Superior
“San Ignacio de Monterrico”
abstract
char
double
for
int
package
static
throws
byvalue
boolean
class
else
goto*
interface
private
super
transient
threadsafe
break
const*
extends
if
long
protected
switch
try
false
byte
continue
final
implements
native
public
synchronized
void
true
case
default
finally
import
new
return
this
volatile
catch
do
float
instanceof
null
short
throw
while
(*) Son palabras reservadas, pero no se utilizan en la actual implementación del
lenguaje Java.
Además, el lenguaje se reserva unas cuantas palabras más, pero que hasta
ahora no tienen un cometido específico. Son:
cast
Var
future
outer
generic
inner
operator
rest
Serían identificadores válidos:
nombre_usuario
Nombre_Usuario
_variable_del_sistema
$transaccion
y su uso sería, por ejemplo:
int contador_principal;
char _lista_de_ficheros;
float $cantidad_en_Ptas;
Tipos de datos primitivos
Estos tipos de datos simples o también llamados tipos primitivos, permiten
declarar un variable de tipo primitiva que reserva el espacio de memoria
necesario.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
21
Instituto de Educación Superior
“San Ignacio de Monterrico”
<tipo> <idetificador1> [= <valor1>] [, <identificadorN> [= <valorN>]];
O bien, con asignación múltiple:
<tipo> <idetificador1>,... ,<identificadorN> = <valor>;
•
Byte
Entero muy pequeño.
•
Short
Entero Corto.
•
Int
Entero normal.
•
Long
Entero largo.
•
Char
Carácter.
•
Float
Número real de precisión simple.
•
Double
Número real de doble precisión.
•
Boolean Valor lógico.
•
Void
Tipo Vació.
De no asignarse un valor de inicio, el compilador por defecto asignará uno.
Tipos
Precisión
Valor Por Defecto
Byte
8 bits
0
Short
16 bits
0
Int
32 bits
0
Long
64 bits
0
Char
16 bits
\u0000
Float
32 bits
+0.0f
Double
64 bits
+0.0d
Boolean
-
false
Enteros
Como en otros lenguajes, Java maneja el tipo de datos entero, y con
distintos tamaños y capacidades. Es uno de los tipos que la máquina
virtual de Java maneja de forma directa. Cada implementación de la
Formando Emprendedores De Calidad Para Un Mundo Empresarial
22
Instituto de Educación Superior
“San Ignacio de Monterrico”
máquina virtual debe cumplir con una serie de reglas de manipulación de
enteros (y otros tipos primitivos) que aseguran que nuestros programas
Java se comporten de la misma forma en distintas plataformas.
Esto asegura la portabilidad de Java. En otros lenguajes, como por
ejemplo C o C++, el tamaño y capacidades de los enteros, varía de
sistema operativo a sistema operativo, y aún dentro de la misma
plataforma, de fabricante de compilador a otro. Eso ha provocado que la
portabilidad de muchos programas C/C++ se haya visto dificultada por este
estado de cosas. Si bien se ha avanzado mucho en solucionar el
problema, los diseñadores del lenguaje Java se plantearon desde el
comienzo solucionarlo.
Para eso adoptaron un formato estándar de los tipos primitivos para todo
Java, independiente de la máquina donde se ejecute.
Hay cuatro tipos enteros en Java:
byte
Valores que pueden tener entre -128 a +127, y ocupan como era de
esperarse un byte (8 bits) en memoria
short
Valores entre -32768 a 32767, que ocupan 2 bytes (16 bits) en memoria
int
El tipo entero normal, con un rango de valores de entre -2147483648 a
2147483647, que ocupa 4 bytes (32 bits) en memoria
long
El tamaño más grande de entero que maneje Java, con valores entre
-9223372036854775808 hasta 9223372036854775807. Ocupan 8 bytes
(64 bits).
Algunos ejemplos de declaraciones:
byte cantidadAhorrada;
short cantidadClientes;
int cantidadDeudas;
Formando Emprendedores De Calidad Para Un Mundo Empresarial
23
Instituto de Educación Superior
“San Ignacio de Monterrico”
long deudaExterna;
Debemos tratar ahora el tema de escribir, en el código, valores
correspondientes a los tipos enteros.
Los literales enteros (recordemos que esto incluye a los byte y a los short,
así como los int y long), se escriben en dígitos decimales, octales o
hexadecimales. Espero que Ud. esté familiarizado con los primeros (en
caso contrario, deberá repasar su cultura general). Como expresiones
decimales válidas tenemos:
10
123
Las expresiones octales (en base 8), comienzan con un dígito 0 (cero), y
las hexadecimales (en base 16, con dígitos 0-9 y a-f o A-F) con un 0x o 0X
(Cero y equis). Las siguientes expresiones entonces son equivalentes:
12 014 0x0c 0x0C
Cuando un literal entero termina con l o L (letra ele), es un entero largo (del
tipo long). En caso contrario, se toma de tipo int (entero simple). Una
constante int se puede asignar a short o byte directamente, si es posible
acomodarla en ese tipo.
Podemos declarar una variable y darle un valor inicial, en la misma
sentencia:
long numeroGrande = 99999999L;
El valor a asignar se coloca luego de un signo de igual. Es común poner un
valor inicial a una variable local a un método. En caso de no hacerlo, si
usamos la variable sin valor inicial, el compilador nos advierte del
problema. El siguiente código, inserto en un método, no compilaría:
int k;
k=k+1;
Formando Emprendedores De Calidad Para Un Mundo Empresarial
24
Instituto de Educación Superior
“San Ignacio de Monterrico”
Pueden declararse varias variables en la misma sentencia, y hasta
asignárseles valores:
int x, y;
int xInicial = 0, yInicial = 0;
Como el compilador no toma como especiales los cambios de línea,
podemos escribir este tipo de declaraciones de la forma:
int xInicial = 0, yInicial = 0, zInicial = 0;
Los valores del tipo byte o short, se inicializan de manera similar:
byte unOcteto = 7;
short unValor = 2300;
Notemos que no hizo falta declarar al 7 como byte, o al 2300 como entero
corto. El compilador se encarga de tomarlos como valores byte y short,
respectivamente.
Java no solamente especifica la forma de tratar a los enteros, sino también
cómo se graban o serializan en algún medio externo. Esto hace posible
que lo grabemos en un archivo como entero, luego lo podamos recuperar
de la misma forma, aún desde otra plataforma. Esto no es así de fácil en
otros lenguajes: muchos compiladores C graban enteros de una forma en
la plataforma Motorola, y de otra forma en plataforma Intel, aún cuando
sean del mismo tamaño. Los de formato Intel, se graban con el byte menos
significativo primero, y en el formato Motorola, se graban en orden inverso
al de Intel.
Números en coma flotante
En Java, como en otros lenguajes, se manejan también números reales,
en los formatos de punto flotante. Estos números no son enteros, tienen
distinta precisión, y su rango de valores es mayor. Se guardan como los
enteros, en formato binario, así que su representación decimal es
aproximada (en cuanto a la parte no entera).
Formando Emprendedores De Calidad Para Un Mundo Empresarial
25
Instituto de Educación Superior
“San Ignacio de Monterrico”
float
Pueden albergar valores desde -3.4E38 hasta +3.4E38, ocupando 4 bytes
en memoria. Tienen una precisión aproximada de 7 dígitos.
double
Contienen valores desde -1.7E308 hasta +1.7E308, consumiendo 8 bytes
en la memoria. La precisión aproximada es de 17 dígitos. El menor valor
almacenable está cercano a los +- 4.9E-324.
Las operaciones sobre los valores de punto flotante, siguen las reglas de
estándard IEEE 754. Notamos que estos valores pueden manejar un
exponente en base 10. Valores válidos son:
1.0
3.1415
1.0f
99.10F
Los dos primeros se asumen de tipo double, mientras los segundos, al
terminar en f o F, el compilador los procesa como del tipo float. El
separador decimal es el punto. En caso de necesitar expresar un
exponente en base 10, se usa la letra E seguida del exponente:
314e-2
0.314E1
Para declarar variables de tipo flotante, se sigue la misma convención que
en las variables de tipo entero:
double valorReal;
double deuda = 1.496E8;
float sueldo = 1.2E-2f;
Como antes, podemos declarar más de una variable en la misma
sentencia:
float valor1 = 190.2f,
Formando Emprendedores De Calidad Para Un Mundo Empresarial
26
Instituto de Educación Superior
“San Ignacio de Monterrico”
valor2 = 192.3f;
Booleanos
Los valores booleanos (o lógicos) sirven para indicar si algo es verdadero
(true) o falso (false). En C se puede utilizar cualquier valor lógico como si
fuera un número; así verdadero es el valor -1 y falso el 0. Eso no es
posible en Java.
Si a un valor booleano no se le da un valor inicial, se toma como valor
inicial el valor false. Por otro lado, a diferencia del lenguaje C, no se
pueden en Java asignar números a una variable booleana (en C, el valor
false se asocia al número 0, y cualquier valor distinto de cero se asocia a
true).
Caracteres
Los valores de tipo carácter sirven para almacenar símbolos de escritura
(en Java se puede almacenar cualquier código Unicode). Los valores
Unicode son los que Java utiliza para los caracteres. Ejemplo:
char letra;
letra=’C’; //Los caracteres van entre comillas
letra=67; //El código Unicode de la C es el 67. Esta línea
//hace lo mismo que la anterior
Cadena de caracteres
Más conocidas por el nombre de Strings, no es un tipo primitivo de dato: es
en realidad una clase, la clase String. Sin embargo, se pueden escribir
constantes de este tipo, encerrándolas entre comillas dobles. Pueden
contener todo tipo de caracteres, pudiendo ser algunas secuencias
'escape' como las vistas más arriba:
"Hola, mundo\n"
"Esta frase tiene \"comillas dobles\" y \'comillas simples\'"
Formando Emprendedores De Calidad Para Un Mundo Empresarial
27
Instituto de Educación Superior
“San Ignacio de Monterrico”
La doble comilla inicial y la final de un String deben estar en la misma línea
del código fuente. Como hemos visto en algunos ejemplos, el operador +
sirve para concatenar strings. Si uno de los dos operandos del + no es un
String, lo convierte automáticamente a una cadena de caracteres.
Variables
Una variable es un nombre que contiene un valor que puede cambiar a lo largo
del programa es decir representan direcciones de memoria en las que podemos
alojar temporalmente la información que necesitemos, y por otro, nos ofrecen la
facilidad de referirnos a ellas mediante un nombre.
Las variables son los contenedores de los datos que utiliza un programa. Cada
variable ocupa un espacio en la memoria RAM del ordenador para almacenar un
dato determinado.
Una declaración de variable se compone de dos partes: su tipo y su nombre.
Adicionalmente puede indicarse un valor para su inicialización. El tipo de la
variable determina los valores que puede contener y las operaciones que se
podrán realizar con ella. Según el tipo de información que contienen pueden ser
variables de tipos primitivos o variables de referencia.
Las variables tienen un nombre (un identificador) que sólo puede contener letras,
números y el carácter de subrayado (también vale el símbolo $). El nombre
puede contener cualquier carácter Unicode.
Desde el punto de vista del papel que desempeñan, las variables pueden ser:
•
Variable miembro de una clase: Se definen dentro de la clase y fuera de
cualquier método.
•
Variables locales: Se definen dentro de un método o, más en general,
dentro de cualquier bloque entre llaves ({ }). Se crean dentro del bloque y
se destruyen al finalizar dicho bloque.
Declaración de variables
Formando Emprendedores De Calidad Para Un Mundo Empresarial
28
Instituto de Educación Superior
“San Ignacio de Monterrico”
Antes de poder utilizar una variable, ésta se debe declarar. Lo cual se
debe hacer de esta forma:
TipoDeDato NombreVariable;
Donde tipo es el tipo de datos que almacenará la variable (texto, números
enteros,...) y nombrevariable es el nombre con el que se conocerá la
variable.
Ejemplo:
int dias;
boolean decision;
También se puede hacer que la variable tome un valor inicial al declarar:
int dias=365;
Y también se puede declarar más de una variable a la vez:
int dias=365, anio=23, semanas;
Al declarar una variable se puede incluso utilizar una expresión:
int a=13, b=18;
int c=a+b;
Alcance o ámbito
Se denomina visibilidad, ámbito o alcance de una variable, a la parte de
una aplicación en la que dicha variable es accesible. En otras palabras, la
parte en la cual puede usarse dicha variable es decir hace referencia a la
duración de una variable. Como norma general, podemos decir que las
variables declaradas dentro de un bloque (entre llaves) son visibles y
existen dentro de ese bloque. Por ejemplo, las variables declaradas al
principio de una función existen mientras se ejecute la función, las
declaradas dentro de un bloque “if” sólo serán accesibles y válidas dentro
de dicho bloque y las variables miembro de una clase son válidas mientras
existe el objeto.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
29
Instituto de Educación Superior
“San Ignacio de Monterrico”
En el ejemplo:
if(z>1)
{
int x=12;
}
System.out.println(x); //Error
Existe un error, porque la variable se usa fuera del bloque en el que se
creo. Eso no es posible, porque una variable tiene como ámbito el bloque
de código en el que fue creada (salvo que sea una propiedad de un
objeto).
Variables Miembro
Las variables miembro de una clase declarada como pública (public),
serán accesibles mediante una referencia a un objeto de dicha clase
usando el operador punto. Por su parte, las declaradas como privadas
(private) no son accesibles desde otras clases.
Las funciones miembro de una clase tienen acceso directo a todas las
variables miembro de la clase sin necesidad de anteponer el nombre de un
objeto de la clase. Sin embargo, las funciones miembro de una clase “B”
derivada de otra “A”, tienen acceso a todas las variables miembro de “A”
declaradas como públicas o protegidas (protected), pero no a las
declaradas como privadas.
Una clase hija sólo puede acceder directamente a las variables y función
miembro de su clase padre declaradas como públicas o protegidas.
Otra característica del lenguaje Java consiste en que es posible declarar
una variable dentro de un bloque con el mismo nombre que una variable
miembro, pero no con el nombre de otra variable local que ya existiera. La
variable declarada dentro del bloque oculta a la variable miembro en ese
bloque. Para acceder a la variable miembro oculta sería necesario que
usáramos el operador this.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
30
Instituto de Educación Superior
“San Ignacio de Monterrico”
Sintaxis:
[modificador] <Tipo> <nombreDeVariable> [= <inicializador>];
Convenciones y nomenclatura
Con respecto a los nombres de variables, las reglas del lenguaje Java son muy
amplias y permiten mucha libertad, pero es habitual seguir ciertas normas que
faciliten la lectura y el mantenimiento de los programas.
Por convención, se recomienda seguir las siguientes reglas:
o Normalmente se emplean nombres con minúsculas salvo las excepciones
que se enumeran en los siguientes puntos.
o En los nombres que se componen de varias palabras es aconsejable
colocar una detrás de otra poniendo en mayúscula la primera letra de cada
palabra.
o Los nombres de las clases y las interfaces empiezan siempre por
mayúscula.
o Los nombres de objetos, métodos y variables empiezan siempre por
minúscula.
o Los nombres de las variables finales (las constantes) se definen siempre
con mayúsculas.
Ejercicio #
class Circunferencia { // Es una clase
private final double PI = 3.14159; // Es una variable final
private double elRadio; // Es una variable miembro de la clase
public void establecerRadio(double radio) {
// Son un método y una variable local
elRadio = radio;
}
public double calcularLongitud() {
// Es un método
Formando Emprendedores De Calidad Para Un Mundo Empresarial
31
Instituto de Educación Superior
“San Ignacio de Monterrico”
return (2 * PI * elRadio);
}
}
Modificadores
Los modificadores son elementos del lenguaje que se colocan delante de la
definición de variables locales, dato miembro, métodos o clases y que alteran o
condicionan el significado del elemento. Dentro de este grupo de modificadores
tenemos un subgrupo denominado modificadores de acceso.
Modificadores de Acceso
public (Acceso libre)
El campo es accesible de forma general sin restricciones. Significa que
toda definición será accesible de cualquier punto, ya sea un método,
campo o clase; Es decir indica que el elemento puede ser utilizado dentro
de la clase, dentro de las clases descendientes y desde cualquier objeto
de la clase.
protected (Clases Heredadas y misma Clase)
El campo es accesible desde cualquier subclase (tanto dentro como fuera
del paquete) y a todo método dentro del paquete. Es decir indica que el
elemento puede ser utilizado dentro de la clase, dentro de las clases
descendientes, mas no desde un objeto de la clase.
private (Solo en la misma Clase)
El calificador private indica que dicho componente será accesible
únicamente dentro de la Clase en cuestión, si se intenta accesar cualquier
elemento de este tipo dentro de otra Clase será generado un error de
compilación.
El calificador private suele utilizarse en Clases que serán modificadas
continuamente, esto permite evitar futuros quebrantos en otras Clases
como fue mencionado al inicio.
default (Clase en Librería y misma Clase)
Formando Emprendedores De Calidad Para Un Mundo Empresarial
32
Instituto de Educación Superior
“San Ignacio de Monterrico”
Tambien denominado Package o tambien denominado Friendly, El campo
es accesible a todos los métodos dentro del paquete. Es el acceso por
defecto.
Cuando no es empleado ninguno de los calificadores de acceso
mencionados anteriormente los elementos son considerados amigables,
esto implica que todo campo/método carente de calificador será accesible
dentro de todas Clases pertenecientes a su misma librería ("package").
Modificadores
static
Una de los posibles usos del modificador static es compartir el valor de una
variable miembro entre objetos de una misma clase. Si declaramos una
variable miembro de una clase, todos los objetos que declaremos
basandonos en esa clase compartiran el valor de aquellas variables a las
que se les haya aplicado el modificador static, y se podrá modificar el valor
de este desde todas.
Un caso en el que nos podría ser muy util este modificador, es en una
clase que nos diga la cantidad de objetos que se han creado basandose
en ella. Podemos escribir una línea de código en el constructor que
incremente la variable contador con el modificador static, y así cada vez
que se declare un objeto el contador se incrementará. Desde cualquier
objeto podremos consultar el valor del contador. El código sería algo así.
class Clase {
static int contador;
Clase() {
contador++;
}
int getContador() {
return contador;
}
}
class Codigo {
Formando Emprendedores De Calidad Para Un Mundo Empresarial
33
Instituto de Educación Superior
“San Ignacio de Monterrico”
public static void main(String[] args) {
Clase uno = new Clase();
Clase dos = new Clase();
Clase tres = new Clase();
Clase cuatro = new Clase();
System.out.println("Hemos declarado" + uno.getContador() + "
objetos.");
}
}
Esto tiene varias ventajas, porque además de ahorrarnos algunas
posiciones de memoria (porque todos objetos comparten la misma)
podemos crear variables compartidas, cosa que abre la puerta a diversas
posibilidades de uso.
final
El modificador final se aplica a clases, métodos y variables. El significado
de final varia de contexto a contexto, pero la idea en si es la misma, la
característica no puede ser cambiada. Esto quiere decir que de una clase
final no se puede derivar otra clase, una variable final no puede ser
modificada una vez que se le ha asignado el valor y un método final no
puede ser sobreescrito.
Aunque una variable final no puede ser cambiada, su contenido puede ser
modificado. Si esta variable apunta a un objeto, es perfectamente válido
modificar las propiedades del objeto.
abstract
El modificador abstract puede ser aplicado a clases y métodos. Una clase
que es abstracta no puede ser instanciada (esto es, no se puede llamar a
su constructor). Las clases abstractas se pueden utilizar como mecanismo
para trasladar la implementación de los métodos a la subclase. De esta
forma, para poder utilizar una clase abstracta se tiene que derivar y se
tienen que implementar los métodos abstractos definidos en ellas. El
compilador obligará a implementar todos los métodos abstractos de la
clase o declarar la clase derivada abstracta. Si una clase tiene uno o más
métodos abstractos el compilador insiste en que se debe declarar
Formando Emprendedores De Calidad Para Un Mundo Empresarial
34
Instituto de Educación Superior
“San Ignacio de Monterrico”
abstracta. También debe ser abstracta la clase si hereda uno o más
métodos abstractos para el cual no provee implementación o si la clase
declara una interfase pero no proporciona implementaciones para cada
uno de sus métodos.
native
Este modificador solo puede referirse a métodos, indica que el cuerpo de
un método se encuentra fuera de la máquina virtual de Java, en una
librería. El código nativo esta escrito en otro lenguaje de programación,
típicamente C o C++ y compilado para una sola plataforma por lo que la
independencia de plataforma de Java es violada cuando utilizamos este
tipo de métodos.
transient
El modificador transient es solo aplicado a variables. Una variable transient
no es almacenada como parte del estado persistente de su objeto.
Muchos objetos que son declarados con interfases Serializable o
Externalizable pueden tener su estado serializado y escribir a destinos
fuera de la máquina virtual. Esto se logra pasando el objeto a el método
writeObject() de la clase ObjectOutputStream. Si el objeto es pasado a un
FileOutputStream, entonces el estado del objeto es escrito en un fichero.
Si el objeto es pasado a un socket OutputStream, entonces el estado del
objeto será escrito en una red. En ambos casos el objeto puede ser
reconstruido leyendo de un ObjectInputStream.
Cuando hay cierta información que no se desea enviar como parte del
objeto, como puede ser cierta información delicada que quizás por razones
de seguridad no deben ser enviadas a través de un canal inseguro (una
contraseña), se declara transient y esta no es escrita durante la
serialización.
synchronized
Este modificador es utilizado para controlar el acceso a código crítico en
programas de hilado múltiple. Es casi imprescindible su uso cuando
Formando Emprendedores De Calidad Para Un Mundo Empresarial
35
Instituto de Educación Superior
“San Ignacio de Monterrico”
utilizamos hilado múltiple y debemos acceder a propiedades o recursos
desde varios hilos a la vez.
volatile
Solo las variables pueden ser declaradas volatile. Esto quiere decir que
pueden ser modificadas de forma asincrónica para que el compilador tome
las precauciones necesarias. Este tipo de variables tiene especial interés
para los ambientes con varios procesadores ya que cuando un hilo
modifica una variable de un objeto, este puede tener una copia local de la
variable y modificar esta. El problema surge cuando otro hilo lee la variable
y el valor recibido es el de la variable local al hilo. Esta puede diferir de la
variable local del otro hilo e inclusive de la variable almacenada en una
suerte de "memoria principal". Al especificar la variable como volatile se le
indica a el compilador que la variable debe ser leída y escrita directamente
de la "memoria principal" cada vez que se necesita evitando tener copias
locales distintas.
Características
No todos los modificadores pueden ser aplicados a todas las
características. Las clases de nivel superior no pueden ser protegidas y los
métodos no pueden ser transient. En cambio la palabra clave static es tan
general que se puede aplicar hasta en bloques de código flotantes.
La siguiente tabla muestra las posibles combinaciones entre
características y modificadores:
Modificador Clase Variable Método Constructor Bloque flotante
public
yes
yes
yes
yes
no
protected
no
yes
yes
yes
no
default
yes
yes
yes
yes
yes
private
no
yes
yes
yes
no
final
yes
yes
yes
no
no
abstract
yes
no
yes
no
no
static
no
yes
yes
no
yes
native
no
no
yes
no
no
transient
no
yes
no
no
no
volatile
no
yes
no
no
no
synchronized no
no
yes
no
yes
Formando Emprendedores De Calidad Para Un Mundo Empresarial
36
Instituto de Educación Superior
“San Ignacio de Monterrico”
Constantes
Constantes Literales
Por su parte, las constantes literales son representaciones literales de
datos en el código fuente. Estos valores se emplean para inicializar
variables o para usarlas en expresiones en las que se requieren valores
constantes.
Las constantes literales pueden usarse para referirse explícitamente a uno
de los tipos siguientes: int, long, float, double, boolean, char, String y null.
En caso de que necesitáramos representar un tipo primitivo distinto de los
indicados, podríamos hacer una conversión explícita al tipo deseado. Por
otra parte, los objetos no pueden ser representados mediante estas
constantes, y por tanto, no existen constantes literales de tipo de
referencia.
Constantes Enteras
Las constantes enteras se representan por un valor que está formado sólo
por dígitos numéricos y no tienen la coma decimal. Si el número no
comienza por cero, representa un número decimal (en base 10).
Para representar constantes de tipo “long” se emplea el mismo método
pero añadiendo al final la letra “L”.
Está permitido el empleo de la letra “L” en minúsculas, pero no debe
usarse ya que puede confundirse con el número uno.
Además de representaciones en formato decimal, se pueden representar
constantes en los formatos octal y hexadecimal. Las constantes octales se
representan anteponiendo un cero al propio número, que además, sólo
podrá estar formado por los dígitos del 0 al 7. Por su parte, las constantes
hexadecimales se representan anteponiendo 0x o 0X y permitiendo que los
dígitos del número puedan ser del 0 al 9 y las letras de la A a la F (en
mayúsculas o minúsculas). Las constantes octales y hexadecimales son
de tipo entero a menos que estén seguidas por la letra “L”, en cuyo caso
serían de tipo long.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
37
Instituto de Educación Superior
“San Ignacio de Monterrico”
int a = 1002; // Tipo int
long b = 1002L; // Tipo long
int c = 053; // Tipo int en octal
int d = 053L; // Tipo long en octal
int e = 0X002B ; // Tipo int en hexadecimal
int f = 0X002BL; // Tipo long en hexadecimal
El tipo char es un tipo entero, no obstante se trata de un tipo especial
pensado para almacenar caracteres Unicode. Estos últimos son similares a
los caracteres ASCII en que están pensados para almacenar símbolos,
números y caracteres. Sin embargo, el rango del tipo char es mucho mayor
porque permite representar caracteres de casi todos los idiomas del
mundo.
Las constantes literales de tipo char pueden representarse de dos formas
distintas:
•
Encerrando un único carácter entre comillas simples.
•
Utilizando una secuencia de escape, que es muy útil para
representar caracteres que no pueden escribirse mediante el
teclado. La secuencia de escape se representa por una barra
inclinada inversa (\) seguida de un número octal de tres dígitos, o
bien por la barra seguida de la letra “u” y de un número hexadecimal
de cuatro dígitos. La secuencia de escape también debe ir
encerrada entre comillas simples. También existe una serie de
secuencias de escape especiales que se utilizan para representar
algunos de los caracteres ASCII más comunes. En la siguiente tabla
se relacionan todos.
Carácter
\n
\t
\r
\f
\b
\\
\’
\”
Significado
Nueva Línea
Tabulador
Retroceso de Carro
Comienzo de Pagina
Borrado a la Izquierda
El Carácter \
El Carácter ’
El Carácter ”
Formando Emprendedores De Calidad Para Un Mundo Empresarial
38
Instituto de Educación Superior
“San Ignacio de Monterrico”
Constantes De Coma Flotante
Se expresan mediante un valor numérico que incluya al menos una
posición decimal. A menos que se indique otra cosa, las constantes de
coma flotante serán de tipo double. Para denotar que la constante sea de
tipo float es necesario posponer la letra “f” o “F”. Por claridad, también
pueden expresarse las constantes de tipo double posponiendo la letra “d” o
“D” al número.
Este tipo de constantes también pueden expresarse mediante la notación
exponencial, que define un número en dos partes: la mantisa y el
exponente. La mantisa es un número en coma flotante con un dígito
significativo y cierto número de decimales. El exponente es un número que
representa la potencia de 10 que multiplica a la mantisa. Entre la mantisa y
el exponente se debe poner la letra “e” o “E”.
double a = 123.4; // Tipo double
float b = 123.4F; // Tipo float
double c = 4.502e-12; // Tipo double en notación exponencial.
Constantes De Cadena
Las constantes de cadena se representan por una secuencia de caracteres
encerrados entre comillas dobles. Dentro de ellas se pueden incluir las
secuencias de escape que antes mencionamos. Veamos algunos
ejemplos:
String a = “Texto simple”; // Texto simple
String b = “Esta es la letra \u0041”;
// Usando una secuencia de escape para la letra “A”
String c = “El proceso ha terminado.\nPulse una tecla”;
// Texto en dos líneas
Constantes Booleanas
Existen dos: true y false. Al contrario que en otros lenguajes de
programación, ninguna de estas constantes puede ser representada
mediante un valor entero usando como criterio la igualdad a cero.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
39
Instituto de Educación Superior
“San Ignacio de Monterrico”
Constante “null”
Existe una constante nula (null) que podemos usarla para representar el
valor null.
Operadores
Los datos se manipulan muchas veces utilizando operaciones con ellos. Los
datos se suman, se restan,... y a veces se realizan operaciones más complejas.
Operadores Aritméticos
Son:
Operador
significado
+
Suma
-
Resta
*
Producto
/
División
%
Módulo (resto)
Hay que tener en cuenta que el resultado de estos operadores varía
notablemente si usamos enteros o si usamos números de coma flotante.
Por ejemplo:
double resultado1, d1=14, d2=5;
int resultado2, i1=14, i2=5;
resultado1= d1 / d2;
resultado2= i1 / i2;
resultado1 valdrá 2.8 mientras que resultado2 valdrá 2.
Es más incluso:
double resultado;
int i1=7,i2=2;
resultado=i1/i2; //Resultado valdrá 3
resultado=(double)i1/(double)i2; //Resultado valdrá 3.5
Formando Emprendedores De Calidad Para Un Mundo Empresarial
40
Instituto de Educación Superior
“San Ignacio de Monterrico”
El operador del módulo (%) para calcular el resto de una división entera.
Ejemplo:
int resultado, i1=14, i2=5;
resultado = i1 % i2; //El resultado será 4
Operadores Condicionales o relacionales
Sirven para comparar valores. Siempre devuelven valores booleanos.
Son:
Operador
significado
<
Menor
>
Mayor
>=
Mayor o igual
<=
Menor o igual
==
Igual
!=
Distinto
Operadores Lógicos
Los operadores lógicos (AND, OR y NOT), sirven para evaluar condiciones
complejas. NOT sirve para negar una condición. Ejemplo:
boolean mayorDeEdad, menorDeEdad; int edad = 21;
mayorDeEdad = edad >= 18; //mayorDeEdad será true
menorDeEdad = !mayorDeEdad; //menorDeEdad será false
El operador && (AND) sirve para evaluar dos expresiones de modo que si
ambas son ciertas, el resultado será true sino el resultado será false.
Ejemplo:
boolean carnetConducir=true;
int edad=20;
boolean puedeConducir= (edad>=18) && carnetConducir;
//Si la edad es de al menos 18 años y carnetConducir es
//true, puedeConducir es true
Formando Emprendedores De Calidad Para Un Mundo Empresarial
41
Instituto de Educación Superior
“San Ignacio de Monterrico”
El operador || (OR) sirve también para evaluar dos expresiones. El
resultado será true si al menos uno de las expresiones es true. Ejemplo:
boolean nieva =true, llueve=false, graniza=false;
boolean malTiempo= nieva || llueve || graniza;
Operadores de Asignación
Permiten asignar valores a una variable. El fundamental es “=”. Pero sin
embargo se pueden usar expresiones más complejas como:
x += 3;
En el ejemplo anterior lo que se hace es sumar 3 a la x (es lo mismo x+=3,
que x=x+3). Eso se puede hacer también con todos estos operadores:
+=
|=
-=
^=
*=
%=
/=
>>=
&=
<<=
También se pueden concatenar asignaciones:
x1 = x2 = x3 = 5;
Otros operadores de asignación son “++” (incremento) y “- -” (decremento).
Ejemplo:
x++; //esto es x=x+1;
x--; //esto es x=x-1;
Pero hay dos formas de utilizar el incremento y el decremento. Se puede
usar por ejemplo x++ o ++x
La diferencia estriba en el modo en el que se comporta la asignación.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
42
Instituto de Educación Superior
“San Ignacio de Monterrico”
Ejemplo:
int x=5, y=5, z;
z=x++; //z vale 5, x vale 6 z=++y; //z vale 6, y vale 6
Operador (?)
Este operador (conocido como if de una línea) permite ejecutar una
instrucción u otra según el valor de la expresión. Sintaxis:
expresionlogica?valorSiVerdadero:valorSiFalso;
Ejemplo:
paga=(edad>18)?6000:3000;
En este caso si la variable edad es mayor de 18, la paga será de 6000,
sino será de 3000. Se evalúa una condición y según es cierta o no se
devuelve un valor u otro. Nótese que esta función ha de devolver un valor
y no una expresión correcta. Es decir, no funcionaría:
(edad>18)? paga=6000: paga=3000; /ERROR!!!!
Conversión Entre Tipos (Casting)
Hay veces en las que se deseará realizar algo como:
int a;byte b=12;
a=b;
La duda está en si esto se puede realizar. La respuesta es que sí. Sí porque un
dato byte es más pequeño que uno int y Java le convertirá de forma implícita. Sin
embargo en:
int a=1;
byte b;
b=a;
El compilador devolverá error aunque el número 1 sea válido para un dato byte.
Para ello hay que hacer un casting. Eso significa poner el tipo deseado entre
paréntesis delante de la expresión.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
43
Instituto de Educación Superior
“San Ignacio de Monterrico”
int a=1;
byte b;
b= (byte) a; //No da error
En el siguiente ejemplo:
byte n1=100, n2=100, n3;
n3= n1 * n2 /100;
Aunque el resultado es 100, y ese resultado es válido para un tipo byte; lo que
ocurrirá en realidad es que ocurrirá un error. Eso es debido a que primero
multiplica 100 * 100 y como eso da 10000, no tiene más remedio el compilador
que pasarlo a entero y así quedará aunque se vuelva a dividir. La solución
correcta sería:
n3 = (byte) (n1 * n2 / 100);
Más ejemplos de CASTING
int a = 24;
byte b = (byte) a;
short c = (short) a;
long d = (long) a;
Estructuras De Control De Flujo
Las estructuras de control en Java son básicamente la misma que en C, con
excepción del goto, que no existe.
Por ejemplo:
public final String toString() {
if (y<0)
return x+"-i"+(-y);
else
return +x+"+i"+y;
}
if
Permite crear estructuras condicionales simples; en las que al cumplirse una
condición se ejecuta una serie de instrucciones. Se puede hacer que otro
Formando Emprendedores De Calidad Para Un Mundo Empresarial
44
Instituto de Educación Superior
“San Ignacio de Monterrico”
conjunto de instrucciones se ejecute si la condición es falsa. La condición es
cualquier expresión que devuelva un resultado de true o false.
Sintaxis:
if (condición) instrucción si es true;
else instrucción si es false;
O bien:
if (condición) {
instrucciones que se ejecutan si la condición es true
}
else {
instrucciones que se ejecutan si la condición es false
}
La parte else es opcional. Ejemplo:
if ((diasemana>=1) && (diasemana<=5)){
trabajar = true;
}
else {
trabajar = false;
}
Se pueden anidar varios if a la vez. De modo que se comprueban varios valores.
Ejemplo:
if (diasemana==1) dia=”Lunes”;
else if (diasemana==2) dia=”Martes”;
else if (diasemana==3) dia=”Miércoles”;
else if (diasemana==4) dia=”Jueves”;
else if (diasemana==5) dia=”Viernes”;
else if (diasemana==6) dia=”Sábado”;
else if (diasemana==7) dia=”Domingo”;
else dia=”?”;
switch
Formando Emprendedores De Calidad Para Un Mundo Empresarial
45
Instituto de Educación Superior
“San Ignacio de Monterrico”
Permite ejecutar una serie de operaciones para el caso de que una variable tenga
un valor entero dado. La ejecución saltea todos los case hasta que encuentra uno
con el valor de la variable, y ejecuta desde allí hasta el final del case o hasta que
encuentre un break, en cuyo caso salta al final del case. El default permite poner
una serie de instrucciones que se ejecutan en caso de que la igualdad no se de
para ninguno de los case.
Sintaxis:
switch (expresión) {
case valor1: sentencias si la expresiona es igual al valor1;
[break]
case valor2: sentencias si la expresiona es igual al valor2;
[break]
.
.
default:
sentencias que se ejecutan si no se cumple ninguna de las anteriores
}
Esta instrucción evalúa una expresión (que debe ser short, int, byte o char), y
según el valor de la misma ejecuta instrucciones. Cada case contiene un valor de
la expresión; si efectivamente la expresión equivale a ese valor, se ejecutan las
instrucciones de ese case y de los siguientes.
La instrucción break se utiliza para salir del switch. De tal modo que si queremos
que para un determinado valor se ejecuten las instrucciones de un apartado case
y sólo las de ese apartado, entonces habrá que finalizar ese case con un break.
El bloque default sirve para ejecutar instrucciones para los casos en los que la
expresión no se ajuste a ningún case.
Ejemplo:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
46
Instituto de Educación Superior
“San Ignacio de Monterrico”
switch (diasemana) {
case 1:
dia=”Lunes”;
break;
case 2:
dia=”Martes”;
break;
case 3:
dia=”Miércoles”;
break;
case 4:
dia=”Jueves”;
break;
case 5:
dia=”Viernes”;
break;
case 6:
dia=”Sábado”;
break;
case 7:
dia=”Domingo”;
break;
default:
dia=”?”;
}
Ejemplo 2:
switch (diasemana) {
case 1:
case 2:
case 3:
case 4:
case 5:
laborable=true;
break;
case 6:
case 7:
laborable=false;
}
while
Formando Emprendedores De Calidad Para Un Mundo Empresarial
47
Instituto de Educación Superior
“San Ignacio de Monterrico”
La instrucción while permite crear bucles. Un bucle es un conjunto de sentencias
que se repiten si se cumple una determinada condición. Los bucles while
agrupan instrucciones las cuales se ejecutan continuamente hasta que una
condición que se evalúa sea falsa.
La condición se mira antes de entrar dentro del while y cada vez que se termina
de ejecutar las instrucciones del while
Sintaxis:
while (condición) {
sentencias que se ejecutan si la condición es true
}
Ejemplo (cálculo de factorial de un número, el factorial de 4 sería: 4*3*2*1):
//factorial de 4
int n=4, factorial=1, temporal=n;
while (temporal>0) {
factorial*=temporal--;
}
do while
Crea un bucle muy similar al anterior, en la que también las instrucciones del
bucle se ejecutan hasta que una condición pasa a ser falsa. La diferencia estriba
en que en este tipo de bucle la condición se evalúa después de ejecutar las
instrucciones; lo cual significa que al menos el bucle se ejecuta una vez. Sintaxis:
do {
instrucciones
} while (condición)
for
Es un bucle más complejo especialmente pensado para rellenar arrays o para
ejecutar instrucciones controladas por un contador. Una vez más se ejecutan una
serie de instrucciones en el caso de que se cumpla una determinada condición.
Sintaxis:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
48
Instituto de Educación Superior
“San Ignacio de Monterrico”
for (expresiónInicial; condición; expresiónEncadavuelta) {
instrucciones;
}
La expresión inicial es una instrucción que se ejecuta una sola vez: al entrar por
primera vez en el bucle for (normalmente esa expresión lo que hace es dar valor
inicial al contador del bucle).
La condición es cualquier expresión que devuelve un valor lógico. En el caso de
que esa expresión sea verdadera se ejecutan las instrucciones. Cuando la
condición pasa a ser falsa, el bucle deja de ejecutarse. La condición se valora
cada vez que se terminan de ejecutar las instrucciones del bucle.
Después de ejecutarse las instrucciones interiores del bucle, se realiza la
expresión que tiene lugar tras ejecutarse las instrucciones del bucle (que,
generalmente, incrementa o decrementa al contador). Luego se vuelve a evaluar
la condición y así sucesivamente hasta que la condición sea falsa.
Ejemplo (factorial):
//factorial de 4
int n=4, factorial=1, temporal=n;
for (temporal=n;temporal>0;temporal--){
factorial *=temporal;
}
Sentencias De Salida De Un Bucle
break
Es una sentencia que permite salir del bucle en el que se encuentra
inmediatamente. Hay que intentar evitar su uso ya que produce malos hábitos al
programar.
continue
Instrucción que siempre va colocada dentro de un bucle y que hace que el flujo
del programa ignore el resto de instrucciones del bucle; dicho de otra forma, va
Formando Emprendedores De Calidad Para Un Mundo Empresarial
49
Instituto de Educación Superior
“San Ignacio de Monterrico”
hasta la siguiente iteración del bucle. Al igual que ocurría con break, hay que
intentar evitar su uso.
Ejemplo #
import java.io.*;
class Bucles {
public static void main (String argv[ ]) {
int i=0;
for (i=1; i<5; i++) {
System.out.println("antes "+i);
if (i==2) continue;
if (i==3) break;
System.out.println("después "+i);
}
}
}
La salida es:
antes 1
después 1
antes 2
antes 3
i comienza en 1 (imprime "antes" y "después"); cuando pasa a 2, el continue salta
al principio del bucle (no imprime el "después"). Finalmente, cuando "i" vale 3, el
break da por terminado el bucle for.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
50
Instituto de Educación Superior
“San Ignacio de Monterrico”
PROGRAMACION ORIENTADA A OBJETOS CON JAVA
Clase
La primer característica de un programa Java es que este debe definir una Clase
que lleve por nombre el mismo nombre del archivo fuente, en este caso si el
nombre del archivo se llama Basico.java este debe incluir una definición de una
Clase llamada Basico, nótese que ambos nombres coinciden en su sintaxis, esto
es, ambos inician con letra mayúscula lo cual es una convención llevada acabo
para la definición de Clases.
Para definir una Clase se utiliza la palabra reservada class así como un
calificador de acceso. Dentro de la definición de la Clase se deben incluir los
respectivos métodos que podrán ser invocados.
Ejemplo (Basico.java):
public class Basico {
public static void main(String args[]) {
System.out.println("Un despliegue de Datos");
}
}
En el caso del ejemplo Basico.java únicamente se define el método main, dicho
método es invocado por "default" al ejecutarse la Clase.
Nótese que el método inicia con letra minúscula, esta es otra convención utilizada
para diferenciarse de las distintas clases. La definición del método indica lo
siguiente:
o Primero se definen los modificadores del método en este caso son public y
static.
o También se define el valor de retorno del método: void, lo cual indica que
no será retornado ningún valor.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
51
Instituto de Educación Superior
“San Ignacio de Monterrico”
o Le sigue el nombre del método: main.
o Dentro de paréntesis se incluyen los parámetros de entrada para el
método (String args[]).
o Finalmente
el
Paquete.Clase.Método
System.out.println
envía
un
mensaje a la pantalla.
Definir atributos de la clase
(variables, propiedades o datos de las clases)
Cuando se definen los datos de una determinada clase, se debe indicar el tipo de
propiedad que es (String, int, double, int[][],...) y el modificador de acceso (public,
private,...). El modificador indica en qué partes del código ese dato será visible.
Ejemplo:
class Persona {
public String nombre;
//Se puede acceder desde cualquier clase
private int contraseña;
//Sólo se puede acceder desde la clase Persona
protected String dirección;
//Acceden a esta propiedad esta clase y sus descendientes
Por lo general las propiedades de una clase suelen ser privadas o protegidas, a
no ser que se trate de un valor constante, en cuyo caso se declararán como
públicos.
Las variables locales de una clase pueden ser inicializadas.
class auto{
public nRuedas=4;
Para poder acceder a los atributos de un objeto, se utiliza esta sintaxis:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
52
Instituto de Educación Superior
“San Ignacio de Monterrico”
objeto.atributo
ejemplo:
Miauto.velocidad;
Creación De Constructores
Un constructor es un método que es llamado automáticamente al crear un objeto
de una clase, es decir al usar la instrucción new.
Un constructor no es más que un método que tiene el mismo nombre que la
clase. Con lo cual para crear un constructor basta definir un método en el código
de la clase que tenga el mismo nombre que la clase.
Ejemplo:
class Ficha {
private int casilla;
Ficha() { //constructor
casilla = 1;
}
public void avanzar(int n) {
casilla += n;
}
public int casillaActual(){
return casilla;
}
}
public class app {
public static void main(String[] args) {
Ficha ficha1 = new Ficha();
ficha1.avanzar(3);
System.out.println(ficha1.casillaActual());//Da 4
}
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
53
Instituto de Educación Superior
“San Ignacio de Monterrico”
En la línea Ficha ficha1 = new Ficha(); es cuando se llama al constructor, que es
el que coloca inicialmente la casilla a 1. Pero el constructor puede tener
parámetros:
class Ficha {
private int casilla; //Valor inicial de la propiedad
Ficha(int n) { //constructor
casilla = n;
}
public void avanzar(int n) {
casilla += n;
}
public int casillaActual(){
return casilla;
}
}
public class app {
public static void main(String[] args) {
Ficha ficha1 = new Ficha(6);
ficha1.avanzar(3);
System.out.println(ficha1.casillaActual());//Da 9
}
}
En este otro ejemplo, al crear el objeto ficha1, se le da un valor a la casilla, por lo
que la casilla vale al principio 6.
Hay que tener en cuenta que puede haber más de un constructor para la misma
clase. Al igual que ocurre con los métodos, los constructores se pueden
sobrecargar.
De este modo en el código anterior de la clase Ficha se podrían haber colocado
los dos constructores que hemos visto, y sería entonces posible este código:
Ficha ficha1= new Ficha();
//La propiedad casilla de la
//ficha valdrá 1
Formando Emprendedores De Calidad Para Un Mundo Empresarial
54
Instituto de Educación Superior
“San Ignacio de Monterrico”
Ficha ficha1= new Ficha(6);
//La propiedad casilla de la
//ficha valdrá 6
Cuando se sobrecargan los constructores (se utilizan varias posibilidades de
constructor), se pueden hacer llamadas a constructores mediante el objeto this
Métodos
Los métodos se utilizan de la misma forma que los atributos, excepto porque los
métodos poseen siempre paréntesis, dentro de los cuales pueden ir valores
necesarios para la ejecución del método (parámetros):
objeto.método(argumentosDelMétodo)
Los métodos siempre tienen paréntesis (es la diferencia con las propiedades) y
dentro de los paréntesis se colocan los argumentos del método. Que son los
datos que necesita el método para funcionar. Por ejemplo:
Miauto.avanza(5);
Lo cual podría hacer que el auto avance a 5 Km/h.
Definir Métodos De Clase (Operaciones O Funciones De Clase)
Un método es una llamada a una operación de un determinado objeto. Al
realizar esta llamada (también se le llama enviar un mensaje), el control
del programa pasa a ese método y lo mantendrá hasta que el método
finalice o se haga uso de return.
Para que un método pueda trabajar, normalmente hay que pasarle unos
datos en forma de argumentos o parámetros, cada uno de los cuales se
separa por comas.
Ejemplos:
balón.botar(); //sin argumentos
miCoche.acelerar(10);
ficha.comer(posición15);
//posición 15 es una variable que se pasa como argumento
Formando Emprendedores De Calidad Para Un Mundo Empresarial
55
Instituto de Educación Superior
“San Ignacio de Monterrico”
partida.empezarPartida(“18:15”,colores);
Los métodos de la clase se definen dentro de ésta. Hay que indicar un
modificador de acceso (public, private, protected o ninguno, al igual que
ocurre con las variables y con la propia clase) y un tipo de datos, que
indica qué tipo de valores devuelve el método.
Esto último se debe a que los métodos son funciones que pueden devolver
un determinado valor (un entero, un texto, un valor lógico,...) mediante el
comando return. Si el método no devuelve ningún valor, entonces se utiliza
el tipo void que significa que no devuelve valores (en ese caso el método
no tendrá instrucción return).
El último detalle a tener en cuenta es que los métodos casi siempre
necesitan datos para realizar la operación, estos datos van entre
paréntesis y se les llama argumentos. Al definir el método hay que indicar
que argumentos se necesitan y de qué tipo son.
Ejemplo:
public class vehiculo {
/** Función principal */
int ruedas;
private double velocidad=0;
String nombre;
/** Aumenta la velocidad*/
public void acelerar(double cantidad) {
velocidad += cantidad;
}
/** Disminuye la velocidad*/
public void frenar(double cantidad) {
velocidad -= cantidad;
}
/** Devuelve la velocidad*/
Formando Emprendedores De Calidad Para Un Mundo Empresarial
56
Instituto de Educación Superior
“San Ignacio de Monterrico”
public double obtenerVelocidad(){
return velocidad;
}
public static void main(String args[]){
vehiculo miCoche = new vehiculo();
miCoche.acelerar(12);
miCoche.frenar(5);
System.out.println(miCoche.obtenerVelocidad());
} // Da 7.0
}
Argumentos por valor y por referencia
En todos los lenguajes éste es un tema muy importante. Los argumentos
son los datos que recibe un método y que necesita para funcionar.
Ejemplo:
public class Matemáticas {
public double factorial(int n){
double resultado;
for (resultado=n;n>1;n--)
resultado*=n;
return resultado;
}
public static void main(String args[]){
Matemáticas m1=new Matemáticas();
double x=m1.factorial(25);//Llamada al método
}
}
En el ejemplo anterior, el valor 25 es un argumento requerido por el
método factorial para que éste devuelva el resultado (que será el factorial
de 25). En el código del método factorial, este valor 25 es copiado a la
variable n, que es la encargada de almacenar y utilizar este valor.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
57
Instituto de Educación Superior
“San Ignacio de Monterrico”
Se dice que los argumentos son por valor, si la función recibe una copia de
esos datos, es decir la variable que se pasa como argumento no estará
afectada por el código.
Ejemplo:
class prueba {
public void metodo1(int entero){
entero=18;
}
public static void main(String args[]){
int x=24;
prueba miPrueba = new prueba(); miPrueba.metodo1(x);
System.out.println(x); //Escribe 24, no 18
}
Este es un ejemplo de paso de parámetros por valor. La variable x se pasa
como argumento o parámetro para el método metodo1,
allí la variable
entero recibe una copia del valor de x en la variable entero, y a esa copia
se le asigna el valor 18. Sin embargo la variable x no está afectada por
esta asignación.
Sin embargo en este otro caso:
class prueba {
public void metodo1(int[] entero){
entero[0]=18;
}
public static void main(String args[]){
int x[]={24,24};
prueba miPrueba = new prueba(); miPrueba.metodo1(x);
System.out.println(x[0]); //Escribe 18, no 24
Aquí sí que la variable x está afectada por la asignación entero[0]=18. La
razón es porque en este caso el método no recibe el valor de esta variable,
Formando Emprendedores De Calidad Para Un Mundo Empresarial
58
Instituto de Educación Superior
“San Ignacio de Monterrico”
sino la referencia, es decir la dirección física de esta variable. Entero no es
una replica de x, es la propia x llamada de otra forma.
Los tipos básicos (int, double, char, boolean, float, short y byte) se pasan
por valor. También se pasan por valor las variables String. Los objetos y
arrays se pasan por referencia.
Devolución de valores
Los métodos pueden devolver valores básicos (int, short, double, etc.),
Strings, arrays e incluso objetos.
En todos los casos es el comando return el que realiza esta labor. En el
caso de arrays y objetos, devuelve una referencia a ese array u objeto.
Ejemplo:
class FabricaArrays {
public int[] obtenArray(){
int array[]= {1,2,3,4,5}; return array;
}
}
public class returnArray {
public static void main(String[] args) {
FabricaArrays fab=new FabricaArrays();
int nuevoArray[]=fab.obtenArray();
}
}
Sobrecarga De Métodos
Una propiedad de la POO es el polimorfismo. Java posee esa propiedad ya que
admite sobrecargar los métodos. Esto significa crear distintas variantes del
mismo método.
Ejemplo:
class Matemáticas{
Formando Emprendedores De Calidad Para Un Mundo Empresarial
59
Instituto de Educación Superior
“San Ignacio de Monterrico”
public double suma(double x, double y) {
return x+y;
}
public double suma(double x, double y, double z){
return x+y+z;
}
public double suma(double[] array){
double total =0;
for(int i=0; i<array.length;i++){
total+=array[i];
}
return total;
}
La clase matemáticas posee tres versiones del método suma. una versión que
suma dos números double, otra que suma tres y la última que suma todos los
miembros de un array de doubles. Desde el código se puede utilizar cualquiera
de las tres versiones según convenga.
Método principal main
El método principal main de una Clase Java es inalterable, en este sentido
inalterable se refiere a sus características:
o Siempre debe incluir los calificadores: public y static.
o Nunca puede retornar un valor como resultado, es decir siempre debe
indicar el tipo de dato void como retorno.
o Su parámetro de entrada siempre será un arreglo de String (String[]) el
cual es tomado de la línea de comandos o una fuente alterna.
Aunque no es un obligacion definir el método main dentro de toda Clase Java,
dicho método representa el único mecanismo automático para realizar tareas al
invocarse una Clase, esto es, al momento de ejecutarse determinada Clase
siempre será ejecutado todo el contenido dentro de dicho método.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
60
Instituto de Educación Superior
“San Ignacio de Monterrico”
Para generar una Clase compilada (Byte-Code) se utiliza el comando javac :
javac Basico.java
Lo anterior genera un archivo llamado Basico.class; para ejecutar este Byte-Code
es empleado el comando java:
java Basico
Al invocar el comando anterior será ejecutada la Clase Basico.class. Nótese que
el comando java recibe el nombre de la Clase sin la extensión .class.
Nota: Si se utiliza un IDE de desarrollo este trae opciones para compilar y
ejecutar la clase y mostrara los resultados en su propia interfase en algún panel
de salida.
La Referencia This
La palabra this es una referencia al propio objeto en el que estamos.
Ejemplo:
class punto {
int posX, posY;//posición del punto punto(posX, posY){
this.posX=posX;
this.posY=posY;
}
En el ejemplo hace falta la referencia this para clarificar cuando se usan
las propiedades posX y posY, y cuando los argumentos con el mismo nombre.
Ejemplo:
class punto {
int posX, posY;
/**Suma las coordenadas de otro punto*/
public void suma(punto punto2){
posX = punto2.posX;
Formando Emprendedores De Calidad Para Un Mundo Empresarial
61
Instituto de Educación Superior
“San Ignacio de Monterrico”
posY = punto2.posY;
}
/** Dobla el valor de las coordenadas del punto*/
public void dobla(){
suma(this);
}
En el ejemplo anterior, la función dobla, dobla el valor de las coordenadas
pasando el propio punto como referencia para la función suma (un punto sumado
a sí mismo, daría el doble). Los posibles usos de this son:
o this. Referencia al objeto actual. Se usa por ejemplo pasarle como
parámetro a un método cuando es llamado desde la propia clase.
o this.atributo. Para acceder a una propiedad del objeto actual.
o this.método(parámetros). Permite llamar a un método del objeto actual
con los parámetros indicados.
o this(parámetros). Permite llamar a un constructor del objeto actual. Esta
llamada sólo puede ser empleada en la primera línea de un constructor.
Manejo de Objetos
Se les llama instancias de clase. Son un elemento en sí de la clase, Un objeto se
crea utilizando el llamado constructor de la clase. El constructor es el método que
permite iniciar el objeto.
Creación De Objetos De La Clase
Una
vez definida la clase, se pueden utilizar objetos de la clase.
Normalmente consta de dos pasos. Su declaración, y su creación. La
declaración consiste en indicar que se va a utilizar un objeto de una clase
determinada. Y se hace igual que cuando se declara una variable simple.
Ejemplo:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
62
Instituto de Educación Superior
“San Ignacio de Monterrico”
Auto Miauto;
Eso declara el objeto Miauto como objeto de tipo Auto; se supone que
previamente se ha definido la clase Auto.
Para poder utilizar un objeto, hay que crearle de verdad. Eso consiste en
utilizar el operador new.
Ejemplo:
noriaDePalencia = new Noria();
Al hacer esta operación el objeto reserva la memoria que necesita y se
inicializa el objeto mediante su constructor.
Organización de las Clases
Paquetes (Packages) son un grupo o librería de clases desarrolladas en
java que se utilizan para algo específico.
Las principales clases de java son las siguientes (incluidas en el JDK):
o
java.applet: Creado para soportar la creación de applet Java, el
paquete java.applet permite a las aplicaciones ser descargadas sobre
una red y ejecutarse dentro de una sandbox..
o
java.awt: La Abstract Window Toolkit contiene rutinas para soportar
operaciones básicas GUI y utiliza ventanas básicas desde el sistema
nativo subyacente.
o
java.awt.event: Soporte y procesamiento de eventos para controles
awt.
o
java.io: El paquete java.io contiene clases que soportan entrada/salida.
Las clases del paquete son principalmente streams; sin embargo, se
incluye una clase para ficheros de acceso aleatorio.
o
java.lang: El paquete Java java.lang contiene clases fundamentales e
interfaces fuertemente relacionadas con el lenguaje y el sistema
runtime. Esto incluye las clases raíz que forman la jerarquía de clases,
tipos relacionados con la definición del lenguaje, excepciones básicas,
Formando Emprendedores De Calidad Para Un Mundo Empresarial
63
Instituto de Educación Superior
“San Ignacio de Monterrico”
funciones matemáticas, Hilos, funciones de seguridad, así como
también alguna información sobre el sistema nativo subyacente.
o
java.net: El paquete java.net suminista rutinas especiales IO para
redes, permitiendo las peticiones HTTP, así como también otras
transacciones comunes.
o
java.util: Las estructuras de datos que agregan objetos son el foco del
paquete java.util. En el paquete está incluida la API Collections, una
jerarquía organizada de estructura de datos influenciada fuertemente
por consideraciones de patrones de diseño.
o
javax.swing: Swing es una colección de rutinas que se construyen
sobre java.awt para suministrar un toolkit de widgets independiente de
plataforma.
o
javax.swing.event: Soporte y procesamiento de eventos para
controles swing.
o
java.math: El paquete java.math soporta aritmética multiprecision
(incluyendo
operaciones
aritméticas
modulares)
y
suministra
generadores de números primos multiprecision usados para la
generación de claves criptográficas.
Paquetes
Un paquete es una colección de clases e interfaces relacionadas. El compilador
de Java usa los paquetes para organizar la compilación y ejecución. Es decir, un
paquete es una biblioteca. De hecho el nombre completo de una clase es el
nombre del paquete en el que está la clase, punto y luego el nombre de la clase.
Es decir si la clase Coche está dentro del paquete locomoción, el nombre
completo de Coche es locomoción.Coche.
A veces resulta que un paquete está dentro de otro paquete, entonces habrá que
indicar la ruta completa a la clase. Por ejemplo locomoción.motor.Motor1300
Mediante el comando import, se evita tener que colocar el nombre completo. El
comando import se coloca antes de definir la clase.
Ejemplo:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
64
Instituto de Educación Superior
“San Ignacio de Monterrico”
import locomoción.motor.Motor1300;
Gracias a esta instrucción para utilizar la clase Motor1300 no hace falta indicar el
paquete en el que se encuentra, basta indicar sólo Motor1300.
Ejemplo:
import locomoción.*;
//Importa todas las clases del paquete locomoción
Esta instrucción no importa el contenido de los paquetes interiores a locomoción
(es decir que si la clase Coche está dentro del paquete motor, no sería importada
con esa instrucción, ya que el paquete motor no ha sido importado, sí lo sería
la clase locomoción.BarcoDeVela). Por ello en el ejemplo lo completo sería:
import locomoción.*;
import locomoción.motor.*;
Cuando desde un programa se hace referencia a una determinada clase se
busca ésta en el paquete en el que está colocada la clase y, sino se encuentra,
en los paquetes que se han importado al programa. Si ese nombre de clase se ha
definido en un solo paquete, se usa. Si no es así podría haber ambigüedad por
ello se debe usar un prefijo delante de la clase con el nombre del paquete.
Es decir:
paquete.clase
O incluso:
paquete1.paquete2... ...clase
En el caso de que el paquete sea subpaquete de otro más grande.
Las clases son visibles en el mismo paquete a no ser que se las haya declarado
con el modificador private.
Organización de los paquetes
Los paquetes en realidad son subdirectorios cuya raíz debe ser
absolutamente accesible por
el
sistema
operativo.
Para
ello
es
necesario usar la variable de entorno CLASSPATH de la línea de
comandos. Esta variable se suele definir en el archivo autoexec.bat o en
Formando Emprendedores De Calidad Para Un Mundo Empresarial
65
Instituto de Educación Superior
“San Ignacio de Monterrico”
MI PC en el caso de las últimas versiones de Windows. Hay que añadirla
las rutas a las carpetas que contienen los paquetes (normalmente todos
los paquetes se suelen crear en la misma carpeta), a estas carpetas se las
llama filesystems.
Así para el paquete prueba.reloj tiene que haber una carpeta prueba,
dentro de la cual habrá una carpeta reloj y esa carpeta prueba tiene que
formar parte del classpath.
Una clase se declara perteneciente aun determinado paquete usando la
instrucción package al principio del código (sin usar esta instrucción, la
clase no se puede compilar). Si se usa package tiene que ser la primera
instrucción del programa Java:
//Clase perteneciente al paquete tema5 que está en ejemplos
package ejemplos.tema5;
En los entornos de desarrollo o IDEs
puede
(NetBeans, JBuilder,...) se
uno despreocupar de la variable classpath ya que poseen
mecanismos de ayuda para gestionar los paquetes. Pero hay que tener en
cuenta que si se compila a mano mediante el comando java (véase
proceso de compilación, página i) se debe añadir el modificador -cp para
que sean accesibles las clases contenidas en paquetes del classpath (por
ejemplo java -cp prueba.java).
El uso de los paquetes permite que al compilar sólo se compile el código
de la clase y de las clases importadas, en lugar de compilar todas las
librerías. Sólo se compila lo que se utiliza.
Diagrama de Paquetes
Formando Emprendedores De Calidad Para Un Mundo Empresarial
66
Instituto de Educación Superior
“San Ignacio de Monterrico”
Uso de los métodos print y println
println(“mensaje”) Escribe el “mensaje” y luego se produce un cambio de
línea que equivale a pulsar [Return]
print(“mensaje”) Escribe el “mensaje” y el cursor se sitúa al final del
mensaje y en la misma línea.
Para recordarlo piensa en la siguiente “fórmula”:
Esto seria una equivalencia más explicativa:
println = print + ln (línea nueva)
El comando \n dentro del mensaje del método print o println también
realiza un salto de línea en el mensaje.
Ejercicio 4
class J001 {
public static void main(String [] args) {
System.out.println("Hola\nQue tal");
System.out.println("Uno\nDos\nTres\nCuatro");
}
}
Ejercicio 5
class J002 {
Formando Emprendedores De Calidad Para Un Mundo Empresarial
67
Instituto de Educación Superior
“San Ignacio de Monterrico”
public static void main(String [] args) {
System.out.println("Hola");
System.out.println("Adios");
System.out.println("");
System.out.print("Pues vale");
System.out.print("Eso");
System.out.print("Vale");
}
}
Instrucción Import
Hay código que se puede utilizar en los programas que realicemos en Java. Se
importa clases de objetos que están contenidas, a su vez, en paquetes
estándares.
Por ejemplo la clase Date es una de las más utilizadas, sirve para manipular
fechas. Si alguien quisiera utilizar en su código objetos de esta clase, necesita
incluir una instrucción que permita utilizar esta clase. La sintaxis de esta
instrucción es:
import paquete.subpaquete.subsubapquete....clase
Esta instrucción se coloca en la parte superior del código. Para la clase Date
sería:
import java.util.Date
Lo que significa, importar en el código la clase Date que se encuentra dentro del
paquete util que, a su vez, está dentro del gran paquete llamado java.
También se puede utilizar el asterisco en esta forma:
import java.util.*
Esto significa que se va a incluir en el código todas las clases que están dentro
del paquete util de java.
Ejemplo:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
68
Instituto de Educación Superior
“San Ignacio de Monterrico”
// J003.java = dibuja 2 circunferencias
import java.awt.*;
import java.awt.event.*;
class J003 extends Frame {
public J003() {
setTitle("Dos circunferencias");
}
public static void main(String [] args) {
Frame f=new J003();
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize(300,200);
f.setVisible(true);
}
public void paint(Graphics g){
g.setColor(Color.red);
g.drawOval(10,30,30,30);
g.setColor(Color.blue);
g.drawOval(35,30,30,30);
g.drawString("Dos circunferencias",40,100);
}
}
f.setSize(600,400) Determina el tamaño de la ventana: 1ª coordenada =
600 = anchura en píxels, 2ª coordenada = 400 = altura en píxels.
g.setColor(Color,red) Establece el rojo como color de dibujo.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
69
Instituto de Educación Superior
“San Ignacio de Monterrico”
g.drawOval(10,30,30,30) Dibuja una circunferencia cuyo “vértice” superior
izquierdo se encuentra en el punto 10,30. La tercera coordenada es el
diametro horizontal y la cuarta el diámetro vertical. Como los dos diámetros
son iguales, resulta una circunferencia. Si fueran distintos, seria una elipse.
g.drawString(“Dos circunferencias”,40,100) “Dibuja” el texto que hay
entre comillas en el punto de coordenadas 40,100 (1ª coordenada=
distáncia horizontal desde el borde izquierdo, 2ª coordenada = distáncia
vertical desde el borde superior).
Interfaces
La limitación de que sólo se puede heredar de una clase, hace que haya
problemas ya que muchas veces se deseará heredar de varias clases. Aunque
ésta no es la finalidad directa de las interfaces, sí que tiene cierta relación.
Mediante interfaces se definen una serie de comportamientos de objeto. Estos
comportamientos puede ser “implementados” en una determinada clase. No
definen el tipo de objeto que es, sino lo que puede hacer (sus capacidades). Por
ello lo normal es que el nombre de las interfaces terminen con el texto “able”
(configurable, modificable, cargable, etc).
Por ejemplo en el caso de la clase Coche, esta deriva de la superclase Vehículo,
pero además puesto que es un vehículo a motor, puede implementar métodos de
una interfaz llamada por ejemplo arrancable. Se dirá entonces que la clase Coche
es arrancable.
Utilizar interfaces
Para hacer que una clase utilice una interfaz, se añade detrás del nombre
de la clase la palabra implements seguida del nombre del interfaz. Se
pueden poner varios nombres de interfaces separados por comas
(solucionando, en cierto modo, el problema de la herencia múltiple).
class Coche extends vehiculo implements arrancable {
public void arrancar (){
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
70
Instituto de Educación Superior
“San Ignacio de Monterrico”
public void detenerMotor(){
}
Hay que tener en cuenta que la interfaz arrancable no tiene porque tener
ninguna relación de herencia con la clase vehículo, es más se podría
implementar el interfaz arrancable a una bomba de agua.
Creación de interfaces
Una interfaz en realidad es una serie de constantes y métodos abstractos.
Cuando una clase implementa un determinado interfaz debe anular los
métodos abstractos de éste, redefiniéndolos en la propia clase. Esta es la
base de una interfaz, en realidad no hay una relación sino que hay una
obligación por parte de la clase que implemente la interfaz de redefinir los
métodos de ésta.
Una interfaz se crea exactamente igual que una clase (se crean en
archivos propios también), la diferencia es que la palabra interface
sustituye a la palabra class y que sólo se pueden definir en un interfaz
constantes y métodos abstractos.
Todas las interfaces son abstractas y sus métodos también son todos
abstractos y públicos (no hace falta poner el modificar abstract se toma de
manera implícita). Las variables se tienen obligatoriamente que inicializar.
Ejemplo:
interface arrancable(){
boolean motorArrancado=false; void arrancar();
void detenerMotor();
}
Los métodos son simples prototipos y toda variable se considera una
constante (a no ser que se redefina en una clase que implemente esta
interfaz, lo cual no tendría mucho sentido).
Formando Emprendedores De Calidad Para Un Mundo Empresarial
71
Instituto de Educación Superior
“San Ignacio de Monterrico”
Subinterfaces
Una interfaz puede heredarse de otra interfaz, como por ejemplo en:
interface dibujable extends escribible, pintable {
Dibujable
es subinterfaz de escribible y pintable. Es curioso, pero los
interfaces sí admiten herencia múltiple. Esto significa que la clase que
implemente el interfaz dibujable deberá incorporar los métodos definidos
en escribible y pintable.
Variables de interfaz
Al definir una interfaz, se pueden crear después variables de interfaz.
Se puede interpretar esto como si el interfaz fuera un tipo especial de
datos (que no de clase). La ventaja que proporciona es que pueden
asignarse variables interfaz a cualquier objeto de una clase que
implementa la interfaz. Esto permite cosas como:
Arrancable motorcito; //motorcito es una variable de tipo arrancable
Coche c=new Coche(); //Objeto de tipo coche
BombaAgua ba=new BombaAgua(); //Objeto de tipo BombaAgua
motorcito=c; //Motorcito apunta a c
motorcito.arrancar() //Se arrancará c
motorcito=ba; //Motorcito apunta a ba
motorcito=arrancar; //Se arranca la bomba de agua
El juego que dan estas variables es impresionante, debido a que fuerzan
acciones sobre objetos de todo tipo, y sin importar este tipo; siempre
Formando Emprendedores De Calidad Para Un Mundo Empresarial
72
Instituto de Educación Superior
“San Ignacio de Monterrico”
y
cuando
estos
objetos pertenezcan a clases que implementen el
interfaz.
Interfaces como funciones de retroinvocación
En C++ una función de retroinvocación es un puntero que señala a un
método o a un objeto. Se usan para controlar eventos. En Java se usan
interfaces para este fin.
Ejemplo:
interface Escribible {
void escribe(String texto);
}
class Texto implements Escribible {
public void escribe(texto){
System.out.println(texto);
}
}
class Prueba {
Escribible escritor;
public Prueba(Escribible e){
escritor=e;
}
public void enviaTexto(String s){
escritor.escribe(s);
}
}
En el ejemplo escritor es una variable de la interfaz Escribible, cuando se
llama a su método escribe, entonces se usa la implementación de la clase
texto.
Herencia
Es una de las armas fundamentales de la programación orientada a objetos.
Permite crear nuevas clases que heredan características presentas en clases
anteriores. Esto facilita enormemente el trabajo porque ha permitido crear clases
Formando Emprendedores De Calidad Para Un Mundo Empresarial
73
Instituto de Educación Superior
“San Ignacio de Monterrico”
estándar para todos los programadores y a partir de ellas crear nuestras propias
clases personales. Esto es más cómodo que tener que crear nuestras clases
desde cero.
Para que una clase herede las características de otra hay que utilizar la palabra
clave extends tras el nombre de la clase. A esta palabra le sigue el nombre de la
clase cuyas características se heredarán. Sólo se puede tener herencia de una
clase (a la clase de la que se hereda se la llama superclase y a la clase heredada
se la llama subclase).
Ejemplo:
class coche extends vehiculo {
} //La clase coche parte de la definición de vehículo
Métodos y propiedades no heredados
Por defecto se heredan todos los métodos y propiedades protected y public (no
se heredan los private). Además si se define un método o propiedad en la
subclase con el mismo nombre que en la superclase, entonces se dice que se
está redefiniendo el método, con lo cual no se hereda éste, sino que se
reemplaza por el nuevo.
Ejemplo:
class vehiculo {
public int velocidad;
public int ruedas;
Formando Emprendedores De Calidad Para Un Mundo Empresarial
74
Instituto de Educación Superior
“San Ignacio de Monterrico”
public void parar() {
velocidad = 0;
}
public void acelerar(int kmh) {
velocidad += kmh;
}
}
class coche extends vehiculo{
public int ruedas=4;
public int gasolina;
public void repostar(int litros) {
gasolina+=litros;
}
}
public class app {
public static void main(String[] args) {
coche coche1=new coche();
coche.acelerar(80);//Método heredado
coche.repostar(12);
Anulación De Métodos
Como se ha visto, las subclases heredan los métodos de las superclases. Pero
es más, también los pueden sobrecargar para proporcionar una versión de un
determinado método.
Por último, si una subclase define un método con el mismo nombre, tipo y
argumentos que un método de la superclase, se dice entonces que se
sobrescribe o anula el método de la superclase. Ejemplo:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
75
Instituto de Educación Superior
“San Ignacio de Monterrico”
Super
A veces se requiere llamar a un método de la superclase. Eso se realiza con la
palabra reservada super. Si this hace referencia a la clase actual, super hace
referencia a la superclase respecto a la clase actual, con lo que es un método
imprescindible para poder acceder a métodos anulados por herencia.
Ejemplo
public class vehiculo{
double velocidad;
public void acelerar(double cantidad){
velocidad+=cantidad;
}
}
public class coche extends vehiculo{
double gasolina;
public void acelerar(double cantidad){
super.acelerar(cantidad);
gasolina*=0.9;
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
76
Instituto de Educación Superior
“San Ignacio de Monterrico”
En el ejemplo anterior, la llamada super.acelerar(cantidad) llama al método
acelerar de la clase vehículo (el cual acelerará la marcha). Es necesario redefinir
el método acelerar en la clase coche ya que aunque la velocidad varía igual que
en la superclase, hay que tener en cuenta el consumo de gasolina.
Se puede incluso llamar a un constructor de una superclase, usando la sentencia
super().
Ejemplo:
public class vehiculo{
double velocidad;
public vehiculo(double v){
velocidad=v;
}
}
public class coche extends vehiculo{
double gasolina;
public coche(double v, double g){
super(v);
//Llama al constructor de la clase vehiculo gasolina=g
}
}
Por defecto Java realiza estas acciones:
o Si la primera instrucción de un constructor de una subclase es una
sentencia que no es ni super ni this, Java añade de forma invisible e
implícita una llamada super() al constructor por defecto de la superclase,
luego inicia las variables de la subclase y luego sigue con la ejecución
normal.
o Si se usa super(..) en la primera instrucción, entonces se llama al
constructor seleccionado de la superclase, luego inicia las propiedades de
la subclase y luego sigue con el resto de sentencias del constructor.
o Finalmente, si esa primera instrucción es this(..), entonces se llama
al constructor seleccionado por medio de this, y después continúa con las
Formando Emprendedores De Calidad Para Un Mundo Empresarial
77
Instituto de Educación Superior
“San Ignacio de Monterrico”
sentencias del constructor. La inicialización de variables la habrá realizado
el constructor al que se llamó mediante this.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
78
Instituto de Educación Superior
“San Ignacio de Monterrico”
EXCEPCIONES
Introducción a las excepciones
Uno de los problemas más importantes al escribir aplicaciones es el tratamiento
de los errores. Errores no previstos que distorsionan la ejecución del
programa.
Las excepciones de Java hacen referencia e este hecho. Se
denomina excepción a una situación que no se puede resolver y que provoca la
detención del programa; es decir una condición de error en tiempo de ejecución
(es decir cuando el programa ya ha sido compilado y se está ejecutando).
Ejemplos:
•
•
•
El archivo que queremos abrir no existe
Falla la conexión a una red
La clase que se desea utilizar no se encuentra en ninguno de los paquetes
reseñados con import
Los errores de sintaxis son detectados durante la compilación. Pero las
excepciones pueden provocar situaciones irreversibles,
su
control
debe
hacerse en tiempo de ejecución y eso presenta un gran problema. En Java se
puede preparar el código susceptible a provocar errores de ejecución de modo
que si ocurre una excepción, el código es lanzado (throw) a una determinada
rutina previamente preparada por el programador, que permite manipular esa
excepción. Si la excepción no fuera capturada, la ejecución del programa se
detendría irremediablemente.
En Java hay muchos tipos de excepciones (de operaciones de entrada y salida,
de operaciones irreales. El paquete java.lang.Exception y sus subpaquetes
contienen todos los tipos de excepciones.
Cuando se produce un error se genera un objeto asociado a esa excepción. Este
objeto es de la clase Exception o de alguna de sus herederas. Este objeto se
pasa al código que se ha definido para manejar la excepción. Dicho código puede
manipular las propiedades del objeto Exception.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
79
Instituto de Educación Superior
“San Ignacio de Monterrico”
Hay una clase, la java.lang.Error y sus subclases que sirven para definir los
errores irrecuperables más serios. Esos errores causan parada en el programa,
por lo que el programador no hace falta que los manipule. Estos errores les
produce el sistema y son incontrolables para el programador. Las excepciones
son fallos más leves, y más manipulables.
La estructura Try – match.
Las sentencias que tratan las excepciones son try y catch.
La sintaxis es:
try {
instrucciones que se ejecutan salvo que haya un error
}
catch (ClaseExcepción objetoQueCapturaLaExcepción) {
instrucciones que se ejecutan si hay un error}
Puede haber más de una sentencia catch para un mismo bloque try.
Ejemplo:
try {
readFromFile(“arch”);
}
catch(FileNotFoundException e) {
//archivo no encontrado
}
catch (IOException e) {
}
Jerarquía de Excepciones
Formando Emprendedores De Calidad Para Un Mundo Empresarial
80
Instituto de Educación Superior
“San Ignacio de Monterrico”
Dentro del bloque try se colocan las instrucciones susceptibles de provocar una
excepción, el bloque catch sirve para capturar esa excepción y evitar el fin de la
ejecución del programa. Desde el bloque catch se maneja, en definitiva, la
excepción.
Cada catch maneja un tipo de excepción. Cuando se produce una excepción, se
busca el catch que posea el manejador de excepción adecuado, será el que
utilice el mismo tipo de excepción que se ha producido. Esto puede causar
problemas si no se tiene cuidado, ya que la clase Exception es la superclase de
todas las demás. Por lo que si se produjo, por ejemplo, una excepción de tipo
AritmethicException y el primer catch captura el tipo genérico Exception, será ese
catch el que se ejecute y no los demás.
Por eso el último catch debe ser el que capture excepciones genéricas y los
primeros deben ser los más específicos. Lógicamente si vamos a tratar a todas
las excepciones (sean del tipo que sean) igual, entonces basta con un solo catch
que capture objetos Exception.
Manejo de excepciones
Formando Emprendedores De Calidad Para Un Mundo Empresarial
81
Instituto de Educación Superior
“San Ignacio de Monterrico”
Siempre se debe controlar una excepción, de otra forma nuestro software está a
merced de los fallos. En la programación siempre ha habido dos formas de
manejar la excepción:
•
Interrupción. En este caso se asume que el programa ha encontrado un
error irrecuperable. La operación que dio lugar a la excepción se anula y
se entiende que no hay manera de regresar al código que provocó la
excepción. Es decir, la operación que dio pies al error, se anula.
•
Reanudación. Se puede manejar el error y regresar de nuevo al código
que
provocó el error.
La filosofía de Java es del tipo interrupción, pero se puede intentar emular la
reanudación encerrando el bloque try en un while que se repetirá hasta que el
error deje de existir.
Ejemplo:
boolean indiceNoValido=true;
int i; //Entero que tomará números aleatorios de 0 a 9
String texto[]={“Uno”,”Dos”,”Tres”,”Cuatro”,”Cinco”};
while(indiceNoValido){
try{
i=Math.round(Math.random()*9);
System.out.println(texto[i];
indiceNoValido=false;
}catch(ArrayIndexOutOfBoundsException exc){
System.out.println(“Fallo en el índice”);
}
}
En el código anterior, el índice i calcula un número del 0 al 9 y con ese número el
código accede al array texto que sólo contiene 5 elementos. Esto producirá muy a
menudo una excepción del tipo ArrayIndexOutOfBoundsException que es
manejada por el catch correspondiente. Normalmente no se continuaría
Formando Emprendedores De Calidad Para Un Mundo Empresarial
82
Instituto de Educación Superior
“San Ignacio de Monterrico”
intentando. Pero como tras el bloque catch está dentro del while, se hará otro
intento y así hasta que no haya excepción, lo que provocará que indiceNovalido
valga true y la salida, al fin, del while.
Como se observa en la ¡Error! No se encuentra el origen de la referencia., la
clase Exception es la superclase de todos los tipos de excepciones. Esto permite
utilizar una serie de métodos comunes a todas las clases de excepciones:
•
String getMessage(). Obtiene el mensaje descriptivo de la excepción o una
indicación específica del error ocurrido:
try{
} catch (IOException ioe){
System.out.println(ioe.getMessage());
}
•
String toString(). Escribe una cadena sobre la situación de la excepción.
Suele indicar la clase de excepción y el texto de getMessage().
•
void printStackTrace(). Escribe el método y mensaje de la excepción (la
llamada información de pila). El resultado es el mismo mensaje que
muestra el ejecutor (la máquina virtual de Java) cuando no se controla la
excepción.
Uso del throws
Al llamar a métodos, ocurre un problema con las excepciones. El problema es, si
el método da lugar a una excepción, ¿quién la maneja? ¿El propio método? ¿O el
código que hizo la llamada al método?
Con lo visto hasta ahora, sería el propio método quien se encargara de sus
excepciones, pero esto complica el código. Por eso otra posibilidad es hacer que
la excepción la maneje el código que hizo la llamada.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
83
Instituto de Educación Superior
“San Ignacio de Monterrico”
Esto se hace añadiendo la palabra throws tras la primera línea de un método.
Tras
esa palabra se indica qué excepciones puede provocar el código del método. Si
ocurre una excepción en el método, el código abandona ese método y regresa al
código desde el que se llamó al método. Allí se posará en el catch apropiado para
esa excepción.
Ejemplo:
void usarArchivo (String archivo) throws IOException, InterruptedException
{...
En este caso se está indicando que el método usarArchivo puede provocar
excepciones del tipo IOException y InterruptedException. Esto significará,
además, que el que utilice este método debe preparar el catch correspondiente
para manejar los posibles errores.
Ejemplo:
try{
objeto.usarArchivo(“C:\texto.txt”);//puede haber excepción
}
catch(IOException ioe){..
}
catch(InterruptedException ie){...
}
...//otros catch para otras posibles excepciones
Uso del throw
Esta instrucción nos permite lanzar a nosotros nuestras propias excepciones (o lo
que es lo mismo, crear artificialmente nosotros las excepciones). Ante:
throw new Exception();
Formando Emprendedores De Calidad Para Un Mundo Empresarial
84
Instituto de Educación Superior
“San Ignacio de Monterrico”
El flujo del programa se dirigirá a la instrucción try/catch más cercana. Se pueden
utilizar constructores en esta llamada (el formato de los constructores depende de
la clase que se utilice):
throw new Exception(“Error grave, grave”);
Eso construye una excepción con el mensaje indicado. throw permite también
relanzar excepciones. Esto significa que dentro de un catch podemos colocar una
instrucción throw para lanzar la nueva excepción que será capturada por el catch
correspondiente:
try{
} catch(ArrayIndexOutOfBoundsException exc){
throw new IOException();
} catch(IOException){
}
El segundo catch capturará también las excepciones del primer tipo
Uso del finally
La cláusula finally está pensada para limpiar el código en caso de excepción. Su
uso es:
try{
}catch (FileNotFoundException fnfe){
}catch(IOException ioe){
}catch(Exception e){
}finally{
...//Instrucciones de limpieza
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
85
Instituto de Educación Superior
“San Ignacio de Monterrico”
Las
sentencias
finally
se
ejecutan
tras
haberse
ejecutado
el
catch
correspondiente. Si ningún catch capturó la excepción, entonces se ejecutarán
esas sentencias antes de devolver el control al siguiente nivel o antes de
romperse la ejecución.
Hay que tener muy en cuenta
que las sentencias finally se ejecutan
independientemente de si hubo o no excepción. Es decir esas sentencias se
ejecutan siempre, haya o no excepción. Son sentencias a ejecutarse en todo
momento. Por ello se coloca en el bloque finally código común para todas las
excepciones (y también para cuando no hay excepciones).
Clases Envoltorio (wrapper)
No son más que clases que modelan los tipos de datos primitivos tales como
enteros y flotantes, precisamente estos tipos primitivos son los únicos elementos
en Java que no son clases.
Cabe destacar una diferencia notable entre los tipos primitivos y sus wrapper: Los
tipos primitivos se pasan como argumento a los métodos por valor, mientras que
los objetos se pasan por referencia. Esto implica una ventaja en cuanto a
eficiencia.
Por otra parte estos wrapper ofrecen métodos de conversión muy convenientes.
Estas clases se encuentran en java.lang, derivan de Number que deriva de
Object. Cada una de estas clases contiene un valor primitivo del tipo relacionado,
por ejemplo un objeto Integer contiene un int como atributo.
En Java se dice que todo es considerado un objeto. Para hacer que esta filosofía
sea más real se han diseñado una serie de clases relacionadas con los tipos
básicos. El nombre de estas clases es:
Clase
java.lang.Void
java.lang.Boolean
java.lang.Character
java.lang.Byte
java.lang.Short
java.lang.Integer
Representa al tipo básico..
void
boolean
char
byte
short
int
Formando Emprendedores De Calidad Para Un Mundo Empresarial
86
Instituto de Educación Superior
“San Ignacio de Monterrico”
java.lang.Long
java.lang.Float
java.lang.Double
long
float
double
Hay que tener en cuenta que no son equivalentes a los tipos básicos. La creación
de estos tipos lógicamente requiere usar constructores, ya que son objetos y no
tipos básicos.
Double n=new Double(18.3);
Double o=new Double(“18.5”);
El constructor admite valores del tipo básico relacionado e incluso valores String
que contengan texto convertible a ese tipo básico. Si ese texto no es convertible,
ocurre una excepción del tipo NumberFormatException.
La conversión de un String a un tipo básico es una de las utilidades básicas de
estas clases, por ello estas clases poseen el método estático valueOf entre
otros para convertir un String en uno de esos tipos.
Ejemplos:
String s=”2500”;
Integer a=Integer.valueOf(s); Short b=Short.valueOf(s);
Double c=Short.valueOf(s);
Byte d=Byte.valueOf(s);//Excepción!!!
Hay otro método en cada una de esas clases que se llama parse. La diferencia
estriba en que en los métodos parse la conversión se realiza hacia tipos básicos
(int, double, float, boolean,...) y no hacia las clase anteriores.
Ejemplo:
String s=”2500”;
int y=Integer.parseInt(s);
short z=Short.parseShort(s);
double c=Short.parseDouble(s); byte x=Byte.parseByte(s);
Formando Emprendedores De Calidad Para Un Mundo Empresarial
87
Instituto de Educación Superior
“San Ignacio de Monterrico”
Estos métodos son todos estáticos. Todas las clases además poseen métodos
dinámicos para convertir a otros tipos (intValue, longValue,... o el conocido
toString).
Todos estos métodos lanzan excepciones del tipo NumberFormatException, que
habrá que capturar con el try y el catch pertinentes.
Además han redefinido el método equals para comparar objetos de este tipo.
Además poseen el método compareTo que permite comparar dos elementos de
este tipo.
Algunos métodos de Integer.
El constructor de un wrapper está sobrecargado para aceptar el tipo de
dato primitivo que va a contener o un objeto String.
Integer(int)
Integer(String)
Integer(int)
Integer(String)
Entre sus métodos encontramos algunos que nos permiten recuperar el
tipo primitivo que queramos.
•
doubleValue()
•
floatValue()
•
longValue()
•
intValue()
•
shortValue()
•
byteValue()
También tenemos conversores con la clase String.
•
String toString()
•
Integer valueOf(String)
Formando Emprendedores De Calidad Para Un Mundo Empresarial
88
Instituto de Educación Superior
“San Ignacio de Monterrico”
Además todos estos métodos pueden utilizarse en forma estática, esto es,
no es necesario crear una instancia para utilizarlos. Aunque esto no es del
todo cierto, ya que por ejemplo como sucede con el método valueOf(),
retorna una instancia del wrapper. Por lo tanto estas clases wrapper son
fabricantes de objetos (ver factory pattern).
Estas clases también declaran constantes que definen su valor máximo, su
valor mínimo, etc.
Conversiones entre distintos tipos de datos básicos
He creado esta clase para conversiones con el fin de agrupar estos métodos y
mostrar los simples pasos de conversión que debemos seguir.
Básicamente el procedimiento en cada uno de los métodos es el mismo: crear
una instancia del wrapper del argumento, con el valor de dicho argumento, y
luego solicitar el tipo de dato primitivo a retornar por el método de conversión.
final class Cast
{
/*float a int*/
public static int toInt(float f)
{
int i;
i = Float.valueOf(f).intValue();
return i;
}
/*double a int*/
public static int toInt(double d)
{
int i;
i = Double.valueOf(d).intValue();
return i;
}
/*int a double*/
Formando Emprendedores De Calidad Para Un Mundo Empresarial
89
Instituto de Educación Superior
“San Ignacio de Monterrico”
public static double toDouble(int i)
{
double d;
d = Integer.valueOf(i).doubleValue();
return d;
}
/*String a double*/
public static double toDouble(String s)
{
double d;
d = Double.valueOf(s).doubleValue();
return d;
}
/*float a String*/
public static String toString(float f)
{
String s;
s = Float.valueOf(f).toString();
return s;
}
}
Si bien esta clase está incompleta (le faltarían muchos métodos más para ampliar
su utilidad) podemos hacer un ejemplo y ver como trabaja.
int i;
float f;
double d;
String s = "9.543";
d = Cast.toDouble(s); // d = 9.543
i = Cast.toInt(d); // i = 9
f = i; // f = 9.0
s = Cast.toString(f); // s = "9.0"
Formando Emprendedores De Calidad Para Un Mundo Empresarial
90
Instituto de Educación Superior
“San Ignacio de Monterrico”
En conclusión estas clases son muy valiosas, aprender a utilizarlas es muy facil
(sus métodos son muy similares, recuerden que derivan de Number) y nos
proveen de la funcionalidad extra que se necesita.
La clase String
Para Java las cadenas de texto son objetos especiales. Los textos deben
manejarse creando objetos de tipo String. Ejemplo:
String texto1 = “¡Prueba de texto!”;
Las
cadenas
pueden
ocupar
varias
líneas
utilizando
el
operador
de
concatenación “+”.
String texto2 =”Este es un texto que ocupa “ + “varias líneas, no obstante
se puede “+ “perfectamente encadenar”;
También se pueden crear objetos String sin utilizar constantes entrecomilladas,
usando otros constructores:
char[] palabra = {‘P’,’a’,’l’,’b’,’r’,’a’};//Array de char
String cadena = new String(palabra);
byte[] datos = {97,98,99};
String codificada = new String (datos, “8859_1”);
En el último ejemplo la cadena codificada se crea desde un array de tipo byte que
contiene números que serán interpretados como códigos Unicode. Al asignar, el
valor 8859_1 indica la tabla de códigos a utilizar.
Comparación Entre Objetos String
Los objetos String no pueden compararse directamente con los
operadores de comparación. En su lugar se deben utilizar estas
expresiones:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
91
Instituto de Educación Superior
“San Ignacio de Monterrico”
•
cadena1.equals(cadena2). El resultado es true si la
cadena1 es igual a la cadena2. Ambas cadenas son variables de
tipo String.
•
cadena1.equalsIgnoreCase(cadena2). Como la anterior,
pero en este caso no se tienen en cuentas mayúsculas y
minúsculas.
•
s1.compareTo(s2).
Compara
ambas
cadenas,
considerando el orden alfabético. Si la primera cadena es mayor en
orden alfabético que la segunda devuelve 1, si son iguales devuelve
0 y si es la segunda la mayor devuelve -1. Hay que tener en cuenta
que el orden no es el del alfabeto español, sino que usa la tabla
ASCII, en esa tabla la letra ñ es mucho mayor que la o.
•
s1.compareToIgnoreCase(s2). Igual que la anterior, sólo
que además ignora las mayúsculas (disponible desde Java 1.2)
String.valueOf
Este método pertenece no sólo a la clase String, sino a otras y siempre es
un método que convierte valores de una clase a otra. En el caso de los
objetos String, permite convertir valores que no son de cadena a forma de
cadena. Ejemplos:
String numero = String.valueOf(1234);
String fecha = String.valueOf(new Date());
En el ejemplo se observa que este método pertenece a la clase String
directamente, no hay que utilizar el nombre del objeto creado (como se
verá más adelante, es un método estático).
Métodos De Las Variables De Cadena
Son métodos que poseen las propias variables de cadena. Para utilizarlos
basta con poner el nombre del método y sus parámetros después del
nombre de la variable String.
Es decir: variableString.método(argumentos)
length
Formando Emprendedores De Calidad Para Un Mundo Empresarial
92
Instituto de Educación Superior
“San Ignacio de Monterrico”
Permite devolver la longitud de una cadena (el número de caracteres de la
cadena):
String texto1=”Prueba”;
System.out.println(texto1.length());//Escribe 6
Concatenar Cadenas
Se puede hacer de dos formas, utilizando el método concat o con el
operador +. Ejemplo:
String s1=”Buenos ”, s2=”días”, s3, s4;
s3 = s1 + s2;
s4 = s1.concat(s2);
charAt
Devuelve un carácter de la cadena. El carácter a devolver se indica por su
posición (el primer carácter es la posición 0) Si la posición es negativa o
sobrepasa el tamaño de la cadena, ocurre un error de ejecución, una
excepción tipo IndexOutOfBounds-Exception. Ejemplo:
String s1=”Prueba”;
char c1=s1.charAt(2); //c1 valdrá ‘u’
substring
Da como resultado una porción del texto de la cadena. La porción se toma
desde una posición inicial hasta una posición final (sin incluir esa posición
final). Si las posiciones indicadas no son válidas ocurre una excepción de
tipo IndexOutOfBounds-Exception. Se empieza a contar desde la
posición 0.
Ejemplo:
String s1=”Buenos días”;
String s2=s1.substring(7,10); //s2 = día
indexOf
Formando Emprendedores De Calidad Para Un Mundo Empresarial
93
Instituto de Educación Superior
“San Ignacio de Monterrico”
Devuelve la primera posición en la que aparece un determinado texto en la
cadena. En el caso de que la cadena buscada no se encuentre, devuelve
-1. El texto a buscar puede ser char o String. Ejemplo:
String s1=”Quería decirte que quiero que te vayas”;
System.out.println(s1.indexOf(“que”)); //Da 15
Se puede buscar desde una determinada posición. En el ejemplo anterior:
System.out.println(s1.indexOf(“que”,16)); //Ahora da 26
lastIndexOf
Devuelve la última posición en la que aparece un determinado texto en la
cadena. Es casi idéntica a la anterior, sólo que busca desde el final.
Ejemplo:
String s1=”Quería decirte que quiero que te vayas”;
System.out.println(s1.lastIndexOf(“que”); //Da 26
También permite comenzar a buscar desde una determinada posición.
endsWith
Devuelve true si la cadena termina con un determinado texto. Ejemplo:
String s1=”Quería decirte que quiero que te vayas”;
System.out.println(s1.endsWith(“vayas”); //Da true
startsWith
Devuelve true si la cadena empieza con un determinado texto.
replace
Cambia todas las apariciones de un carácter por otro en el texto que se
indique y lo almacena como resultado. El texto original no se cambia, por
lo que hay que asignar el resultado de replace a un String para almacenar
el texto cambiado:
String s1=”Mariposa”;
System.out.println(s1.replace(‘a’,’e’));//Da Meripose
System.out.println(s1);//Sigue valiendo Mariposa
Formando Emprendedores De Calidad Para Un Mundo Empresarial
94
Instituto de Educación Superior
“San Ignacio de Monterrico”
replaceAll
Modifica en un texto cada entrada de una cadena por otra y devuelve el
resultado. El primer parámetro es el texto que se busca (que puede ser
una expresión regular), el segundo parámetro es el texto con el que se
reemplaza el buscado. La cadena original no se modifica.
String s1=”Cazar armadillos”;
System.out.println(s1.replace(“ar”,”er”));//Da Cazer ermedillos
System.out.println(s1);//Sigue valiendo Cazar armadilos
toUpperCase
Devuelve la versión en mayúsculas de la cadena.
toLowerCase
Devuelve la versión en minúsculas de la cadena.
toCharArray
Obtiene un array de caracteres a partir de una cadena.
La clase Arrays
En el paquete java.utils se encuentra una clase estática llamada Arrays. Una
clase estática permite ser utilizada como si fuera un objeto (como ocurre con
Math). Esta clase posee métodos muy interesantes para utilizar sobre arrays.
Su uso es
Arrays.método(argumentos);
fill
Permite rellenar todo un array unidimensional con un determinado valor.
Sus argumentos son el array a rellenar y el valor deseado:
int valores[]=new int[23];
Arrays.fill(valores,-1);//Todo el array vale -1
También permite decidir desde que índice hasta qué índice rellenamos:
Arrays.fill(valores,5,8,-1);//Del elemento 5 al 7 valdrán -1
Formando Emprendedores De Calidad Para Un Mundo Empresarial
95
Instituto de Educación Superior
“San Ignacio de Monterrico”
equals
Compara dos arrays y devuelve true si son iguales. Se consideran iguales
si son del mismo tipo, tamaño y contienen los mismos valores.
sort
Permite ordenar un array en orden ascendente. Se pueden ordenar sólo
una serie de elementos desde un determinado punto hasta un determinado
punto.
int x[]={4,5,2,3,7,8,2,3,9,5};
Arrays.sort(x);//Estará ordenado
Arrays.sort(x,2,5);//Ordena del 2º al 4º elemento
binarySearch
Permite buscar un elemento de forma ultrarrápida en un array ordenado
(en un array desordenado sus resultados son impredecibles). Devuelve el
índice en el que está colocado el elemento. Ejemplo:
int x[]={1,2,3,4,5,6,7,8,9,10,11,12};
Arrays.sort(x);
System.out.println(Arrays.binarySearch(x,8));//Da 7
El método System.arraysCopy
La clase System también posee un método relacionado con los arrays,
dicho método permite copiar un array en otro. Recibe cinco argumentos: el
array que se copia, el índice desde que se empieza a copia en el origen, el
array destino de la copia, el índice desde el que se copia en el destino, y el
tamaño de la copia (número de elementos de la copia).
int uno[]={1,1,2};
int dos[]={3,3,3,3,3,3,3,3,3};
System.arraycopy(uno, 0, dos, 0, uno.length);
for (int i=0;i<=8;i++){
System.out.print(dos[i]+" ");
} //Sale 112333333
Formando Emprendedores De Calidad Para Un Mundo Empresarial
96
Instituto de Educación Superior
“San Ignacio de Monterrico”
Arrays Unidimensionales
Un array es una colección de valores de un mismo tipo engrosados en la
misma variable. De forma que se puede acceder a cada valor
independientemente. Para Java además un array es un objeto que tiene
propiedades que se pueden manipular.
Los arrays solucionan problemas concernientes al manejo de muchas
variables que se refieren a datos similares. Por ejemplo si tuviéramos la
necesidad de almacenar las notas de una clase con 18 alumnos,
necesitaríamos 18 variables, con la tremenda lentitud de manejo que
supone eso. Solamente calcular la nota media requeriría una tremenda
línea de código. Almacenar las notas supondría al menos 18 líneas de
código.
Gracias a los arrays se puede crear un conjunto de variables con el mismo
nombre. La diferencia será que un número (índice del array) distinguirá a
cada variable. En el caso de las notas, se puede crear un array llamado
notas, que representa a todas las notas de la clase. Para poner la nota del
primer alumno se usaría notas[0], el segundo sería notas[1], etc. (los
corchetes permiten especificar el índice en concreto del array).
La declaración de un array unidimensional se hace con esta sintaxis.
tipo nombre[];
Ejemplo:
double cuentas[]; //Declara un array que almacenará valores
doubles
Declara un array de tipo double. Esta declaración indica para qué servirá el
array, pero no reserva espacio en la RAM al no saberse todavía el tamaño
del mismo.
Tras la declaración del array, se tiene que iniciar. Eso lo realiza el operador
new, que es el que realmente crea el array indicando un tamaño. Cuando
Formando Emprendedores De Calidad Para Un Mundo Empresarial
97
Instituto de Educación Superior
“San Ignacio de Monterrico”
se usa new es cuando se reserva el espacio necesario en memoria. Un
array no inicializado es un array null. Ejemplo:
int notas[]; //sería válido también int[] notas;
notas = new int[3]; //indica que el array constará de tres
//valores de tipo int
//También se puede hacer todo a la vez
//int notas[]=new int[3];
En el ejemplo anterior se crea un array de tres enteros (con los tipos
básicos se crea en memoria el array y se inicializan los valores, los
números se inician a 0).
Los valores del array se asignan utilizando el índice del mismo entre
corchetes:
notas[2]=8;
También se pueden asignar valores al array en la propia declaración:
int notas[] = {8, 7, 9};
int notas2[]= new int[] {8,7,9};//Equivalente a la anterior
Esto declara e inicializa un array de tres elementos. En el ejemplo lo que
significa es que notas[0] vale 8, notas[1] vale 7 y notas[2] vale 9.
En Java (como en otros lenguajes) el primer elemento de un array es el
cero. El primer elemento del array notas, es notas[0]. Se pueden declarar
arrays a cualquier tipo de datos (enteros, booleanos, doubles, ... e incluso
objetos).
La ventaja de usar arrays (volviendo al caso de las notas) es que gracias a
un simple bucle for se puede rellenar o leer fácilmente todos los elementos
de un array:
//Calcular la media de las 18 notas
suma=0;
for (int i=0;i<=17;i++){
suma+=nota[i];
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
98
Instituto de Educación Superior
“San Ignacio de Monterrico”
media=suma/18;
A un array se le puede inicializar las veces que haga falta:
int notas[]=new notas[16];
...
notas=new notas[25];
Pero hay que tener en cuenta que el segundo new hace que se pierda el
contenido anterior. Realmente un array es una referencia a valores que se
almacenan en memoria mediante el operador new, si el operador new se
utiliza en la misma referencia, el anterior contenido se queda sin referencia
y, por lo tanto se pierde.
Un array se puede asignar a otro array (si son del mismo tipo):
int notas[];
int ejemplo[]=new int[18];
notas=ejemplo;
En el último punto, notas equivale a ejemplo. Esta asignación provoca que
cualquier cambio en notas también cambie el array ejemplos. Es decir esta
asignación anterior, no copia los valores del array, sino que notas y
ejemplo son referencias al mismo array.
Ejemplo:
int notas[]={3,3,3};
int ejemplo[]=notas;
ejemplo= notas;
ejemplo[0]=8;
System.out.println(notas[0]);//Escribirá el número 8
Arrays Multidimensionales
Los arrays además pueden tener varias dimensiones. Entonces se habla
de arrays de
arrays (arrays que contienen arrays) Ejemplo:
int notas[][];
Formando Emprendedores De Calidad Para Un Mundo Empresarial
99
Instituto de Educación Superior
“San Ignacio de Monterrico”
notas es un array que contiene arrays de enteros
notas = new int[3][12];//notas está compuesto por 3 arrays
//de 12 enteros cada uno
notas[0][0]=9;//el primer valor es 0
Puede haber más dimensiones incluso (notas[3][2][7]). Los arrays
multidimensionales se pueden inicializar de forma más creativa incluso.
Ejemplo:
int notas[][]=new int[5][];//Hay 5 arrays de enteros
notas[0]=new int[100]; //El primer array es de 100 enteros
notas[1]=new int[230]; //El segundo de 230
notas[2]=new int[400];
notas[3]=new int[100];
notas[4]=new int[200];
Hay que tener en cuenta que en el ejemplo anterior, notas[0] es un array
de 100 enteros. Mientras que notas, es un array de 5 arrays de enteros.
Se pueden utilizar más de dos dimensiones si es necesario.
Longitud De Un Array
Los arrays poseen un método que permite determinar cuánto mide un
array. Se trata de length. Ejemplo (continuando del anterior):
System.out.println(notas.length); //Sale 5
System.out.println(notas[2].length); //Sale 400
La clase Math.
Se echan de menos operadores matemáticos más potentes en Java. Por ello se
ha incluido una clase especial llamada Math dentro del paquete java.lang Para
poder utilizar esta clase, se debe incluir esta instrucción:
import java.lang.Math;
Esta clase posee métodos muy interesantes para realizar cálculos matemáticos
complejos. Por ejemplo:
double x= Math.pow(3,3); //x es 27
Math posee dos constantes, que son:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
100
Instituto de Educación Superior
“San Ignacio de Monterrico”
Math.E
Math.PI
para la base exponencial, aproximadamente 2.72
para PI, aproximadamente 3.14
Por otro lado posee numerosos métodos que son:
Método
Math.abs( x )
Math.sin( double a )
Math.cos( double a )
Math.tan( double a )
Math.asin( double r )
Math.acos( double r )
Math.atan( double r )
Math.atan2(double a,double b)
Math.exp( double x )
Math.log( double x )
Math.sqrt( double x )
Math.ceil( double a )
Math.floor( double a )
Math.rint( double a )
Math.pow( double x,double y )
Math.round( x )
Math.random()
Math.max( a,b )
Math.min( a,b )
COLECCIONES.
Descripción
Para int, long, float y double.
Devuelve el seno del ángulo a en
radianes.
Devuelve el coseno del ángulo a en
radianes.
Devuelve la tangente del ángulo a en
radianes.
Devuelve el ángulo cuyo seno es r.
Devuelve el ángulo cuyo coseno es r.
Devuelve el ángulo cuya tangente es
r.
Devuelve el ángulo cuya tangente es
a/b.
Devuelve e elevado a x.
Devuelve el logaritmo natural de x.
Devuelve la raíz cuadrada de x.
Devuelve el número completo más
pequeño mayor o igual que a.
Devuelve el número completo más
grande menor o igual que a.
Devuelve el valor double truncado de
a
Devuelve x elevado a y.
Para double y float.
Devuelve un double entre 0 y 1.
Para int, long, float y double.
Para int, long, float y double.
Estructuras estáticas de datos y estructuras dinámicas
En prácticamente todos los lenguajes de computación existen estructuras
para almacenar colecciones de datos. Esto es una serie de datos agrupados a los
que se puede hacer referencia con un único nombre. Ejemplo de ello son los
arrays. El problema del uso de los arrays es que es una estructura estática, esto
significa que se debe saber el número de elementos que formarán parte de esa
Formando Emprendedores De Calidad Para Un Mundo Empresarial
101
Instituto de Educación Superior
“San Ignacio de Monterrico”
colección a priori, es decir en tiempo de compilación hay que decidir el tamaño de
un array.
Las estructuras dinámicas de datos tienen la ventaja de que sus integrantes se
deciden en tiempo de ejecución y que el número de elementos es ilimitado. Estas
estructuras dinámicas son clásicas en la programación y son las colas, pilas,
listas enlazadas, árboles, grafos, etc. En muchos lenguajes se implementan
mediante punteros; como Java no posee punteros se crearon clases especiales
para implementar estas funciones.
En Java desde la primera versión se incluyeron las clases: vector, Stack,
Hashtable, BitSet y la interfaz Enumeration. En Java 2 se modificó este
funcionamiento y se potenció la creación de estas clases.
Trabajo con Colecciones
Cuando se necesitan características más sofisticadas para almacenar objetos,
que las que proporciona un simple array, Java pone a disposición las clases
colección: Vector, BitSet, Stack y Hashtable.
Entre
otras
características,
las
clases
colección
se
redimensionan
automáticamente, por lo que se puede colocar en ellas cualquier número de
objetos, sin necesidad de tener que ir controlando continuamente en el programa
la longitud de la colección.
La gran desventaja del uso de las colecciones en Java es que se pierde la
información de tipo cuando se coloca un objeto en una colección. Esto ocurre
porque cuando se escribió la colección, el programador de esa colección no tenía
ni idea del tipo de datos específicos que se iban a colocar en ella, y teniendo en
mente el hacer una herramienta lo más general posible, se hizo que manejase
directamente objetos de tipo Object, que es el objeto raíz de todas las clases en
Java. La solución es perfecta, excepto por dos razones:
1. Como la información de tipo se pierde al colocar un objeto en la colección,
cualquier tipo de objeto se va a poder colar en ella, es decir, si la colección
está destinada a contener animales mamíferos, nada impide que se pueda
colar un coche en ella.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
102
Instituto de Educación Superior
“San Ignacio de Monterrico”
2. Por la misma razón de la pérdida de tipo, la única cosa que sabe la
colección es que maneja un Object. Por ello, hay que colocar siempre un
moldeo al tipo adecuado antes de utilizar cualquier objeto contenido en
una colección.
La verdad es que no todo es tan negro, Java no permite que se haga uso
inadecuado de los objetos que se colocan en una colección. Si se introduce un
coche en una colección de animales mamíferos, al intentar extraer el coche se
obtendrá una excepción. Y del mismo modo, si se intenta colocar un moldeo al
coche que se está sacando de la colección para convertirlo en animal mamífero,
también se obtendrá una excepción en tiempo de ejecución.
Ejemplo #
import java.util.*;
class Coche {
private int numCoche;
Coche( int i ) {
numCoche = i;
}
void print() {
System.out.println( "Coche #"+numCoche );
}
}
class Barco {
private int numBarco;
Barco( int i ) {
numBarco = i;
}
void print() {
System.out.println( "Barco #"+numBarco );
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
103
Instituto de Educación Superior
“San Ignacio de Monterrico”
}
public class java411 {
public static void main( String args[] ) {
Vector coches = new Vector();
for( int i=0; i < 7; i++ )
coches.addElement( new Coche( i ) );
//No hay ningun problema en añadir un barco a los coches
coches.addElement( new Barco( 7 ) );
for( int i=0; i < coches.size(); i++ )
(( Coche )coches.elementAt( i ) ).print();
//El barco solamente es detectado en tiempo de ejecucion
}
}
Como se puede observar, el uso de un Vector es muy sencillo: se crea uno, se
colocan elementos en él con el método addElement() y se recuperan con el
método elementAt(). Vector tiene el método size() que permite conocer cuántos
elementos contiene, para evitar el acceso a elementos fuera de los límites del
Vector y obtener una excepción.
Las clases Coche y Barco son distintas, no tienen nada en común excepto que
ambas son Object. Si no se indica explícitamente de la clase que se está
heredando, automáticamente se hereda de Object. La clase Vector maneja
elementos de tipo Object, así que no solamente es posible colocar en ella objetos
Coche utilizando el método addElement(), sino que también se pueden colocar
elementos de tipo Barco sin que haya ningún problema ni en tiempo de
compilación ni a la hora de ejecutar el programa. Cuando se recupere un objeto
que se supone es un Coche utilizando el método elementAt() de la clase Vector,
hay que colocar un moldeo para convertir el objeto Object en el Coche que se
espera, luego hay que colocar toda la expresión entre paréntesis para forzar la
evaluación del moldeo antes de llamar al método print() de la clase Coche, sino
habrá un error de sintaxis. Posteriormente, ya en tiempo de ejecución, cuando se
Formando Emprendedores De Calidad Para Un Mundo Empresarial
104
Instituto de Educación Superior
“San Ignacio de Monterrico”
intente moldear un objeto Barco a un Coche, se generará una excepción, tal
como se puede comprobar en las siguientes líneas, que reproducen la salida de
la ejecución del ejemplo:
%java java411
Coche #0
Coche #1
Coche #2
Coche #3
Coche #4
Coche #5
Coche #6
java.lang.ClassCastException: Barco
at java411.main(java411.java:54)
Lo cierto es que esto es un fastidio, porque puede ser la fuente de errores que
son muy difíciles de encontrar. Si en una parte, o en varias partes, del programa
se insertan elementos en la colección, y se descubre en otra parte diferente del
programa que se genera una excepción es porque hay algún elemento erróneo
en la colección, así que hay que buscar el sitio donde se ha insertado el elemento
de la discordia, lo cual puede llevar a intensas sesiones de depuración. Así que,
para enredar al principio, es mejor empezar con clases estandarizadas en vez de
aventurarse en otras más complicadas, a pesar de que estén menos optimizadas.
Jerarquia de Colecciones
Formando Emprendedores De Calidad Para Un Mundo Empresarial
105
Instituto de Educación Superior
“San Ignacio de Monterrico”
Enumeraciones
En cualquier clase de colección, debe haber una forma de meter cosas y otra de
sacarlas; después de todo, la principal finalidad de una colección es almacenar
cosas. En un Vector, el método addElement() es la manera en que se colocan
objetos dentro de la colección y llamando al método elementAt() es cómo se
sacan. Vector es muy flexible, se puede seleccionar cualquier cosa en cualquier
momento y seleccionar múltiples elementos utilizando diferentes índices.
Si se quiere empezar a pensar desde un nivel más alto, se presenta un
inconveniente: la necesidad de saber el tipo exacto de la colección para utilizarla.
Esto no parece que sea malo en principio, pero si se empieza implementando un
Vector a la hora de desarrollar el programa, y posteriormente se decide cambiarlo
a List, por eficiencia, entonces sí es problemático.
El concepto de enumerador, o iterador, que es su nombre más común en C++ y
OOP, puede utilizarse para alcanzar el nivel de abstracción que se necesita en
este caso. Es un objeto cuya misión consiste en moverse a través de una
secuencia de objetos y seleccionar aquellos objetos adecuados sin que el
Formando Emprendedores De Calidad Para Un Mundo Empresarial
106
Instituto de Educación Superior
“San Ignacio de Monterrico”
programador cliente tenga que conocer la estructura de la secuencia. Además, un
iterador es normalmente un objeto ligero, lightweight, es decir, que consumen
muy pocos recursos, por lo que hay ocasiones en que presentan ciertas
restricciones; por ejemplo, algunos iteradores solamente se puede mover en una
dirección.
La Enumeration en Java es un ejemplo de un iterador con esas características, y
las cosas que se pueden hacer son:
o Crear una colección para manejar una Enumeration utilizando el método
elements(). Esta Enumeration estará lista para devolver el primer elemento
en la secuencia cuando se llame por primera vez al método nextElement().
o Obtener el siguiente elemento en la secuencia a través del método
nextElement().
o Ver
si
hay
más
elementos
en
la
secuencia
con
el
método
hasMoreElements().
Y esto es todo. No obstante, a pesar de su simplicidad, alberga bastante poder.
Para ver cómo funciona, el ejemplo java412.java, es la modificación de anterior,
en que se utilizaba el método elementAt() para seleccionar cada uno de los
elementos. Ahora se utiliza una enumeración para el mismo propósito, y el único
código interesante de este nuevo ejemplo es el cambio de las líneas del ejemplo
original
for( int i=0; i < coches.size(); i++ )
(( Coche )coches.elementAt( i ) ).print();
Por estas otras en que se utiliza la enumeración para recorrer la secuencia de
objetos
while( e.hasMoreElements() )
(( Coche )e.nextElement()).print();
Con la Enumeration no hay que preocuparse del número de elementos que
contenga la colección, ya que del control sobre ellos se encargan los métodos
hasMoreElements() y nextElement().
Formando Emprendedores De Calidad Para Un Mundo Empresarial
107
Instituto de Educación Superior
“San Ignacio de Monterrico”
Tipos de Colecciones
Con el JDK 1.0 y 1.1 se proporcionaban librerías de colecciones muy básicas,
aunque suficientes para la mayoría de los proyectos. En el JDK 1.2 en adelante
ya se amplía esto y, además, las anteriores colecciones han sufrido un profundo
rediseño. A continuación se verán cada una de ellas por separado para dar una
idea del potencial que se ha incorporado a Java.
Vector
El Vector es muy simple y fácil de utilizar. Aunque los métodos más
habituales en su manipulación son addElement() para insertar elementos
en el Vector, elementAt() para recuperarlos y elements() para obtener una
Enumeration con el número de elementos del Vector, lo cierto es que hay
más métodos, pero no es el momento de relacionarlos todos, así que, al
igual que sucede con todas las librerías de Java, se remite al lector a que
consulte la documentación electrónica que proporciona Javasoft, para
conocer los demás métodos que componen esta clase.
Las colecciones estándar de Java contienen el método toString(), que
permite obtener una representación en forma de String de sí mismas,
incluyendo los objetos que contienen. Dentro de Vector, por ejemplo,
toString() va saltando a través de los elementos del Vector y llama al
método toString() para cada uno de esos elementos. En caso, por poner
un ejemplo, de querer imprimir la dirección de la clase, parecería lógico
referirse a ella simplemente como this (los programadores C++ estarán
muy inclinados a esta posibilidad), así que tendríamos el código que
muestra el ejemplo java413.java y que se reproduce en las siguientes
líneas.
import java.util.*;
public class java413 {
public String toString() {
return( "Direccion del objeto: "+this+"\n" );
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
108
Instituto de Educación Superior
“San Ignacio de Monterrico”
public static void main( String args[] ) {
Vector v = new Vector();
for( int i=0; i < 10; i++ )
v.addElement( new java413() );
System.out.println( v );
}
}
El ejemplo no puede ser más sencillo, simplemente crea un objeto de tipo
java413 y lo imprime; sin embargo, a la hora de ejecutar el programa lo
que se obtiene es una secuencia infinita de excepciones. Lo que está
pasando es que cuando se le indica al compilador:
"Direccion del objeto: "+this
El compilador ve un String seguido del operador + y otra cosa que no es
un String, así que intenta convertir this en un String. La conversión la
realiza llamando al método toString() que genera una llamada recursiva,
llegando a llenarse la pila.
Si realmente se quiere imprimir la dirección del objeto en este caso, la
solución pasa por llamar al método toString() de la clase Object. Así, si en
vez de this se coloca super.toString(), el ejemplo funcionará. En otros
casos, este método también funcionará siempre que se esté heredando
directamente de Object o, aunque no sea así, siempre que ninguna clase
padre haya sobreescrito el método toString().
BitSet
Se llama así lo que en realidad es un Vector de bits. Lo que ocurre es que
está optimizado para uso de bits. Bueno, optimizado en cuanto a tamaño,
porque en lo que respecta al tiempo de acceso a los elementos, es
bastante más lento que el acceso a un array de elementos del mismo tipo
básico.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
109
Instituto de Educación Superior
“San Ignacio de Monterrico”
Además, el tamaño mínimo de un BitSet es de 64 bits. Es decir, que si se
está almacenando cualquier otra cosa menor, por ejemplo de 8 bits, se
estará desperdiciando espacio.
En un Vector normal, la colección se expande cuando se añaden más
elementos. En el BitSet ocurre los mismo pero ordenadamente. El ejemplo
java414.java, muestra el uso de esta colección.
Se utiliza el generador de números aleatorios para obtener un byte, un
short y un int, que son convertidos a su patrón de bits e incorporados al
BitSet.
Stack
Un Stack es una Pila, o una colección de tipo LIFO (last-in, first-out). Es
decir, lo último que se coloque en la pila será lo primero que se saque.
Como en todas las colecciones de Java, los elementos que se introducen y
sacan de la pila son Object, así que hay que tener cuidado con el moldeo a
la hora de sacar alguno de ellos.
Los diseñadores de Java, en vez de utilizar un Vector como bloque para
crear un Stack, han hecho que Stack derive directamente de Vector, así
que tiene todas las características de un Vector más alguna otra propia ya
del Stack. El ejemplo siguiente, java415.java, es una demostración muy
simple del uso de una Pila que consisten en leer cada una de las líneas de
un array y colocarlas en un String.
Cada línea en el array diasSemana se inserta en el Stack con push() y
posteriormente se retira con pop(). Para ilustrar una afirmación anterior,
también se utilizan métodos propios de Vector sobre el Stack. Esto es
posible ya que en virtud de la herencia un Stack es un Vector, así que
todas las operaciones que se realicen sobre un Vector también se podrán
realizar sobre un Stack, como por ejemplo, elementAt().
Formando Emprendedores De Calidad Para Un Mundo Empresarial
110
Instituto de Educación Superior
“San Ignacio de Monterrico”
Hashtable
Un Vector permite selecciones desde una colección de objetos utilizando
un número, luego parece lógico pensar que hay números asociados a los
objetos. Bien, entonces ¿qué es lo que sucede cuando se realizan
selecciones utilizando otros criterios? Un Stack podría servir de ejemplo:
su criterio de selección es "lo último que se haya colocado en el Stack".
Si rizamos la idea de "selección desde una secuencia", nos encontramos
con un mapa, un diccionario o un array asociativo. Conceptualmente, todo
parece ser un vector, pero en lugar de acceder a los objetos a través de un
número, en realidad se utiliza otro objeto. Esto nos lleva a utilizar claves y
al procesado de claves en el programa. Este concepto se expresa en Java
a través de la clase abstracta Dictionary. El interfaz para esta clase es muy
simple:
•
size(), indica cuántos elementos contiene,
•
isEmpty(), es true si no hay ningún elemento,
•
put( Object clave,Object valor), añade un valor y lo asocia con una
clave
•
get( Object clave ), obtiene el valor que corresponde a la clave que
se indica
•
remove( Object clave ), elimina el par clave-valor de la lista
•
keys(), genera una Enumeration de todas las claves de la lista
•
elements(), genera una Enumeration de todos los valores de la lista
Todo es lo que corresponde a un Diccionario (Dictionary), que no es
excesivamente difícil de implementar. El ejemplo java416.java es una
aproximación muy simple que utiliza dos Vectores, uno para las claves y
otro para los valores que corresponden a esas claves.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
111
Instituto de Educación Superior
“San Ignacio de Monterrico”
import java.util.*;
public class java416 extends Dictionary {
private Vector claves = new Vector();
private Vector valores = new Vector();
public int size() {
return( claves.size() );
}
public boolean isEmpty() {
return( claves.isEmpty() );
}
public Object put( Object clave,Object valor ) {
claves.addElement( clave );
valores.addElement( valor );
return( clave );
}
public Object get( Object clave ) {
int indice = claves.indexOf( clave );
// El metodo indexOf() devuelve -1
// si no encuentra la clave que se
// esta buscando
if( indice == -1 )
return( null );
return( valores.elementAt( indice ) );
}
public Object remove(Object clave) {
int indice = claves.indexOf( clave );
if( indice == -1 )
return( null );
Formando Emprendedores De Calidad Para Un Mundo Empresarial
112
Instituto de Educación Superior
“San Ignacio de Monterrico”
claves.removeElementAt( indice );
Object valorRetorno = valores.elementAt( indice );
valores.removeElementAt( indice );
return( valorRetorno );
}
public Enumeration keys() {
return( claves.elements() );
}
public Enumeration elements() {
return( valores.elements() );
}
// Ahora es cuando se prueba el ejemplo
public static void main( String args[] ) {
java416 ej = new java416();
for( char c='a'; c <= 'z'; c++ )
ej.put( String.valueOf( c ),String.valueOf( c ).toUpperCase() )
;
char[] vocales = { 'a','e','i','o','u' };
for( int i=0; i < vocales.length; i++ )
System.out.println( "Mayusculas: " +
ej.get( String.valueOf( vocales[i] ) ) );
}
}
La primera cosa interesante que se puede observar en la definición de
java416 es que extiende a Dictionary. Esto significa que java416 es un tipo
de Diccionario, con lo cual se pueden realizar las mismas peticiones y
llamar a los mismos métodos que a un Diccionario. A la hora de
construirse un Diccionario propio todo lo que se necesita es rellenar todos
Formando Emprendedores De Calidad Para Un Mundo Empresarial
113
Instituto de Educación Superior
“San Ignacio de Monterrico”
los métodos que hay en Dictionary. Se deben sobreescribir todos ellos,
excepto el constructor, porque todos son abstractos.
Los Vectores claves y valores están relacionados a través de un número
índice común. Es decir, si se llama al método put() con la clave "león" y el
valor "rugido" en la asociación de animales con el sonido que producen, y
ya hay 100 elementos en la clase java416, entonces "león" será el
elemento 101 de claves y "rugido" será el elemento 101 de valores. Y
cuando se pasa al método get() como parámetro "león", genera el número
índice con claves.indexOf(), y luego utiliza este índice para obtener el valor
asociado en el vector valores.
Para mostrar el funcionamiento, en main() se utiliza algo tan simple como
mapear las letras minúsculas y mayúsculas, que aunque se pueda hacer
de otras formas más eficientes, sí sirve para mostrar el funcionamiento de
la clase, que es lo que se pretende por ahora.
La librería estándar de Java solamente incorpora una implementación de
un Dictionary, la Hashtable. Esta Hashtable tiene el mismo interfaz básico
que la clase del ejemplo anterior java416, ya que ambas heredan de
Dictionary, pero difiere en algo muy importante: la eficiencia. Si en un
Diccionario se realiza un get() para obtener un valor, se puede observar
que la búsqueda es bastante lenta a través del vector de claves. Aquí es
donde la Hashtable acelera el proceso, ya que en vez de realizar la tediosa
búsqueda línea a línea a través del vector de claves, utiliza un valor
especial llamado código hash. El código hash es una forma de conseguir
información sobre el objeto en cuestión y convertirlo en un int
relativamente único para ese objeto. Todos los objetos tienen un código
hash y hashCode() es un método de la clase Object. Una Hashtable coge
el hashCode() del objeto y lo utiliza para cazar rápidamente la clave. El
resultado es una impresionante reducción del tiempo de búsqueda. La
tabla Hash es un Diccionario muy rápido y que un Diccionario es una
herramienta muy útil.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
114
Instituto de Educación Superior
“San Ignacio de Monterrico”
Para ver el funcionamiento de la tabla Hash está el ejemplo java417.java,
que intenta comprobar la aleatoriedad del método Math.random().
Idealmente, debería producir una distribución perfecta de números
aleatorios, pero para poder comprobarlo sería necesario generar una
buena cantidad de números aleatorios y comprobar los rangos en que
caen. Una Hashtable es perfecta para este propósito al asociar objetos con
objetos, en este caso, los valores producidos por el método Math.random()
con el número de veces en que aparecen esos valores.
import java.util.*;
class Contador {
int i = 1;
public String toString() {
return( Integer.toString( i ) );
}
}
class java417 {
public static void main( String args[] ) {
Hashtable ht = new Hashtable();
for( int i=0; i < 10000; i++ ) {
// Genera un número cuasi-aleatorio entre 0 y 20
Integer r = new Integer( (int)( Math.random()*20 ) );
if( ht.containsKey( r ) )
( (Contador)ht.get( r ) ).i++;
else
ht.put( r,new Contador() );
}
System.out.println( ht );
}
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
115
Instituto de Educación Superior
“San Ignacio de Monterrico”
En el método main() del ejemplo, cada vez que se genera un número
aleatorio, se convierte en objeto Integer para que pueda ser manejado por
la tabla Hash, ya que no se pueden utilizar tipos básicos con una
colección, porque solamente manejan objetos. El método containsKey()
comprueba si la clave se encuentra ya en la colección. En caso afirmativo,
el método get() obtiene el valor asociado a la clave, que es un objeto de
tipo Contador. El valor i dentro del contador se incrementa para indicar que
el número aleatorio ha aparecido una vez más.
Si la clave no se encuentra en la colección, el método put() colocará el
nuevo par clave-valor en la tabla Hash. Como Contador inicializa
automáticamente su variable i a 1 en el momento de crearla, ya se indica
que es la primera vez que aparece ese número aleatorio concreto.
Para presentar los valores de la tabla Hash, simplemente se imprimen. El
método toString() de Hashtable navega a través de los pares clave-valor y
llama a método toString() de cada uno de ellos. El método toString() de
Integer está predefinido, por lo que no hay ningún problema en llamar a
toString() para Contador. Un ejemplo de ejecución del programa sería la
salida que se muestra a continuación:
%java java417
{19=526, 18=533, 17=460, 16=513, 15=521, 14=495, 13=512,
12=483,
11=488, 10=487, 9=514, 8=523, 7=497, 6=487, 5=489, 3=509,
2=503,
1=475, 0=505}
Al lector le puede parecer superfluo el uso de la clase Contador, que
parece que no hace nada que no haga ya la clase Integer. ¿Por qué no
utilizar int o Integer? Pues bien, int no puede utilizarse porque como ya se
ha indicado antes, las colecciones solamente manejan objetos, por ello
están las clases que envuelven a esos tipos básicos y los convierten en
objetos. Sin embargo, la única cosa que pueden hacer estas clases es
inicializar los objetos a un valor determinado y leer ese valor. Es decir, no
Formando Emprendedores De Calidad Para Un Mundo Empresarial
116
Instituto de Educación Superior
“San Ignacio de Monterrico”
hay modo alguno de cambiar el valor de un objeto correspondiente a un
tipo básico, una vez que se ha creado. Esto hace que la clase Integer sea
inútil para resolver el problema que plantea el ejemplo, así que la creación
de la clase Contador es imprescindible. Quizás ahora que el lector sabe
que no puede colocar objetos creados a partir de las clases
correspondientes a tipos básicos en colecciones, estas clases tengan un
poco menos de valor, pero... la vida es así, por un lado da y por otro
quita... y Java no va a ser algo diferente.
En el ejemplo se utiliza la clase Integer, que forma parte de la librería
estándar de Java como clave para la tabla Hash, y funciona perfectamente
porque tiene todo lo necesario para funcionar como clave. Pero un error
muy común se presenta a la hora de crear clases propias para que
funcionen como claves. Por ejemplo, supóngase que se quiere
implementar un sistema de predicción del tiempo en base a objetos de tipo
Oso y tipo Prediccion, para detectar cuando entra la primavera. Tal como
se muestra en el ejemplo java418.java, la cosa parece muy sencilla, se
crean las dos clases y se utiliza Oso como clave y Prediccion como valor.
Cada Oso tendrá un número de identificación, por lo que sería factible
buscar una Prediccion en la tabla Hash de la forma: "Dime la Prediccion
asociada con el Oso número 3". La clase Prediccion contiene un booleano
que es inicializado utilizando Math.random(), y una llamada al método
toString() convierte el resultado en algo legible. En el método main(), se
rellena una Hashtable con los Osos y sus Predicciones asociadas. Cuando
la tabla Hash está completa, se imprime. Y ya se hace la consulta anterior
sobre la tabla para buscar la Prediccion que corresponde al Oso número 3.
Esto parece simple y suficiente, pero no funciona. El problema es que Oso
deriva directamente de la clase raíz Object, que es lo que ocurre cuando
no se especifica una clase base, que en última instancia se hereda de
Object. Luego es el método hashCode() de Object el que se utiliza para
generar el código hash para cada objeto que, por defecto, utiliza la
dirección de ese objeto. Así, la primera instancia de Oso(3) no va a
producir un código hash igual que producirá una segunda instancia de
Formando Emprendedores De Calidad Para Un Mundo Empresarial
117
Instituto de Educación Superior
“San Ignacio de Monterrico”
Oso(3), con lo cual no se puede utilizar para obtener buenos resultados de
la tabla.
Se puede seguir pensando con filosofía ahorrativa y decir que todo lo que
se necesita es sobreescribir el método hashCode() de la forma adecuada y
ya está. Pero, esto tampoco va a funcionar hasta que se haga una cosa
más: sobreescribir el método equals(), que también es parte de Object.
Este es el método que utiliza la tabla Hash para determinar si la clave que
se busca es igual a alguna de las claves que hay en la tabla. De nuevo, el
método Object.equals() solamente compara direcciones de objetos, por lo
que un Oso(3) probablemente no sea igual a otro Oso(3).
Por lo tanto, a la hora de escribir clases propias que vayan a funcionar
como clave en una Hastable, hay que sobreescribir los métodos
hashCode() y equals(). El ejemplo java419.java ya se incorporan estas
circunstancias.
import java.util.*;
// Si se crea una clase que utilice una clave en una Tabla Hash, es
// imprescindible sobreescribir los metodos hashCode() y equals()
// Utilizamos un oso para saber si está hibernando en su temporada
de
// invierno o si ya tine que despertarse porque le llega la primavera
class Oso2 {
int numero;
Oso2( int n ) {
numero = n;
}
public int hashCode() {
return( numero );
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
118
Instituto de Educación Superior
“San Ignacio de Monterrico”
public boolean equals( Object obj ) {
if( (obj != null) && (obj instanceof Oso2) )
return( numero == ((Oso2)obj).numero );
else
return( false );
}
}
// En función de la oscuridad, o claridad del día, pues intenta saber
si
// ya ha la primavera ha asomado a nuestras puertas
class Prediccion {
boolean oscuridad = Math.random() > 0.5;
public String toString() {
if( oscuridad )
return( "Seis semanas mas de Invierno!" );
else
return( "Entrando en la Primavera!" );
}
}
public class java419 {
public static void main(String args[]) {
Hashtable ht = new Hashtable();
for( int i=0; i < 10; i++ )
ht.put( new Oso2( i ),new Prediccion() );
System.out.println( "ht = "+ht+"\n" );
System.out.println( "Comprobando la prediccion para el oso
#3:");
Oso2 oso = new Oso2( 3 );
if( ht.containsKey( oso ) )
System.out.println( (Prediccion)ht.get( oso ) );
}
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
119
Instituto de Educación Superior
“San Ignacio de Monterrico”
El método hashCode() devuelve el número que corresponde a un Oso
como un identificador, siendo el programador el responsable de que no
haya dos números iguales. El método hashCode() no es necesario que
devuelva un identificador, sino que eso es necesario porque equals() debe
ser capaz de determinar estrictamente cuando dos objetos son
equivalentes.
El método equals() realiza dos comprobaciones adicionales, una para
comprobar si el objeto es null, y, en caso de que no lo sea, comprobar que
sea una instancia de Oso, para poder realizar las comparaciones, que se
basan en los números asignados a cada objeto Oso. Cuando se ejecuta
este nuevo programa, sí se produce la salida correcta. Hay muchas clases
de la librería de Java que sobreescriben los métodos hashCode() y
equals() basándose en el tipo de objetos que son capaces de crear.
Las tablas Hash son utilizadas también por muchas clases de la librería
estándar de Java, por ejemplo, para obtener las propiedades del sistema
se usa la clase Properties que hereda directamente de Hashtable. Y
además, contiene una segunda Hashtable en donde guarda las
propiedades del sistema que se usan por defecto.
Para el autor, las colecciones son una de las herramientas más poderosas
que se pueden poner en manos de un programador. Por ello, las
colecciones que incorporaba Java, adolecían de precariedad y de
demasiada rapidez en su desarrollo. Por todo ello, para quien escribe esto
ha sido una tremenda satisfacción comprobar las nuevas colecciones que
incorpora el JDK 1.2, y ver que incluso las antiguas han sido rediseñadas.
Probablemente, las colecciones, junto con la librería Swing, son las dos
cosas más importantes que aporta la versión 1.2 del JDK, y ayudarán
enormemente a llevar a Java a la primera línea de los lenguajes de
programación.
Hay cambios de diseño que hacen su uso más simple. Por ejemplo,
muchos nombres son más cortos, más claros y más fáciles de entender; e
Formando Emprendedores De Calidad Para Un Mundo Empresarial
120
Instituto de Educación Superior
“San Ignacio de Monterrico”
incluso algunos de ellos han sido cambiados totalmente para adaptarse a
la terminología habitual.
El rediseño también incluye la funcionalidad, pudiendo encontrar ahora
listas enlazadas y colas. El diseño de una librería de colecciones es
complicado y difícil. En C++, la Standard Template Library (STL) cubría la
base con muchas clases diferentes. Desde luego, esto es mejor que
cuando no hay nada, pero es difícil de trasladar a Java porque el resultado
llevaría a tal cantidad de clases que podría ser muy confuso. En el otro
extremo, hay librerías de colecciones que constan de una sola clase,
Collection, que actúa como un Vector y una Hashtable al mismo tiempo.
Los diseñadores de las nuevas colecciones han intentado mantener un
difícil equilibrio: por un lado disponer de toda la funcionalidad que el
programador espera de una buena librería de colecciones, y, por otro, que
sea tan fácil de aprender y utilizar como la STL y otras librerías similares.
El resultado puede parecer un poco extraño en ocasiones, pero, al
contrario que en las librerías anteriores al JDK 1.2, no son decisiones
accidentales, sino que están tomadas a conciencia en función de la
complejidad. Es posible que se tarde un poco en sentirse cómodo con
algunos de los aspectos de la librería, pero de seguro que el programador
intentará adoptar rápidamente estos nuevos métodos. Hay que reconocer
que Joshua Bloch de Sun, ha hecho un magnífico trabajo en el rediseño de
esta librería.
La nueva librería de colecciones parte de la premisa de almacenar objetos,
y diferencia dos conceptos en base a ello:
•
Colección (Collection): un grupo de elementos
individuales, siempre con alguna regla que se les puede aplicar. Una
List almacenará objetos en una secuencia determinada y, un Set no
permitirá elementos duplicados.
•
Mapa (Map): un grupo de parejas de objetos clave-
valor, como la Hastable ya vista. En principio podría parecer que
esto es una Collection de parejas, pero cuando se intenta
Formando Emprendedores De Calidad Para Un Mundo Empresarial
121
Instituto de Educación Superior
“San Ignacio de Monterrico”
implementar, este diseño se vuelve confuso, por lo que resulta
mucho más claro tomarlo como un concepto separado. Además, es
conveniente consultar porciones de un Map creando una Collection
que represente a esa porción; de este modo, un Map puede devolver
un Set de sus claves, una List de sus valores, o una List de sus
parejas clave-valor. Los Mapas, al igual que los arrays, se pueden
expandir fácilmente en múltiples dimensiones sin la incorporación de
nuevos conceptos: simplemente se monta un Map cuyos valores son
Mapas, que a su vez pueden estar constituidos por Mapas, etc.
Las Colecciones y los Mapas pueden ser implementados de muy diversas
formas, en función de las necesidades concretas de programación, por lo
que puede resultar útil el siguiente diagrama de herencia de las nuevas
colecciones que utiliza la notación gráfica propugnada por la metodología
OMT (Object Modeling Technique).
El diagrama está hecho a partir de la versión beta del JDK 1.2, así que
puede haber cosas cambiadas con respecto a la versión final. Quizás
también, un primer vistazo puede abrumar al lector, pero a lo largo de la
sección se comprobará que es bastante simple, porque solamente hay tres
colecciones: Map, List y Set; y solamente dos o tres implementaciones de
cada una de ellas. Las cajas punteadas representan interfaces y las
sólidas representan clases normales, excepto aquellas en que el texto
interior comienza por Abstract, que representan clases abstractas. Las
flechas indican que una clase puede generar objetos de la clase a la que
apunta; por ejemplo, cualquier Collection puede producir un Iterator,
mientras que una List puede producir un ListIterator (al igual que un
Iterator normal, ya que List hereda de Collection).
Los interfaces que tienen que ver con el almacenamiento de datos son:
Collection, Set, List y Map. Normalmente, un programador creará casi todo
su código para entenderse con estos interfaces y solamente necesitará
indicar específicamente el tipo de datos que se están usando en el
momento de la creación. Por ejemplo, una Lista se puede crear de la
siguiente forma:
List lista = new LinkedList();
Formando Emprendedores De Calidad Para Un Mundo Empresarial
122
Instituto de Educación Superior
“San Ignacio de Monterrico”
Desde luego, también se puede decidir que lista sea una lista enlazada, en
vez de una lista genérica, y precisar más el tipo de información de la lista.
Lo bueno, y la intención, del uso de interfaces es que si ahora se decide
cambiar la implementación de la lista, solamente es necesario cambiar el
punto de creación, por ejemplo:
List lista = new ArrayList();
el resto del código permanece invariable.
En la jerarquía de clases, se pueden ver algunas clases abstractas que
pueden confundir en un principio. Son simplemente herramientas que
implementan parcialmente un interfaz. Si el programador quiere hacer su
propio Set, por ejemplo, no tendría que empezar con el interfaz Set e
implementar todos los métodos, sino que podría derivar directamente de
AbstractSet y ya el trabajo para crear la nueva clase es mínimo. Sin
embargo, la nueva librería de colecciones contiene suficiente funcionalidad
para satisfacer casi cualquier necesidad, así que en este Tutorial se
ignorarán las clases abstractas.
Por lo tanto, a la hora de sacar provecho del diagrama es suficiente con lo
que respecta a los interfaces y a las clases concretas. Lo normal será
construir un objeto correspondiente a una clase concreta, moldearlo al
correspondiente interfaz y ya usas ese interfaz en el resto del código. El
ejemplo java420.java es muy simple y consiste en una colección de
objetos String que se imprimen.
import java.util.*;
public class java420 {
public static void main( String args[] ) {
Collection c = new ArrayList();
for( int i=0; i < 10; i++ )
c.add( Integer.toString( i ) );
Iterator it = c.iterator();
while( it.hasNext() )
System.out.println( it.next() );
}
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
123
Instituto de Educación Superior
“San Ignacio de Monterrico”
Como las nuevas colecciones forman parte del paquete java.util, no es
necesario importar ningún paquete adicional para utilizarlas.
A continuación se comentan los trozos interesantes del código del ejemplo.
La primera línea del método main() crea un objeto ArrayList y lo moldea a
una Collection. Como este ejemplo solamente utiliza métodos de
Collection, cualquier objeto de una clase derivada de Collection debería
funcionar, pero se ha cogido un ArrayList porque es el caballo de batalla
de las colecciones y viene a tomar el relevo al Vector.
El método add(), como su nombre sugiere, coloca un nuevo elemento en la
colección. Sin embargo, la documentación indica claramente que add()
"asegura que la colección contiene el elemento indicado". Esto es para que
un Set tenga significado, ya que solamente añadirá el elemento si no se
encuentra en la colección. Con un ArrayList, o cualquier otra lista
ordenada, add() significa siempre "colocarlo dentro".
Todas las colecciones pueden producir un Iterator invocando al método
iterator(). Un Iterator viene a ser equivalente a una Enumeration, a la cual
reemplaza, excepto en los siguientes puntos:
1.
Utiliza
un
nombre
que
está
históricamente
aceptado y es conocido en toda la literatura de programación
orientada a objetos
2.
Utiliza nombres de métodos más cortos que la Enumeration:
hasNext() en vez de hasMoreElements(), o next() en lugar de
nextElement()
3.
Añade un nuevo método, remove(), que permite
eliminar el último elemento producido por el Iterator. Solamente se
puede llamar a remove() una vez por cada llamada a next()
En el ejemplo se utiliza un Iterator para desplazarse por la colección e ir
imprimiendo cada uno de sus elementos.
Colecciones
Formando Emprendedores De Calidad Para Un Mundo Empresarial
124
Instituto de Educación Superior
“San Ignacio de Monterrico”
A continuación se indican los métodos que están disponibles para las
colecciones, es decir, lo que se puede hacer con un Set o una List, aunque las
listas tengan funcionalidad añadida que ya se verá, y Map no hereda de
Collection, así que se tratará aparte.
boolean add( Object )
Asegura que la colección contiene el argumento. Devuelve false si no se puede
añadir el argumento a la colección
boolean addAll( Collection )
Añade todos los elementos que se pasan en el argumento. Devuelve true si es
capaz de incorporar a la colección cualquiera de los elementos del argumento
void clear()
Elimina todos los elementos que componen la colección
boolean contains( Object )
Verdadero si la colección contiene el argumento que se pasa como parámetro
boolean isEmpty()
Verdadero si la colección está vacía, no contiene elemento alguno
Iterator iterator()
Devuelve un Iterator que se puede utilizar para desplazamientos a través de los
elementos que componen la colección
boolean remove( Object )
Si el argumento está en la colección, se elimina una instancia de ese elemento y
se devuelve true si se ha conseguido
boolean removeAll( Collection )
Elimina todos los elementos que están contenidos en el argumento. Devuelve
true si consigue eliminar cualquiera de ellos
boolean retainAll( Collection )
Formando Emprendedores De Calidad Para Un Mundo Empresarial
125
Instituto de Educación Superior
“San Ignacio de Monterrico”
Mantiene solamente los elementos que están contenidos en el argumento, es lo
que sería una intersección en la teoría de conjuntos. Devuelve verdadero en caso
de que se produzca algún cambio
int size()
Devuelve el número de elementos que componen la colección
Object[] toArray()
Devuelve un array conteniendo todos los elementos que forman parte de la
colección. Este es un método opcional, lo cual significa que no está
implementado para una Collection determinada. Si no puede devolver el array,
lanzará una excepción de tipo UnsupportedOperationException
El siguiente ejemplo, java421.java, muestra un ejemplo de todos estos métodos.
De nuevo, recordar que esto funcionaría con cualquier cosa que derive de
Collection, y que se utiliza un ArrayList para mantener un común denominador
solamente.
El primer método proporciona una forma se rellenar la colección con datos de
prueba, en esta caso enteros convertidos a cadenas. El segundo método será
utilizado con bastante frecuencia a partir de ahora.
Las dos versiones de nuevaColeccion() crean ArrayList conteniendo diferente
conjunto de datos que devuelven como objetos Collection, está claro que no se
utiliza ningún otro interfaz diferente de Collection.
El método print() también se usará a menudo a partir de ahora, y lo que hace es
moverse a través de la Colección utilizando un Iterator, que cualquier Collection
puede generar, y funciona con Listas, Conjuntos y Mapas.
El método main() se usa simplemente para llamar a los métodos de la Colección.
Listas
Hay varias implementaciones de List, siendo ArrayList la que debería ser la
elección por defecto, en caso de no tener que utilizar las características que
proporcionan las demás implementaciones.
List (interfaz)
Formando Emprendedores De Calidad Para Un Mundo Empresarial
126
Instituto de Educación Superior
“San Ignacio de Monterrico”
La ordenación es la característica más importante de una Lista,
asegurando que los elementos siempre se mantendrán en una secuencia
concreta. La Lista incorpora una serie de métodos a la Colección que
permiten la inserción y borrar de elementos en medio de la Lista. Además,
en la Lista Enlazada se puede generar un ListIterator para moverse a
través de las lista en ambas direcciones.
ArrayList
Es una Lista volcada en un Array. Se debe utilizar en lugar de Vector como
almacenamiento de objetos de propósito general. Permite un acceso
aleatorio muy rápido a los elementos, pero realiza con bastante lentitud las
operaciones de insertado y borrado de elementos en medio de la Lista. Se
puede utilizar un ListIterator para moverse hacia atrás y hacia delante en la
Lista, pero no para insertar y eliminar elementos.
LinkedList
Proporciona un óptimo acceso secuencial, permitiendo inserciones y
borrado de elementos de en medio de la Lista muy rápidas. Sin embargo
es bastante lento el acceso aleatorio, en comparación con la ArrayList.
Dispone además de los métodos addLast(), getFirst(), getLast(),
removeFirst() y removeLast(), que no están definidos en ningún interfaz o
clase base y que permiten utilizar la Lista Enlazada como una Pila, una
Cola o una Cola Doble.
En el ejemplo java422.java, cubren gran parte de las acciones que se
realizan en las Listas, como moverse con un Iterator, cambiar elementos,
ver los efectos de la manipulación de la Lista y realizar operaciones sólo
permitidas a las Listas Enlazadas.
En testBasico() y moverIter() las llamadas se hacen simplemente para
mostrar la sintaxis correcta, y aunque se recoge el valor devuelto, no se
usa para nada. En otros casos, el valor devuelto no es capturado, porque
no se utiliza normalmente. No obstante, el lector debe recurrir a la
documentación de las clases para comprobar el uso de cualquier método
antes de utilizarlo.
Sets
Formando Emprendedores De Calidad Para Un Mundo Empresarial
127
Instituto de Educación Superior
“San Ignacio de Monterrico”
Set tiene exactamente el mismo interfaz que Collection, y no hay ninguna
funcionalidad extra, como en el caso de las Listas. Un Set es exactamente una
Colección, pero tiene utilizada en un entorno determinado, que es ideal para el
uso de la herencia o el polimorfismo. Un Set sólo permite que exista una instancia
de cada objeto.
A continuación se muestran las diferentes implementaciones de Set, debiendo
utilizarse HashSet en general, a no ser que se necesiten las características
proporcionadas por alguna de las otras implementaciones.
Set (interfaz)
Cada elemento que se añada a un Set debe ser único, ya que el otro caso
no se añadirá porque el Set no permite almacenar elementos duplicados.
Los elementos incorporados al Conjunto deben tener definido el método
equals(), en aras de establecer comparaciones para eliminar duplicados.
Set tiene el mismo interfaz que Collection, y no garantiza el orden en que
se encuentren almacenados los objetos que contenga.
HashSet
Es la elección más habitual, excepto en Sets que sean muy pequeños.
Debe tener definido el método hashCode().
ArraySet
Un Set encajonado en un Array. Esto es útil para Sets muy pequeños,
especialmente aquellos que son creados y destruidos con frecuencia. Para
estos pequeños Sets, la creación e iteración consume muchos menos
recursos que en el caso del HashSet. Sin embargo, el rendimiento es muy
malo en el caso de Sets con gran cantidad de elementos.
TreeSet
Es un Set ordenado, almacenado en un árbol balanceado. En este caso es
muy fácil extraer una secuencia ordenada a partir de un Set de este tipo.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
128
Instituto de Educación Superior
“San Ignacio de Monterrico”
El ejemplo java423.java, no muestra todo lo que se puede hacer con un
Set, sino que como Set es una Collection, y las posibilidades de las
Colecciones ya se han visto, pues el ejemplo se limita a mostrar aquellas
cosas que son particulares de los Sets.
import java.util.*;
public class java423 {
public static void testVisual( Set a ) {
java421.fill( a );
java421.fill( a );
java421.fill( a );
java421.print( a ); // No permite Duplicados!
// Se añade otro Set al anterior
a.addAll( a );
a.add( "uno" );
a.add( "uno" );
a.add( "uno" );
java421.print( a );
// Buscamos ese elemento
System.out.println( "a.contains(\"uno\"): "+a.contains( "uno" ) );
}
public static void main( String args[] ) {
testVisual( new HashSet() );
testVisual( new ArraySet() );
}
}
Aunque se añaden valores duplicados al Set, a la hora de imprimirlos, se
puede observar que solamente se acepta una instancia de cada valor.
Cuando se ejecuta el ejemplo, se observa también que el orden que
Formando Emprendedores De Calidad Para Un Mundo Empresarial
129
Instituto de Educación Superior
“San Ignacio de Monterrico”
mantiene el HashSet es diferente del que presenta el ArraySet, ya que
cada uno tiene una forma de almacenar los elementos para la
recuperación posterior. El ArraySet mantiene los elementos ordenados,
mientras que el HashSet utiliza sus propias funciones para que las
búsquedas sean muy rápidas. Cuando el lector cree sus propios tipos de
estructuras de datos, deberá prestar atención porque un Set necesita
alguna forma de poder mantener el orden de los elementos que lo
integran, como se muestra en el ejemplo siguiente, java424.java.
Las definiciones de los métodos equals() y hashCode() son semejantes a
las de ejemplos anteriores. Se debe definir equals() en ambos casos,
mientras que hashCode() solamente es necesario si la clase corresponde
a un HashSet, que debería ser la primera elección a la hora de
implementar un Set.
Mapas
Los Mapas almacenan información en base a parejas de valores, formados por
una clave y el valor que corresponde a esa clave.
Map (interfaz)
Mantiene las asociaciones de pares clave-valor, de forma que se puede encontrar
cualquier valor a partir de la clave correspondiente.
HashMap
Es una implementación basada en una tabla hash. Proporciona un
rendimiento muy constante a la hora de insertar y localizar cualquier pareja
de valores; aunque este rendimiento se puede ajustar a través de los
constructores que permite fijar la capacidad y el factor de carga de la tabla
hash.
ArrayMap
Es un Mapa circunscrito en un Array. Proporciona un control muy preciso
sobre el orden de iteración. Está diseñado para su utilización con Mapas
muy pequeños, especialmente con aquellos que se crean y destruyen muy
Formando Emprendedores De Calidad Para Un Mundo Empresarial
130
Instituto de Educación Superior
“San Ignacio de Monterrico”
frecuentemente. En este caso de Mapas muy pequeños, la creación e
iteración consume muy pocos recursos del sistema, y muchos menos que
el HashMap. El rendimiento cae estrepitosamente cuando se intentan
manejar Mapas grandes.
TreeMap
Es una implementación basada en un árbol balanceado. Cuando se
observan las claves o los valores, se comprueba que están colocados en
un orden concreto, determinado por Comparable o Comparator, que ya se
verán. Lo importante de un TreeMap es que se pueden recuperar los
elementos en un determinado orden. TreeMap es el único mapa que
define el método subMap(), que permite recuperar una parte del árbol
solamente.
El ejemplo java425.java contiene dos grupos de datos de prueba y un
método rellena() que permite llenar cualquier mapa con cualquier array de
dos dimensiones de Objects.
Los métodos printClaves(), printValores() y print() no son solamente unas
cuantas utilidades, sino que demuestran como se pueden generar
Colecciones que son vistas de un Mapa. El método keySet() genera un Set
que contiene las claves que componen el Mapa; en este caso, es tratado
como una Colección. Tratamiento similar se da a values(), que genera una
List conteniendo todos los valores que se encuentran almacenados en el
Mapa. Observar que las claves deben ser únicas, mientras que los valores
pueden contener elementos duplicados. Debido a que las Colecciones son
dependientes del Mapa, al representar solamente una vista concreta de
parte de los datos del Mapa, cualquier cambio en una Colección se
reflejará inmediatamente en el Mapa asociado.
El método print() recoge el Iterator producido por entries() y lo utiliza para
imprimir las parejas de elementos clave-valor. El resto del ejemplo
proporciona ejemplos muy simples de cada una de las operaciones
permitidas en un Mapa y prueba cada tipo de Mapa.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
131
Instituto de Educación Superior
“San Ignacio de Monterrico”
A la hora de crear Mapas propios, el lector debe tener en cuenta las
mismas recomendaciones que anteriormente se proporcionaban en el
caso de los Sets.
CONECTANDOSE A BASE DE DATOS.
JDBC
Java Database Connectivity (JDBC) es una interfase de acceso a bases de datos
estándar SQL que proporciona un acceso uniforme a una gran variedad de bases
de datos relacionales. JDBC también proporciona una base común para la
construcción de herramientas y utilidades de alto nivel.
JDBC es un API de Java que permite al programador ejecutar instrucciones en
lenguaje estándar de acceso a Bases de Datos, SQL (Structured Query
Language, lenguaje estructurado de consultas), que es un lenguaje de muy alto
nivel que permite crear, examinar, manipular y gestionar Bases de Datos
relacionales. Para que una aplicación pueda hacer operaciones en una Base de
Datos, ha de tener una conexión con ella, que se establece a través de un driver,
que convierte el lenguaje de alto nivel a sentencias de Base de Datos. Es decir,
las tres acciones principales que realizará JDBC son las de establecer la
conexión a una base de datos, ya sea remota o no; enviar sentencias SQL a esa
base de datos y, en tercer lugar, procesar los resultados obtenidos de la base de
datos.
El paquete JDK incluye JDBC a partir de la versión 1.1; El paquete java.sql que
está incluido en la versión JDK 1.2 (conocido como el API JDBC 2.0) incluye
muchas nuevas características no incluidas en el paquete java.sql que forma
parte de la versión JDK 1.1 (referenciado como el API JDBC 1.0). Actualmente
esta API se encuentra en la versión 3.0.
Con el API JDBC 2.0, podremos hacer las siguientes cosas:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
132
Instituto de Educación Superior
“San Ignacio de Monterrico”
•
Ir hacia adelante o hacia atrás en una hoja de resultados o movernos a un
fila específica.
•
Hacer actualizaciones de las tablas de la base datos utilizando métodos
Java en lugar de utilizar comandos SQL.
•
Enviar múltiples secuencias SQL a la base de datos como una unidad, o
batch.
•
Utilizar los nuevos tipos de datos SQL3 como valores de columnas.
DEFINICIÓN DE SQL
El Lenguaje Estructurado de Consulta (SQL) es un conjunto especializado de
órdenes de programación que permiten al programador (o usuario final) realizar
las siguientes tareas:
•
Recuperar datos de una o más tablas de una o mas bases de datos.
•
Manipular datos de tablas insertando, eliminando o actualizando registros.
•
Obtener información de resumen acerca de los datos de las tablas, como
totales, recuento de registros y valores mínimos, máximos y medios.
•
Crear, modificar o eliminar las tablas de una base de datos.
Drivers JDBC
Para usar JDBC con un sistema gestor de base de datos en particular, es
necesario disponer del driver JDBC apropiado que haga de intermediario entre
ésta y JDBC.
JDBC es el API (Interfaz de Programación de la Aplicación) para la ejecución de
sentencias SQL. Consiste en un conjunto de clases e interfases escritas en el
lenguaje de programación Java. JDBC suministra un API estándar para los
desarrolladores y hace posible escribir aplicaciones de base de datos usando un
API puro Java.
Usando JDBC es fácil enviar sentencias SQL virtualmente a cualquier sistema de
base de datos. En otras palabras, con el API JDBC, no es necesario escribir
un programa que acceda a una base de datos Sybase, otro para acceder a
Oracle y otro para acceder a Informix. Un único programa escrito usando el
API JDBC y el programa será capaz de enviar sentencias SQL a la base de
Formando Emprendedores De Calidad Para Un Mundo Empresarial
133
Instituto de Educación Superior
“San Ignacio de Monterrico”
datos apropiada. Y, con una aplicación escrita en el lenguaje de programación
Java, tampoco es necesario escribir diferentes aplicaciones para ejecutar en
diferentes plataformas. La combinación de Java y JDBC permite al programador
escribir una sola vez y ejecutarlo en cualquier entorno.
Java, siendo robusto, seguro, fácil de usar, fácil de entender, y descargable
automáticamente desde la red, es un lenguaje base excelente para
aplicaciones de base de datos.
JDBC expande las posibilidades de Java. Por ejemplo, con Java y JDBC API, es
posible publicar una página web que contenga un applet que usa información
obtenida de una base de datos remota. O una empresa puede usar JDBC para
conectar a todos sus empleados (incluso si usan un conglomerado de máquinas
Windows, Macintosh y UNIX) a una base de datos interna vía intranet. Con cada
vez más y más programadores desarrollando en lenguaje Java, la necesidad de
acceso fácil a base de datos desde Java continúa creciendo.
¿Qué hace JDBC?
Simplemente JDBC hace posible estas tres cosas:
•
Establece una conexión con la base de datos.
•
Envía sentencias SQL
•
Procesa los resultados.
El acceso a base de datos es siempre un punto a considerar por nuestras
aplicaciones. Para esto en Java usamos Java Database Connectivity conocido
por la abreviación JDBC.
Cuando instalamos el JDK (Java Development Kit) automáticamente se instala
esta API (JDBC) y con ella en forma muy sencilla podemos hacer consultas,
mantenimientos y todo tipo de transacciones a una base de datos.
Para la gente del mundo Windows, JDBC es para Java lo que ODBC es para
Windows. Windows en general no sabe nada acerca de las bases de datos, pero
define el estándar ODBC consistente en un conjunto de primitivas que cualquier
driver o fuente ODBC debe ser capaz de entender y manipular. Los
Formando Emprendedores De Calidad Para Un Mundo Empresarial
134
Instituto de Educación Superior
“San Ignacio de Monterrico”
programadores que a su vez deseen escribir programas para manejar bases de
datos genéricas en Windows utilizan las llamadas ODBC.
Con JDBC ocurre exactamente lo mismo: JDBC es una especificación de un
conjunto de clases y métodos de operación que permiten a cualquier programa
Java acceder a sistemas de bases de datos de forma homogénea. Lógicamente,
al igual que ODBC, la aplicación de Java debe tener acceso a un driver JDBC
adecuado. Este driver es el que implementa la funcionalidad de todas las clases
de acceso a datos y proporciona la comunicación entre el API JDBC y la base de
datos real.
La necesidad de JDBC, a pesar de la existencia de ODBC, viene dada porque
ODBC es un interfaz escrito en lenguaje C, que al no ser un lenguaje portable,
haría que las aplicaciones Java también perdiesen la portabilidad. Y además,
ODBC tiene el inconveniente de que se ha de instalar manualmente en cada
máquina; al contrario que los drivers JDBC, que al estar escritos en Java son
automáticamente instalables, portables y seguros.
Toda la conectividad de bases de datos de Java se basa en sentencias SQL, por
lo que se hace imprescindible un conocimiento adecuado de SQL para realizar
cualquier clase de operación de bases de datos. Aunque, afortunadamente, casi
Formando Emprendedores De Calidad Para Un Mundo Empresarial
135
Instituto de Educación Superior
“San Ignacio de Monterrico”
todos los entornos de desarrollo Java ofrecen componentes visuales que
proporcionan una funcionalidad suficientemente potente sin necesidad de que
sea necesario utilizar SQL, aunque para usar directamente el JDK se haga
imprescindible. La especificación JDBC requiere que cualquier driver JDBC sea
compatible con al menos el nivel «de entrada» de ANSI SQL 92 (ANSI SQL 92
Entry Level).
ACCESO A BASE DE DATOS CON JDBC
El API JDBC soporta dos modelos diferentes de acceso a Bases de Datos, los
modelos de dos y tres capas.
Modelo de dos capas
Este modelo se basa en que la conexión entre la aplicación Java o el
applet que se ejecuta en el navegador, se conectan directamente a la base
de datos.
Esto significa que el driver JDBC específico para conectarse con la base
de datos, debe residir en el sistema local. La base de datos puede estar en
cualquier otra máquina y se accede a ella mediante la red. Esta es la
configuración de típica Cliente/Servidor: el programa cliente envía
instrucciones SQL a la base de datos, ésta las procesa y envía los
resultados de vuelta a la aplicación.
Modelo de tres capas
Formando Emprendedores De Calidad Para Un Mundo Empresarial
136
Instituto de Educación Superior
“San Ignacio de Monterrico”
En este modelo de acceso a las bases de datos, las instrucciones son
enviadas a una capa intermedia entre Cliente y Servidor, que es la que se
encarga de enviar las sentencias SQL a la base de datos y recoger el
resultado desde la base de datos. En este caso el usuario no tiene
contacto directo, ni a través de la red, con la máquina donde reside la base
de datos.
Este modelo presenta la ventaja de que el nivel intermedio mantiene en
todo momento el control del tipo de operaciones que se realizan contra la
base de datos, y además, está la ventaja adicional de que los drivers
JDBC no tienen que residir en la máquina cliente, lo cual libera al usuario
de la instalación de cualquier tipo de driver.
Tipos de drivers
Un driver JDBC puede pertenecer a una de cuatro categorías diferentes en
cuanto a la forma de operar, entonces tenemos que considerar que existen 4
formas de usar JDBC para conexión con base de datos:
•
El puente JDBC-ODBC
•
Driver de Java parcialmente nativo
•
Driver JDBC de Java puro
•
Driver de protocolo de Java puro
Formando Emprendedores De Calidad Para Un Mundo Empresarial
137
Instituto de Educación Superior
“San Ignacio de Monterrico”
El puente JDBC-ODBC
Esta forma es la más sencilla y es la que usaremos inicialmente para
comunicarnos con el gestor SQL Server. Para esto necesitamos del ODBC
(Open Database Connectivity) de Microsoft, a través del cual crearemos un
DSN (Data Source Name) que nos permitirá crear una cadena de conexión de
información sobre la base de datos. El ODBC esta hecho en lenguaje C,
mientras que JDBC en Java. El ODBC se comunica con la base de datos y el
JDBC con el ODBC.
La primera categoría de drivers es la utilizada por Sun inicialmente para
popularizar JDBC y consiste en aprovechar todo lo existente, estableciendo
un puente entre JDBC y ODBC. Este driver convierte todas las llamadas
JDBC a llamadas ODBC y realiza la conversión correspondiente de los
resultados.
La ventaja de este driver, que se proporciona con el JDK, es que Java
dispone de acceso inmediato a todas las fuentes posibles de bases de datos y
no hay que hacer ninguna configuración adicional aparte de la ya existente.
No obstante, tiene dos desventajas muy importantes; por un lado, la mayoría
de los drivers ODBC a su vez convierten sus llamadas a llamadas a una
librería nativa del fabricante DBMS, con lo cual la lentitud del driver JDBCODBC puede ser exasperante, al llevar dos capas adicionales que no añaden
funcionalidad alguna; y por otra parte, el puente JDBC-ODBC requiere una
instalación ODBC ya existente y configurada.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
138
Instituto de Educación Superior
“San Ignacio de Monterrico”
Lo anterior implica que para distribuir con seguridad una aplicación Java que
use JDBC habría que limitarse en primer lugar a entornos Windows (donde
está definido ODBC) y en segundo lugar, proporcionar los drivers ODBC
adecuados y configurarlos correctamente. Esto hace que este tipo de drivers
esté totalmente descartado en el caso de aplicaciones comerciales, e incluso
en cualquier otro desarrollo, debe ser considerado como una solución
transitoria, porque el desarrollo de drivers totalmente en Java hará innecesario
el uso de estos puentes.
Java/Binario (Driver de Java parcialmente nativo)
Esta forma está integrada de controladores que se comunican con el servidor
de base de datos en el protocolo nativo del servidor. Por ejemplo para el
gestor DB2 necesitaríamos un driver nativo de DB2 de IBM, Para Informix
necesitaríamos un driver nativo de Informix de Unix. Nuestro JDBC, hecho en
Java se comunicaría con estos drivers.
Este driver se salta la capa ODBC y habla directamente con la librería nativa
del fabricante del sistema DBMS (como pudiera ser DB-Library para Microsoft
SQL Server o CT-Lib para Sybase SQL Server). Este driver es un driver 100%
Java pero aún así necesita la existencia de un código binario (la librería
DBMS) en la máquina del cliente, con las limitaciones y problemas que esto
implica.
Driver JDBC-Net de Java puro (100% Java/Protocolo nativo)
En esta forma los drivers están hechos en Java puro, pero sin embargo
utilizan protocolos estándares, como por ejemplo HTTP, con servidor de base
Formando Emprendedores De Calidad Para Un Mundo Empresarial
139
Instituto de Educación Superior
“San Ignacio de Monterrico”
de datos. El servidor traduce el protocolo de red. Para el caso de Windows,
puede usar ODBC.
Es un driver realizado completamente en Java que se comunica con el
servidor DBMS utilizando el protocolo de red nativo del servidor. De esta
forma, el driver no necesita intermediarios para hablar con el servidor y
convierte todas las peticiones JDBC en peticiones de red contra el servidor.
La ventaja de este tipo de driver es que es una solución 100% Java y, por lo
tanto, independiente de la máquina en la que se va a ejecutar el programa.
Igualmente, dependiendo de la forma en que esté programado el driver,
puede no necesitar ninguna clase de configuración por parte del usuario. La
única desventaja de este tipo de drivers es que el cliente está ligado a un
servidor DBMS concreto, ya que el protocolo de red que utiliza MS SQL
Server por ejemplo no tiene nada que ver con el utilizado por DB2, PostGres u
Oracle. La mayoría de los fabricantes de bases de datos han incorporado a
sus propios drivers JDBC del segundo o tercer tipo, con la ventaja de que no
suponen un coste adicional.
Driver de protocolo de Java puro (100% Java/Protocolo independiente)
En esta última forma, conformada por drivers de java puro, la comunicación es
a través de un protocolo específico para la marca de base de datos que se
usa. Este tipo de conexión es más eficiente que usar ODBC, pero que
requiere un costo monetario en la compra de estos tipos de drivers.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
140
Instituto de Educación Superior
“San Ignacio de Monterrico”
Esta es la opción más flexible, se trata de un driver 100% Java / Protocolo
independiente, que requiere la presencia de un intermediario en el servidor.
En este caso, el driver JDBC hace las peticiones de datos al intermediario en
un protocolo de red independiente del servidor DBMS. El intermediario a su
vez, que está ubicado en el lado del servidor, convierte las peticiones JDBC
en peticiones nativas del sistema DBMS. La ventaja de este método es
inmediata: el programa que se ejecuta en el cliente, y aparte de las ventajas
de los drivers 100% Java, también presenta la independencia respecto al
sistema de bases de datos que se encuentra en el servidor.
De esta forma, si una empresa distribuye una aplicación Java para que sus
usuarios puedan acceder a su servidor MS SQL y posteriormente decide
cambiar el servidor por Oracle, PostGres o DB2, no necesita volver a distribuir
la aplicación, sino que únicamente debe reconfigurar la aplicación residente
en el servidor que se encarga de transformar las peticiones de red en
peticiones nativas. La única desventaja de este tipo de drivers es que la
aplicación intermediaria es una aplicación independiente que suele tener un
coste adicional por servidor físico, que hay que añadir al coste del servidor de
bases de datos.
Aproximación a JDBC
JDBC define ocho interfaces para operaciones con bases de datos, de las que se
derivan las clases correspondientes. La figura siguiente, en formato OMT, con
nomenclatura UML, muestra la interrelación entre estas clases según el modelo
de objetos de la especificación de JDBC.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
141
Instituto de Educación Superior
“San Ignacio de Monterrico”
La clase que se encarga de cargar inicialmente todos los drivers JDBC
disponibles es DriverManager. Una aplicación puede utilizar DriverManager
para obtener un objeto de tipo conexión, Connection, con una base de datos. La
conexión se especifica siguiendo una sintaxis basada en la especificación más
amplia de los URL, de la forma
jdbc:subprotocolo//servidor:puerto/base de datos
Por ejemplo, si se utiliza mSQL el nombre del subprotocolo será msql. En
algunas ocasiones es necesario identificar aún más el protocolo. Por ejemplo, si
se usa el puente JDBC-ODBC no es suficiente con jdbc:odbc, ya que pueden
existir múltiples drivers ODBC, y en este caso, hay que especificar aún más,
mediante jdbc:odbc:fuente de datos.
Una vez que se tiene un objeto de tipo Connection, se pueden crear sentencias,
statements, ejecutables. Cada una de estas sentencias puede devolver cero o
más resultados, que se devuelven como objetos de tipo ResultSet.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
142
Instituto de Educación Superior
“San Ignacio de Monterrico”
Y la tabla siguiente muestra la misma lista de clases e interfaces junto con una
breve descripción.
Clase/Interface
Descripción
Driver
Permite conectarse a una base de datos: cada gestor de base de datos
requiere un driver distinto
DriverManager
Permite gestionar todos los drivers instalados en el sistema
DriverPropertyInfo
Proporciona diversa información acerca de un driver
Connection
Representa una conexión con una base de datos. Una aplicación puede
tener más de una conexión a más de una base de datos
DatabaseMetadata
Proporciona información acerca de una Base de Datos, como las tablas
que contiene, etc.
Statement
Permite ejecutar sentencias SQL sin parámetros
PreparedStatement
Permite ejecutar sentencias SQL con parámetros de entrada/TD>
CallableStatement
Permite ejecutar sentencias SQL con parámetros de entrada y salida,
típicamente procedimientos almacenados
ResultSet
Contiene las filas o registros obtenidos al ejecutar un SELECT
ResultSetMetadata
Permite obtener información sobre un ResultSet, como el número de
columnas, sus nombres, etc.
La primera aplicación que se va a crear simplemente crea una tabla en el
servidor, utilizando para ello el puente JDBC-ODBC, siendo la fuente de datos un
servidor SQL Server. Si el lector desea utilizar otra fuente ODBC, no tiene más
que cambiar los parámetros de getConnection() en el código fuente. El
establecimiento de la conexión es, como se puede es fácil suponer, la parte que
mayores problemas puede dar en una aplicación de este tipo. Si algo no
funciona, cosa más que probable en los primeros intentos, es muy recomendable
activar la traza de llamadas ODBC desde el panel de control. De esta forma se
puede ver lo que está haciendo exactamente el driver JDBC y por qué motivo no
se está estableciendo la conexión.
El siguiente diagrama relaciona las cuatro clases principales que va a usar
cualquier programa Java con JDBC, y representa el esqueleto de cualquiera de
los programas que se desarrollan para atacar a bases de datos.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
143
Instituto de Educación Superior
“San Ignacio de Monterrico”
La aplicación siguiente es un ejemplo en donde se aplica el esquema anterior, se
trata de instalación java2101.java, crea una tabla y rellena algunos datos
iniciales.
import java.sql.*;
class java2101 {
static public void main( String[] args ) {
Connection conexion;
Statement sentencia;
ResultSet resultado;
System.out.println( "Iniciando programa." );
// Se carga el driver JDBC-ODBC
try {
Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );
} catch( Exception e ) {
System.out.println( "No se pudo cargar el puente JDBC-ODBC." );
return;
}
try {
Formando Emprendedores De Calidad Para Un Mundo Empresarial
144
Instituto de Educación Superior
“San Ignacio de Monterrico”
// Se establece la conexión con la base de datos
conexion = DriverManager.getConnection( "jdbc:odbc:Tutorial","","" );
sentencia = conexion.createStatement();
try {
// Se elimina la tabla en caso de que ya existiese
sentencia.execute( "DROP TABLE AGENDA" );
} catch( SQLException e ) {};
// Esto es código SQL
sentencia.execute( "CREATE TABLE AMIGOS ("+
" NOMBRE VARCHAR(15) NOT NULL, " +
" APELLIDOS VARCHAR(30) NOT NULL, " +
" CUMPLE DATETIME) " );
sentencia.execute( "INSERT INTO AMIGOS " +
"VALUES('JOSE','GONZALEZ','03/15/1973')" );
sentencia.execute( "INSERT INTO AMIGOS " +
"VALUES('PEDRO','GOMEZ','08/15/1961')" );
sentencia.execute( "INSERT INTO AMIGOS " +
"VALUES('GONZALO','PEREZ', NULL)" );
} catch( Exception e ) {
System.out.println( e );
return;
}
System.out.println( "Creacion finalizada." );
}
}
Las partes más interesantes del código son las que se van a revisar a
continuación, profundizando en cada uno de los pasos.
Lo primero que se hace es importar toda la funcionalidad de JDBC, a través de la
primera sentencia ejecutable del programa.
import java.sql.*;
Las siguientes líneas son las que cargan el puente JDBC-ODBC, mediante el
método forName() de la clase Class.
try {
Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );
} catch( Exception e ) {
System.out.println( "No se pudo cargar el puente JDBC-ODBC." );
return;
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
145
Instituto de Educación Superior
“San Ignacio de Monterrico”
En teoría esto no es necesario, ya que DriverManager se encarga de leer todos
los drivers JDBC compatibles, pero no siempre ocurre así, por lo que es mejor
asegurarse. El método forName() localiza, lee y enlaza dinámicamente una clase
determinada. Para drivers JDBC, la sintaxis que JavaSoft recomienda de
forName() es nombreEmpresa.nombreBaseDatos.nombreDriver, y el driver
deberá
estar
ubicado
en
el
directorio
nombreEmpresa\nombreBaseDatos\nombreDriver.class a partir del directorio
indicado por la variable de entorno CLASSPATH. En este caso se indica que el
puente JDBC-ODBC que se desea leer es precisamente el de Sun.
Si por cualquier motivo no es posible conseguir cargar JdbcOdbcDriver.class,
se intercepta la excepción y se sale del programa. En este momento es la hora
de echar mano de la información que puedan proporcionar las trazas ODBC.
La carga del driver también se puede especificar desde la línea de comandos al
lanzar la aplicación:
java -Djdbc.drivers=sun.jdbc.odbc.JdbcOdbcDriver Exprograma
A continuación, se solicita a DriverManager que proporcione una conexión para
una fuente de datos ODBC. El parámetro jdbc:odbc:Tutorial especifica que la
intención es acceder a la fuente de datos con nombre Tutorial, Data Source
Name o DSN, en la terminología ODBC.
conexion = DriverManager.getConnection("jdbc:odbc:Tutorial","","" );
El segundo y tercer parámetro son el nombre del usuario y la clave con la cual se
intentará la conexión. En este caso el acceso es libre, para acceder como
administrador del sistema en el caso de un servidor MS SQL se usa la cuenta sa
o system administrator, cuya cuenta de acceso no tiene clave definida; en caso
de acceder a un servidor MS Access, la cuenta del administrador es admin y
también sin clave definida. Esta es la única línea que con seguridad habrá de
cambiar el programador para probar sus aplicaciones. getConnection admite
también una forma con un único parámetro (el URL de la base de datos), que
debe proporcionar toda la información de conexión necesaria al driver JDBC
Formando Emprendedores De Calidad Para Un Mundo Empresarial
146
Instituto de Educación Superior
“San Ignacio de Monterrico”
correspondiente. Para el caso JDBC-ODBC, se puede utilizar la sentencia
equivalente:
DriverManager.getConnection ( "jdbc:odbc:SQL;UID=sa;PWD=" );
Para el resto de los drivers JDBC, habrá que consultar la documentación de cada
driver en concreto.
Inmediatamente después de obtener la conexión, en la siguiente línea
sentencia = conexion.createStatement();
se solicita que proporcione un objeto de tipo Statement para poder ejecutar
sentencias a través de esa conexión. Para ello se dispone de los métodos
execute(String sentencia) para ejecutar una petición SQL que no devuelve datos
o executeQuery(String sentencia) para ejecutar una consulta SQL. Este último
método devuelve un objeto de tipo ResultSet.
Una vez que se tiene el objeto Statement ya se pueden lanzar consultas y
ejecutar sentencias contra el servidor. A partir de aquí el resto del programa
realmente es SQL «adornado»: en la línea:
sentencia.execute( "DROP TABLE AMIGOS" );
se ejecuta DROP TABLE AMIGOS para borrar cualquier tabla existente
anteriormente. Puesto que este ejemplo es una aplicación «de instalación» y es
posible que la tabla AMIGOS no exista, dando como resultado una excepción, se
aísla la sentencia.execute() mediante un try y un catch.
La línea siguiente ejecuta una sentencia SQL que crea la tabla AMIGOS con tres
campos: NOMBRE, APELLIDOS y CUMPLE. De ellos, únicamente el tercero,
correspondiente al cumpleaños, es el que puede ser desconocido, es decir,
puede contener valores nulos.
sentencia.execute( "CREATE TABLE AMIGOS ("+
" NOMBRE VARCHAR(15) NOT NULL, " +
" APELLIDOS VARCHAR(30) NOT NULL, " +
" CUMPLE DATETIME) " );
Formando Emprendedores De Calidad Para Un Mundo Empresarial
147
Instituto de Educación Superior
“San Ignacio de Monterrico”
Y ya en las líneas siguientes se ejecutan sentencias INSERT para rellenar con
datos la tabla. En todo momento se ha colocado un try ... catch exterior para
interceptar cualquier excepción que puedan dar las sentencias. En general, para
java.sql está definida una clase especial de excepciones que es SQLException.
Se obtendrá una excepción de este tipo cuando ocurra cualquier error de proceso
de JDBC, tanto si es a nivel JDBC como si es a nivel inferior (ODBC o de
protocolo).
Por ejemplo, si en lugar de GONZALO en la línea correspondiente a la última
inserción en la Base de Datos, se intenta añadir un nombre nulo (NULL), se
generará una excepción SQLException con el mensaje
[Microsoft][ODBC SQL Server Driver][SQL Server]Attempt to insert the
value NULL into column 'NOMBRE', table 'master.dbo.AGENDA'; column
does not allow nulls. INSERT fails.
que en el caso de Microsoft Access sería:
[Microsoft][ODBC
Microsoft
Access
97
Driver]
The
field
'AGENDA.NOMBRE' can't contain a Null value because the Required
property for this field is set to True. Enter a value in this field.
En román paladino, el hecho de que la columna NOMBRE esté definida como
NOT NULL, hace que no pueda quedarse vacía.
Ahora se verán los pasos que hay que dar para obtener información a partir de
una base de datos ya creada. Como se ha dicho anteriormente, se utilizará
executeQuery en lugar de execute para obtener resultados. Se sustituyen las
líneas que contenían esa sentencia por:
resultado = sentencia.executeQuery( "SELECT * FROM AMIGOS" );
while( resultado.next() ) {
String nombre = resultado.getString( "NOMBRE" );
String apellidos = resultado.getString( "APELLIDOS" );
String cumple = resultado.getString( "CUMPLE" );
System.out.println( "El aniversario de D. " + nombre + " "
+ apellidos + ", se celebra el " + cumple );
Formando Emprendedores De Calidad Para Un Mundo Empresarial
148
Instituto de Educación Superior
“San Ignacio de Monterrico”
}
En este caso, en la primera línea se utiliza executeQuery para obtener el
resultado de SELECT * FROM AMIGOS. Mediante resultado.next() la posición se
situará en el «siguiente» elemento del resultado, o bien sobre el primero si
todavía no se ha utilizado. La función next() devuelve true o false si el elemento
existe, de forma que se puede iterar mediante while ( resultado.next() ) para tener
acceso a todos los elementos.
A continuación, en las líneas siguientes se utilizan los métodos getXXX() de
resultado para tener acceso a las diferentes columnas. El acceso se puede hacer
por el nombre de la columna, como en las dos primeras líneas, o bien mediante
su ubicación relativa, como en la última línea. Además de getString() están
disponibles getBoolean(), getByte(), getDouble(), getFloat(), getInt(), getLong(),
getNumeric(), getObject(), getShort(), getDate(), getTime() y getUnicodeStream(),
cada uno de los cuales devuelve la columna en el formato correspondiente, si es
posible.
Después de haber trabajado con una sentencia o una conexión es recomendable
cerrarla mediante sentencia.close() o conexión.close(). De forma predeterminada
los drivers JDBC deben hacer un COMMIT de cada sentencia. Este
comportamiento
se
puede
modificar
mediante
el
método
Connection.setAutoCommit( boolean nuevovalor). En el caso de que se
establezca AutoCommit a false, será necesario llamar de forma explícita a
Connection.commit() para guardar los cambios realizados o Connection.rollback()
para deshacerlos.
Como el lector habrá podido comprobar hasta ahora, no hay nada
intrínsecamente difícil en conectar Java con una base de datos remota. Los
posibles problemas de conexión que puede haber (selección del driver o fuente
de datos adecuada, obtención de acceso, etc.), son problemas que se tendrían
de una u otra forma en cualquier lenguaje de programación.
El objeto ResultSet devuelto por el método executeQuery(), permite recorrer las
filas obtenidas, no proporciona información referente a la estructura de cada una
de ellas; para ello se utiliza ResultSetMetaData, que permite obtener el tipo de
Formando Emprendedores De Calidad Para Un Mundo Empresarial
149
Instituto de Educación Superior
“San Ignacio de Monterrico”
cada campo o columna, su nombre, si es del tipo autoincremento, si es sensible a
mayúsculas, si se puede escribir en dicha columna, si admite valores nulos, etc.
Para obtener un objeto de tipo ResultSetMetaData basta con llamar al método
getMetaData() del objeto ResultSet.
En la lista siguiente aparecen algunos de los métodos más importantes de
ResultSetMetaData, que permiten averiguar toda la información necesaria para
formatear la información correspondiente a una columna, etc.
getCatalogName()
Nombre de la columna en el catálogo de la base de datos
getColumnName()
Nombre de la columna
getColumnLabel()
Nombre a utilizar a la hora de imprimir el nombre de la columna
getColumnDisplaySize()
Ancho máximo en caracteres necesario para mostrar el contenido de la
columna
getColumnCount()
Número de columnas en el ResultSet
getTableName()
Nombre de la tabla a que pertenece la columna
getPrecision()
Número de dígitos de la columna
getScale()
Número de decimales para la columna
getColumnType()
Tipo de la columna (uno de los tipos SQL en java.sql.Types)
getColumnTypeName()
Nombre del tipo de la columna
isSigned()
Para números, indica si la columna corresponde a un número con signo
isAutoIncrement()
Indica si la columna es de tipo autoincremento
Formando Emprendedores De Calidad Para Un Mundo Empresarial
150
Instituto de Educación Superior
“San Ignacio de Monterrico”
isCurrency()
Indica si la columna contiene un valor monetario
isCaseSensitive()
Indica si la columna contiene un texto sensible a mayúsculas
isNullable()
Indica si la columna puede contener un NULL SQL. Puede devolver los
valores
columnNoNulls, columnNullable o columnNullableUnknown, miembros
finales
estáticos de ResultSetMetaData (constantes)
isReadOnly()
Indica si la columna es de solo lectura
isWritable()
Indica si la columna puede modificarse, aunque no lo garantiza
isDefinitivelyWritable()
Indica si es absolutamente seguro que la columna se puede modificar
isSearchable()
Indica si es posible utilizar la columna para determinar los criterios de
búsqueda de un SELECT
getSchemaName()
Devuelve el texto correspondiente al esquema de la base de datos para
esa columna
En general pues, los objetos que se van a poder encontrar en una
aplicación que utilice JDBC, serán los que se indican a continuación.
Connection
Representa la conexión con la base de datos. Es el objeto que
permite realizar las consultas SQL y obtener los resultados de
dichas consultas. Es el objeto base para la creación de los objetos
de acceso a la base de datos.
DriverManager
Encargado de mantener los drivers que están disponibles en una
aplicación concreta. Es el objeto que mantiene las funciones de
Formando Emprendedores De Calidad Para Un Mundo Empresarial
151
Instituto de Educación Superior
“San Ignacio de Monterrico”
administración de las operaciones que se realizan con la base de
datos.
Statement
Se utiliza para enviar las sentencias SQL simples, aquellas que no
necesitan parámetros, a la base de datos.
PreparedStatement
Tiene una relación de herencia con el objeto Statement,
añadiéndole la funcionalidad de poder utilizar parámetros de
entrada. Además, tiene la particularidad de que la pregunta ya ha
sido compilada antes de ser realizada, por lo que se denomina
preparada. La principal ventaja, aparte de la utilización de
parámetros, es la rapidez de ejecución de la pregunta.
CallableStatement
Tiene una relación de herencia cn el objeto PreparedStatement.
Permite utilizar funciones implementadas directamente sobre el
sistema de gestión de la base de datos. Teniendo en cuenta que
éste posee información adicional sobre el uso de las estructuras
internas, índices, etc.; las funciones se realizarán de forma más
eficiente. Este tipo de operaciones es muy utilizada en el caso de
ser funciones muy complicadas o bien que vayan a ser ejecutadas
varias veces a lo largo del tiempo de vida de la aplicación.
ResultSet
Contiene la tabla resultado de la pregunta SQL que se haya
realizado. En párrafos anteriores se han comentado los métodos
que proporciona este objeto para recorrer dicha tabla.
Transacciones
En párrafos anteriores se ha tratado de la creación y uso de sentencias SQL, que
siempre se obtenían llamando a un método de un objeto de tipo Connection,
como createStatement() o prepareStatement(). El uso de transacciones, también
se controla mediante métodos del objeto Connection. Como ya se ha dicho,
Connection representa una conexión a una Base de datos dada, luego
Formando Emprendedores De Calidad Para Un Mundo Empresarial
152
Instituto de Educación Superior
“San Ignacio de Monterrico”
representa el lugar adecuado para el manejo de transacciones, dado que estas
afectan a todas las sentencias ejecutadas sobre una conexión a la base de datos.
Por defecto, una conexión funciona en modo autocommit, es decir, cada vez que
se ejecuta una sentencia SQL se abre y se cierra automáticamente una
transacción, que sólo afecta a dicha sentencia. Es posible modificar esta opción
mediante setAutoCommit(), mientras que getAutoCommit() indica si se está en
modo autocommit o no. Si no se está trabajando en modo autocommit será
necesario que se cierren explícitamente las transacciones mediante commit() si
tienen éxito, o rollback(), si fallan; nótese que, tras cerrar una transacción, la
próxima vez que se ejecute una sentencia SQL se abrirá automáticamente una
nueva, por lo que no existe ningún método del tipo que permita iniciar una
transacción.
Es posible también especificar el nivel de aislamiento de una transacción,
mediante setTransactionIsolation(), así como averiguar cuál es el nivel de
aislamiento de la actual mediante getTransactionIsolation(). Los niveles de
aislamiento se representan mediante las constantes que se muestran en la lista
siguiente, en la cual se explica muy básicamente el efecto de cada nivel de
aislamiento.
TRANSACTION_NONE
No se pueden utilizar transacciones.
TRANSACTION_READ_UNCOMMITTED
Desde esta transacción se pueden llegar a ver registros que han sido
modificados por otra transacción, pero no guardados, por lo que podemos
llegar a trabajar con valores que nunca llegan a guardarse realmente.
TRANSACTION_READ_COMMITTED
Se ven solo las modificaciones ya guardadas hechas por otras
transacciones.
TRANSACTION_REPEATABLE_READ
Si se leyó un registro, y otra transacción lo modifica, guardándolo, y lo
volvemos a leer, seguiremos viendo la información que había cuando lo
Formando Emprendedores De Calidad Para Un Mundo Empresarial
153
Instituto de Educación Superior
“San Ignacio de Monterrico”
leímos por primera vez. Esto proporciona un nivel de consistencia mayor
que los niveles de aislamiento anteriores.
TRANSACTION_SERIALIZABLE
Se verán todos los registros tal y como estaban antes de comenzar la
transacción, no importa las modificaciones que otras transacciones hagan,
ni que lo hayamos leído antes o no. Si se añadió algún nuevo registro,
tampoco se verá.
Además de manejar transacciones, el objeto Connection también
proporciona
algunos
otros
métodos
que
permiten
especificar
características de una conexión a una base de datos; por ejemplo, los
métodos isReadOnly() y setReadOnly() permiten averiguar si una conexión
a una base de datos es de sólo lectura, o hacerla de sólo lectura. El
método isClosed() permite averiguar si una conexión está cerrada o no, y
nativeSQL() permite obtener la cadena SQL que el driver mandaría a la
base de datos si se tratase de ejecutar la cadena SQL especificada,
permitiendo averiguar qué es exactamente lo que se le envía a la base de
datos.
Información de la Base de Datos
Falta aún una pieza importante a la hora de trabajar con la conexión a la
base de datos mediante Connection, y es la posibilidad de poder
interrogar sobre las características de una base de datos; por ejemplo,
puede se interesante saber si la base de datos soporta cierto nivel de
aislamiento en una transacción, como la TRANSACTION_SERIALIZABLE,
que muchos gestores no soportan. Para esto está otro de los interfaces
que proporciona JDBC, DatabaseMetaData, al que es posible interrogar
sobre las características de la base de datos con la que se está
trabajando. Es posible obtener un objeto de tipo DatabaseMetaData
mediante el método getMetaData() de Connection.
DatabaseMetaData proporciona diversa información sobre una base de
datos, y cuenta con varias docenas de métodos, a través de los cuales es
posible obtener gran cantidad de información acerca de una tabla; por
ejemplo,
getColumns()
devuelve
las
columnas
de
una
tabla,
getPrimaryKeys() devuelve la lista de columnas que forman la clave
Formando Emprendedores De Calidad Para Un Mundo Empresarial
154
Instituto de Educación Superior
“San Ignacio de Monterrico”
primaria, getIndexInfo() devuelve información acerca de sus índices,
mientras que getExportedKeys() devuelve la lista de todas las claves
ajenas que utilizan la clave primaria de esta tabla, y getImportedKeys() las
claves ajenas existentes en la tabla. El método getTables() devuelve la
lista de todas las tablas en la base de datos, mientras que getProcedures()
devuelve la lista de procedimientos almacenados. Muchos de los métodos
de DatabaseMetaData devuelven un objeto de tipo ResultSet que
contiene la información deseada. El listado que se presenta a
continuación, muestra el código necesario para obtener todas las tablas de
una base de datos.
String nombreTablas = "%";
// Listamos todas las tablas
String tipos[] = new String[1]; // Listamos sólo tablas
tipos[0] = "TABLE";
DatabaseMetaData dbmd = conexion.getMetaData();
ResultSet tablas = dbmd.getTables( null,null,nombreTablas,tipos );
boolean seguir = tablas.next();
while( seguir ) {
// Mostramos sólo el nombre de las tablas, guardado
// en la columna "TABLE_NAME"
System.out.println(
tablas.getString( tablas.findColumn( "TABLE_NAME" ) ) );
seguir = tablas.next();
};
Hay todo un grupo de métodos que permiten averiguar si ciertas
características están soportadas por la base de datos; entre ellos,
destacan supportsGroupBy() indica si se soporta el uso de GROUP BY en
un SELECT, mientras que supportsOuterJoins() indica si se pueden llevar
a cabo outer-joins. El método supportsTransactions(), comentado antes,
indica si cierto tipo de transacciones está soportado o no. Otros métodos
de utilidad son getUserName(), que devuelve el nombre del usuario actual;
getURL(), que devuelve el URL de la base de datos actual.
DatabaseMetaData proporciona muchos otros métodos que permiten
averiguar cosas tales como el máximo número de columnas utilizable en
Formando Emprendedores De Calidad Para Un Mundo Empresarial
155
Instituto de Educación Superior
“San Ignacio de Monterrico”
un SELECT, etc. En general, casi cualquier pregunta sobre las
capacidades de la base de datos se puede contestar llamando a los
distintos métodos del objeto DatabaseMetaData, que merece la pena que
el lector consulte cuando no sepa si cierta característica está soportada.
Puente Jdbc-Odbc
Para desarrollar nuestras aplicaciones nosotros usaremos la primera forma de
conexión a base de datos con el puente JDBC-ODBC, que es la más sencilla y
libre de costos monetarios.
Siempre debemos considerar la siguiente figura en el desarrollo completo de
nuestra aplicación:
•
Lo primero que tenemos considerar es el tener nuestra base de datos en
SQL Server.
•
Luego construir el DSN (Data Source Name) mediante ODBC.
•
Considerar que los objetos para acceder a base de datos se encuentran
en la API JDBC, por lo tanto una línea obligatoria en nuestro programa en
Java es:
impor java.sql.*;
// Acceso a los objetos Connection, Statement y ResulSet
•
Finalmente podemos centrarnos en desarrollar el código Java que
complete nuestra aplicación.
Acceso A Una Base De Datos Con Java
Formando Emprendedores De Calidad Para Un Mundo Empresarial
156
Instituto de Educación Superior
“San Ignacio de Monterrico”
La siguiente figura muestra un. Esta aplicación tiene efecto si hemos creado el
DSN dsnPubs. Al dar clic en el botón Probar DSN veremos el siguiente
resultado:
El código para este programa esta dado por 2 clases: Formulario y MiPanel. El
código de la clase Formulario es el siguiente:
//Archivo Formulario
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Formulario extends JFrame
{
static int wF= 400, hF= 300;
//"static" xq se van a usar en "main()"
public Formulario(String s){
super(s);
//invoca al padre y le pasa la cadena "s" como título
getContentPane().add(new MiPanel());
//crea el panel en el formulario
setLocation((getToolkit().getScreenSize().width
-
wF)/2,
(getToolkit().getScreenSize().height - hF)/2);
//para ubicar el formulario en una posicion especifica
Formando Emprendedores De Calidad Para Un Mundo Empresarial
157
Instituto de Educación Superior
“San Ignacio de Monterrico”
// de la pantalla
//getToolkit() me da informción útil, getScreenSize()
// dimensiones de la pantalla (width, height)
}
static public void main(String[] arg) {
JFrame f= new Formulario("Carlos Durand");
//para cerrar la ventana cuando se da click a la "X"
//de la ventana
f.addWindowListener(new WindowAdapter() {
public
void
windowClosing(WindowEvent
{ System.exit(0); }
});
f.setSize(wF, hF);
f.setVisible(true);
}
}
El código de MiPanel es el siguiente:
//Archivo MiPanel
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.sql.*; // Acceso a objetos de JDBC
class MiPanel extends JPanel implements ActionListener {
JButton b= new JButton("Probar DSN");
JTextArea t= new JTextArea();
public MiPanel() {
setLayout(new BorderLayout());
b.addActionListener(this);
add(b, BorderLayout.NORTH);
Formando Emprendedores De Calidad Para Un Mundo Empresarial
158
e)
Instituto de Educación Superior
“San Ignacio de Monterrico”
add(t, BorderLayout.CENTER);
}
// --------------------Para eventos
public void actionPerformed(ActionEvent e) {
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection cn= DriverManager.getConnection(
"jdbc:odbc:dsnPubs", "sa", "");
DatabaseMetaData meta= cn.getMetaData();
t.setText("Exito en conexión...!\n");
t.append("\nDatabase:\t"
+
meta.getDatabaseProductName());
t.append("\nversion:\t"
+
meta.getDatabaseProductVersion());
cn.close();
} catch(Exception ex) {
t.setText("La conexión fracasó por:\n\n");
t.append(ex.toString());
}
}
}
Explicación:
•
Los objetos de JDBC deben estar siempre dentro de una instrucción try -
catch
•
Para indicar que tipo de driver vamos a usar en nuestra aplicación
ponemos la sentencia: Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
•
Por la anterior sentencia el driver a usar es el puente JDBC-ODBC.
•
El administrador de drivers (DriverManager) del JDBC para el caso del
puente JDBCODBC nos pide 3 argumentos: el DSN, login y password, como
se indica en la siguiente sentencia:
Connection cn= DriverManager.getConnection("jdbc:odbc:dsnPubs", "sa", "");
Formando Emprendedores De Calidad Para Un Mundo Empresarial
159
Instituto de Educación Superior
“San Ignacio de Monterrico”
•
Este nos devuelve un objeto de tipo Connection con el cual se apertura el
acceso a la base de datos.
•
Existen objetos para la metadata. La metadata es la data del entorno de
los datos, por ejemplo si una columna tiene la data “Juan Pérez“, entonces su
metadata es el nombre de la columna por ejemplo Nombre. Para recoger
metadata del gestor de base de datos usamos un objeto de la clase
DatabaseMetaData como indica la siguiente sentencia:
DatabaseMetaData meta= cn.getMetaData();
•
La metadata de la base de datos es pedida a través de un objeto de la
clase Connection (cn).
•
El objeto meta de la clase DatabaseMetaData nos permite obtener el
nombre y la versión del gestor de base de datos, como muestran las dos
sentencias siguientes:
t.append("\nDatabase:\t" + meta.getDatabaseProductName());
t.append("\nversion:\t" + meta.getDatabaseProductVersion());
•
Podemos manipular excepciones a través de un objeto de la clase
Exception o de la clase SQLException. En nuestra aplicación lo hicimos de la
siguiente forma:
catch(Exception ex) {
t.setText("La conexión fracasó por:\n\n");
t.append(ex.toString());
}
•
La aplicación todo lo que hace es probar el dsnPubs mostrando el nombre
y la versión del gestor de base de datos.
•
Esta aplicación puede servir de plantilla para probar otros DSNs.
Objetos básicos de JDBC
Formando Emprendedores De Calidad Para Un Mundo Empresarial
160
Instituto de Educación Superior
“San Ignacio de Monterrico”
Existen muchos objetos de JDBC, sin embargo, estos tienen vital importancia
para empezar a desarrollar nuestras aplicaciones para acceder a base de datos:
Connection
Permite la conexión a la base de datos. Origina un canal entre nuestra
aplicación y la base de datos y será siempre imprescindible en una
aplicación para acceder a una base de datos.
Statement
Este objeto nos permitirá ejecutar una sentencia SQL para nuestra base de
datos. Por ejemplo: select, insert, update y delete.
ResulSet
Si el objeto Statement ejecuta una sentencia select del SQL, entonces,
este devuelve un conjunto de resultados. Este conjunto de resultados es
asignado y manipulado por un objeto ResulSet.
ResultSetMetaData
Un objeto de esta clase tiene información meta sobre el conjunto de
resultados, como por ejemplo: cuántas columnas tiene la consulta, los
nombres de las columnas, los tipos de datos que guarda cada columna,
cuántas filas, etc.
Para dar ejemplo de los objetos anteriores vamos a desarrollar una aplicación
que muestra un formulario con un botón (Consulta a la tabla Authors de la
Pubs) y un objeto área de texto que muestra una consulta cuando se da clic al
botón. El programa funciona con dsnPubs. La consulta será a la tabla Authors
de la base datos Pubs (base de datos de ejemplo que viene con el SQL Server):
Formando Emprendedores De Calidad Para Un Mundo Empresarial
161
Instituto de Educación Superior
“San Ignacio de Monterrico”
El código de la aplicación esta dado por 2 clases: Formulario y MiPanel. El
código de la clase Formulario ya fue creado en el anterior ejemplo y es similar,
sin embargo, el código de MiPanel es el siguiente:
//Archivo MiPanel
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.sql.*;
class MiPanel extends JPanel implements ActionListener {
JButton b= new JButton("Consulta a la tabla Authors de la Pubs");
JTextArea t= new JTextArea();
public MiPanel() {
setLayout(new BorderLayout());
b.addActionListener(this);
add(b, BorderLayout.NORTH);
add(new JScrollPane(t), BorderLayout.CENTER);
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
162
Instituto de Educación Superior
“San Ignacio de Monterrico”
// ----------------------------------------------------Para eventos
public void actionPerformed(ActionEvent e) {
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection cn= DriverManager.getConnection(
"jdbc:odbc:dsnPubs", "sa", "");
Statement st= cn.createStatement();
ResultSet rs= st.executeQuery(
"Select au_lname as Apellido," +
"au_fname as Nombre," +
"phone as Teléfono," +
"address as Dirección" +
" From Authors");
muestraData(rs);
cn.close();
} catch(SQLException ex) {
t.setText("Error de SQL:\n\n"); t.append(ex.toString());
} catch(Exception ex) {
t.setText("Error genérico:\n\n"); t.append(ex.toString());
}
}
// ----------------------------------------------------Auxiliares
public void muestraData(ResultSet r) throws Exception {
ResultSetMetaData rmeta= r.getMetaData();
int numColumnas= rmeta.getColumnCount(); // Cuántas columnas
t.setText("");
for(int i=1; i<=numColumnas; ++i)
t.append(rmeta.getColumnName(i) + "\t"); // nombre de
columna
t.append("\n");
while(r.next()) { // hasta fin de archivo
for(int i=1; i<=numColumnas; ++i)
t.append(r.getString(i) + "\t");
t.append("\n");
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
163
Instituto de Educación Superior
“San Ignacio de Monterrico”
}
}
Explicacion
•
En este ejemplo mostramos los objetos: Connection (cn), Statement (st) y
ResulSet (rs) en una operación conjunta para la consulta a la base de datos.
•
Para acceder a los datos de la consulta necesitamos del objeto ResultSet:
ResultSet rs= st.executeQuery(
"Select au_lname as Apellido," + "au_fname as Nombre," + "phone as
Teléfono," + "address as Dirección" + " From Authors");
•
El objeto ResultSet permite acceder a las filas de la consulta mediante una
instrucción Select del SQL.
•
Para ejecutar la instrucción Select necesitamos de un objeto Statement:
Statement st= cn.createStatement();
•
Por supuesto para ejecutar un Statement necesitamos previamente de un
objeto
Connection:
Connection cn=DriverManager.getConnection("jdbc:odbc:dsnPubs","sa", "");
•
El método muestraData es un método personalizado que responde a una
instrucción Select. Su construcción se debió a la intención de simplificar y
modular la aplicación.
•
El método muestraData como es invocado dentro de una instrucción try
tiene que llevar la instrucción: throws Exception. Esto hará que si se produce
una excepción en el método entonces se ejecuta la instrucción catch de la
instrucción try en la que se invocó al método.
•
En nuestro caso el método muestraData recibe un objeto ResulSet (r) que
viene a ser el contenido del objeto rs que tiene la consulta: muestraData(rs);
Formando Emprendedores De Calidad Para Un Mundo Empresarial
164
Instituto de Educación Superior
“San Ignacio de Monterrico”
•
Dentro del método muestraData usamos un objeto de la clase
ResultSetMetaData con la intención de solicitar información meta de la
consulta, como: cuántas columnas hay y cómo se llama cada columna.
ResultSetMetaData rmeta= r.getMetaData();
int numColumnas= rmeta.getColumnCount();
// Cuántas columnas
t.setText("");
for(int i=1; i<=numColumnas; ++i)
t.append(rmeta.getColumnName(i) + "\t");
// nombre de columna
•
Para leer hasta la ultima fila usamos el siguiente código:
while(r.next()) { // hasta fin de archivo
for(int i=1; i<=numColumnas; ++i)
t.append(r.getString(i) + "\t");
t.append("\n");
}
•
El método next() del ResulSet retorna false cuando no encuentra una fila.
También cuando se ejecuta la consulta el objeto ResulSet está antes de la
primera fila.
•
Para leer el dato de una columna y retornarlo como un dato de tipo String
usamos el método getString(índice) del objeto ResulSet. Este método
requiere como argumento el índice de la columna (1 para la primera) o el
nombre de la columna.
•
Conforme se hace lectura a los datos de las columnas del ResultSet, estos
datos obtenidos como String son añadidos al objeto JTextArea (t) para que
el usuario vea la consulta.
DISEÑO GUI CON AWT Y SWING.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
165
Instituto de Educación Superior
“San Ignacio de Monterrico”
Librería AWT y Swing
Swing es un conjunto de clases desarrolladas por primera vez para Java 1.2 (el
llamado Java2), para mejorar el anterior paquete que implementaba clases
para fabricar interfaces de usuario, el llamado AWT (Abstract Window Tools)
que aún se usa bastante en las aplicaciones Java.
Tanto Swing como AWT forman parte de una colección de clases llamada JFC
(Java Foundation Classes) que incluyen paquetes dedicados a la programación
de interfaces gráficos (así como a la producción multimedia).
Uno de los problemas frecuentes de la programación clásica era como programar
interfaces de usuario, ya que esto implicaba tener que utilizar las API propias del
Sistema Operativo y esto provocaba que el código no fuera transportable a otros
sistemas.
AWT fue la primera solución a este problema propuesta por Java. AWT está
formada por un conjunto de clases que no dependen del sistema operativo, pero
que proponen una serie de clases para la programación de GUIs (graphic users
interfaces, interfaces gráficos de usuario; cualquier entorno de comunicación
entre el ordenador y el usuario).
AWT usa clases gráficas comunes a todos los sistemas operativos gráficos y
luego la máquina virtual traduce esa clase a la forma que tenga en el sistema
concreto en el que se ejecutó el programa, sin importar que dicho sistema sea un
sistema X, McIntosh o Windows. La popularidad de AWT desbordó las
expectativas de la propia empresa Sun.
La clave de AWT era el uso de componentes iguales (peers). Los elementos de
los interfaces AWT dejaban al sistema la responsabilidad de generar
realmente
los componentes. Eso aseguraba una vista coherente respecto al
sistema en el que se ejecutaba el programa. El problema es que ante la
Formando Emprendedores De Calidad Para Un Mundo Empresarial
166
Instituto de Educación Superior
“San Ignacio de Monterrico”
grandiosidad de la imagen en Windows y Mac OS, otros sistemas quedaban peor
ante la misma aplicación.
Por ello (y por otros problemas) aparece Swing en la versión 1.2 como parte del
JFC (Java Foundation Classes) que es el kit de clases más importante de Java
para las producciones gráficas.
Los problemas de AWT eran:
•
AWT tenía problemas de compatibilidad en varios sistemas.
•
A AWT le faltaban algunos componentes avanzados (árboles, tablas,...).
•
Consumía excesivos recursos del sistema.
Swing aporta muchas más clases, consume menos recursos y construye mejor la
apariencia de los programas. En cualquier caso, AWT no desaparece;
simplemente se añade a las nuevas capacidades Swing
Componentes
Los componentes son los elementos básicos de la programación con Swing.
Todo lo que se ve en un GUI de Java es un componente. Los componentes se
colocan en otros elementos llamados contenedores que sirven para agrupar
componentes.
Un administrador de diseño se encarga de de disponer la
presentación de los componentes en un dispositivo de presentación concreto.
La clase javax.swing.JComponent es la clase padre de todos los componentes. A
su
vez,
JComponent
desciende
de
java.awt.container
y
ésta
de
java.awt.component. De esto se deduce que Swing es una extensión de AWT, de
hecho su estructura es análoga.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
167
Instituto de Educación Superior
“San Ignacio de Monterrico”
La clase JComponent posee métodos para controlar la apariencia del objeto. Por
ejemplo: la visibilidad, tamaño, posición, tipo de letra, color,... Al dibujar un
componente, se le asigna un dispositivo de presentación.
Además posee métodos que controlan el comportamiento del componente.
Cuando el usuario ejecuta una acción sobre un componente, entonces se crea un
objeto de evento que describe el suceso. El objeto de evento se envía a objetos
de control de eventos (Listeners). Los eventos son uno de los pilares de la
construcción de Interfaces de usuario y una de las bases de la comunicación
entre objetos.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
168
Instituto de Educación Superior
“San Ignacio de Monterrico”
Pares
En AWT se usaban interfaces de pares. Esto significaba que cada componente
creado con AWT, creaba un par igual correspondiente al mundo real. Es decir al
crear el botón, existía el botón virtual creado en Java y el que realmente era
dibujado en la pantalla (el real). El programador no necesita saber de la
existencia de ese par, la comunicación del objeto creado con su par corría por
cuenta de AWT.
Este modelo de componentes se elimina en Swing. En Swing se habla de
componentes de peso ligero. La clase JComponent que es la raíz de clases
Swing, no utiliza un par, cada componente es independiente del sistema de
ventanas principal. Se dibujan a sí mismos y responden a los eventos de usuario
sin ayuda de un par.
La ventaja de este modelo es que requiere menos recursos y que su modificación
visual es más ágil y efectiva.
Modelo/vista/controlador
Se trata del modelo fundamental del trabajo con interfaces de usuario por parte
de Swing. Consiste en tres formas de abstracción. Un mismo objeto se ve de
esas tres Formas:
•
Modelo. Se refiere al modelo de datos que utiliza el objeto. Es la
información que se manipula mediante el objeto Swing.
•
Vista. Es cómo se muestra el objeto en la pantalla.
•
Controlador. Es lo que define el comportamiento del objeto.
Por ejemplo un array de cadenas que contenga los meses del año, podría ser el
modelo de un cuadro combinado de Windows. Un cuadro combinado es un
rectángulo con un botón con una flecha que permite elegir una opción de una
lista. La vista de ese cuadro es el hecho de mostrar esas cadenas en ese
rectángulo con flecha. Y el controlador es la capa software que permite capturar
el clic del ratón cuando apunta a la flecha del control a fin de mostrar y
seleccionar el contenido.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
169
Instituto de Educación Superior
“San Ignacio de Monterrico”
Métodos de JComponent
La clase JComponent es abstracta, lo cual significa que no puede crear objetos,
pero sí es la superclase de todos los componentes visuales (botones, listas,
paneles, applets,...) y por ello la lista de métodos es interminable, ya que
proporciona la funcionalidad de todos los componentes. Además puesto que
deriva de Component y Container tiene los métodos de estos, por ello aún es
más grande esta lista. Algunos son:
Métodos de información
Método
String getName()
void setname(String nombre)
Container getParent()
uso
Obtiene el nombre del componente
cambia el nombre del componente
Devuelve
el
contenedor
que
sostiene a este componente
Métodos de apariencia y posición
Método
void setVisible(boolean vis)
uso
Muestra u oculta el componente
según el valor del argumento sea
Color getForeground()
true o false
Devuelve el color de frente en
void setForeGround(Color color)
Color getBackground()
forma de objeto Color
Cambia el color frontal
Devuelve el color de fondo en
void setBackground(Color color)
Point getLocation()
forma de objeto java.awt.Color
Cambia el color de fondo
Devuelve
la
posición
del
componente en forma de objeto
void setLocation(int x, int y)
Point
Coloca
componente
en
la
void setLocation(Point p)
posición x, y
Coloca el componente
en
la
posición
Dimension getSize()
el
marcada
por
las
coordenadas del punto P
Devuelve
el
tamaño
del
Formando Emprendedores De Calidad Para Un Mundo Empresarial
170
Instituto de Educación Superior
“San Ignacio de Monterrico”
componente en un objeto de tipo
void setSize(Dimension d)
java.awt.Dimension.
Cambia las dimensiones del objeto
en base a un objeto Dimension o
indicando la anchura y la altura con
dos enteros.
void setSize(int ancho, int alto)
void setBounds(int x, int y, int ancho, int alto)
Determina
la
posición
de
la
ventana (en la
coordenada x, y) así como su
tamaño con los parámetros ancho y
void setPreferredSize(Dimension d)
alto
Cambia el tamaño preferido del
componente. Este tamaño es el
que
void setToolTipText(String texto)
el
componente
quiere tener.
Hace que
el
texto
realmente
indicado
aparezca cuando el usuario posa
el
cursor
del
ratón
sobre
el
String getToolTipText()
componente
Obtiene el texto de ayuda del
Cursor getCursor()
componente
Obtiene el cursor del componente
void setCursor(Cursor cursor)
en forma de objeto java.awt.Cursor
Cambia
el
cursor
del
componente
void setFont(Font fuente)
por
el especificado
en el parámetro.
Permite especificar el tipo de letra
de la fuente del
texto
Contenedores
Son un tipo de componentes pensados para almacenar y manejar otros
componentes.
Los objetos JComponent pueden ser contenedores al ser una clase que
desciende de Container que es la clase de los objetos contenedores de AWT.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
171
Instituto de Educación Superior
“San Ignacio de Monterrico”
Para hacer que un componente forme parte de un contenedor, se utiliza el
método
add. Mientras que el método remove es el encargado de eliminar un componente.
Ambos métodos proceden de la clase java.awt.Container
Swing posee algunos contenedores especiales. Algunos son:
•
JWindow. Representa un panel de ventana sin bordes ni elementos
visibles.
•
JFrame. Objeto que representa una ventana típica con bordes, botones de
cerrar,etc.
•
JPanel. Es la clase utilizada como contenedor genérico para agrupar
componentes.
•
JDialog. Clase que genera un cuadro de diálogo.
•
JApplet. Contenedor que agrupa componentes que serán mostrados en un
navegador.
JWindow
Este objeto deriva de la clase java.awt.Window que a su vez deriva de
java.awt.Container. Se trata de un objeto que representa un marco de ventana
simple, sin borde, ni ningún elemento. Sin embargo son contenedores a los
que se les puede añadir información. Estos componentes suelen estar dentro
de una ventana de tipo Frame o, mejor, JFrame.
Júrame
Los objetos JFrame derivan de la clase Frame que, a su vez deriva, también
de la clase Window, por lo que muchos métodos de esta clase son comunes a
la anterior. Los objetos JFrame son ventanas completas.
JDialog
JDialog deriva de la clase AWT Dialog que es subclase de Window.
Representa un cuadro de diálogo que es una ventana especializada para
realizar operaciones complejas.
Añadir componentes a las ventanas
Formando Emprendedores De Calidad Para Un Mundo Empresarial
172
Instituto de Educación Superior
“San Ignacio de Monterrico”
Las clases JDialog y JFrame no permiten usar el método add, como les ocurre a
los contenedores normales, por eso se utiliza el método getContentPane() que
devuelve un objeto Container que representa el área visible de la ventana. A
este contenedor se le llama panel contenedor y sí permite método add.
public class prbVentana{
public static void main(String args[]){
JFrame ventana=new JFrame(“Prueba”);
ventana.setLocation(100,100);
Container c=ventana.getContentPane();
c.add(new JLabel(“Hola”));
ventana.pack();
ventana.setVisible(true);
}
Este código muestra una ventana ajustada al contenido de una ventana que pone
Hola.
Eventos
En términos de Java, un evento es un objeto que es lanzado por un objeto y
enviado a otro objeto llamado escuchador (listener). Un evento se lanza (o se
dispara, fire) cuando ocurre una determinada situación (un clic de ratón, una
pulsación de tecla,...).
La programación de eventos es una de las bases de Java y permite mecanismos
de diseño de programas orientados a las acciones del usuario. Es decir, son las
acciones del usuario las que desencadenan mensajes entre los objetos (el flujo
del código del programa se desvía en función del evento producido, alterando la
ejecución normal).
Hay multitud de tipos de eventos, más adelante se señala una lista de los eventos
fundamentales. En su captura hay que tener en cuenta que hay tres objetos
implicados:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
173
Instituto de Educación Superior
“San Ignacio de Monterrico”
•
El objeto fuente. Que es el objeto que lanza los eventos. Dependiendo del
tipo de objeto que sea, puede lanzar unos métodos u otros. Por ejemplo un
objeto de tipo JLabel (etiqueta) puede lanzar eventos de ratón
(MouseEvent) pero no de teclado (KeyEvent). El hecho de que dispare
esos eventos no significa que el programa tenga que, necesariamente,
realizar una acción. Sólo se ejecuta una acción si hay un objeto
escuchando.
•
El objeto escuchador u oyente (listener). Se trata del objeto que recibe el
evento producido. Es el objeto que captura el evento y ejecuta el código
correspondiente. Para ello debe implementar una interfaz relacionada con
el tipo de evento que captura. Esa interfaz obligará a implementar uno o
más métodos cuyo código es el que se ejecuta cuando se dispare el
evento.
•
El objeto de evento. Se trata del objeto que es enviado desde el objeto
fuente al escuchador. Según el tipo de evento que se haya producido se
ejecutará uno u otro método en el escuchador.
Escuchadores De Eventos
Cada tipo de evento tiene asociado un interfaz para manejar el evento. A esos
interfaces se les llama escuchadores (Listeners) ya que proporcionan métodos
que están a la espera de que el evento se produzca. Cuando el evento es
disparado por el objeto fuente al que se estaba escuchando, el método
manejador del evento se dispara automáticamente.
Por ejemplo, el método actionPerformed es el encargado de gestionar eventos
del tipo ActionEvent (eventos de acción, se producen, por ejemplo, al hacer clic
en un botón). Este método está implementado en la interfaz ActionListener
(implementa escuchadores de eventos de acción).
Cualquier clase que desee escuchar eventos (los suyos o los de otros objetos)
debe implementar la interfaz (o interfaces) pensada para capturar los eventos del
Formando Emprendedores De Calidad Para Un Mundo Empresarial
174
Instituto de Educación Superior
“San Ignacio de Monterrico”
tipo deseado. Esta interfaz habilita a la clase para poder implementar métodos de
gestión de eventos.
Por ejemplo; un objeto que quiera escuchar eventos ActionEvent, debe
implementar la interfaz ActionListener. Esa interfaz obliga a definir el método ya
comentado
actionPerformed. El código de
ese
método será invocado
automáticamente cuando el objeto fuente produzca un evento de acción.
Es decir, hay tres actores fundamentales en el escuchador de eventos:
•
El objeto de evento que se dispara cuando ocurre un suceso. Por ejemplo
para capturar el ratón sería MouseEvent.
•
El método o métodos de captura del evento (que se lanza cuando el
evento se produce). Pueden ser varios, por ejemplo para la captura de
eventos de tipo MouseEvent (evento de ratón) existen los métodos
mouseReleased (es invocado cuando se libera un botón del ratón),
mousePressed (es invocado cuando se pulsa un botón del ratón),
mouseEntered (es invocado cuando el cursor entra en el objeto) y
mouseExited (ocurre cuando el ratón sale del objeto).
•
La interfaz que tiene que estar implementada en la clase que desea
capturar ese evento. En este ejemplo sería MouseListener, que es la que
obliga a la clase del escuchador a implementar los cuatro métodos de
gestión comentados anteriormente Sin duda, el más complejo es este
último, pero hay que entender que una internaz lo único que consigue es
dar a una clase la facultad de escuchar (Listen) eventos.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
175
Instituto de Educación Superior
“San Ignacio de Monterrico”
Fuentes De Eventos
Disparar eventos El objeto fuente permite que un objeto tenga capacidad de
enviar eventos. Esto se consigue mediante un método que comienza por la
palabra add seguida por el nombre de la interfaz que captura este tipo de
eventos. Este método recibe como parámetro el objeto escuchador de los
eventos.
Esto es más fácil de lo que parece. Para que un objeto fuente, sea escuchado,
hay que indicar quién será el objeto que escuche (que obligadamente deberá
implementar la interfaz relacionada con el evento a escuchar). Cualquier
componente puede lanzar eventos, sólo hay que indicárselo, y eso es lo que hace
el método add.
Ejemplo:
public class MiVentana extends JFrame implements ActionListener{
JButton boton1=new JButton(“Prueba”);
//Constructor
public MiVentana() {
boton1.addActionListener(this);//El botón lanza
//eventos que son capturados por la ventana
}
public void actionPerformed(ActionEvent e){
//Manejo del evento
}
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
176
Instituto de Educación Superior
“San Ignacio de Monterrico”
En el ejemplo anterior se habilita al boton1 para que lance eventos mediante el
método addActionListener. Este método requiere un objeto escuchador que, en
este caso, será la ventana en la que está el botón. Esta ventana tiene que
implementar la interfaz ActionListener para poder escuchar eventos (de hecho el
método addActionListener sólo permite objetos de esta interfaz). Cuando se haga
clic con el ratón se llamará al método actionPerformed de la ventana, que es el
método de gestión.
Hay que señalar que una misma fuente puede tener varios objetos escuchando
los eventos (si lanza varios métodos add). Si hay demasiados objetos
escuchando
eventos,
se
produce
una
excepción
del
tipo
TooManyListenersException eliminar oyentes, Hay un método remove que sirve
para que un oyente del objeto deje de escuchar los eventos.
boton1.removeActionListener(this);
//La ventana deja de
//escuchar los eventos del botón
Objeto de evento.
Clase EventObject Ya se ha comentado que cuando se produce un evento se
crea un objeto llamado objeto de evento. Este objeto es pasado al objeto que
está escuchando los eventos.
Todos los objetos de evento pertenecen a clases que derivan de EventObject.
Esta es la superclase de todos los objetos de evento. Representa un evento
genérico y en la práctica sólo sirve para definir los métodos comunes a todos los
eventos que son:
método
Object getSource()
uso
Obtiene el objeto que lanzó el evento (método
String toString()
muy importante)
Método toString redefinido
para
mostrar
la
información del evento
Formando Emprendedores De Calidad Para Un Mundo Empresarial
177
Instituto de Educación Superior
“San Ignacio de Monterrico”
Lanzar Eventos Propios
Se pueden crear eventos propios y lanzarlos a cualquier objeto que esté
preparado para capturar eventos. Para ello basta crear el evento deseado
indicando, al menos, en el constructor el objeto que capturará el evento y el
identificador de evento.
El identificador es un entero que sirve para indicar el tipo de evento producido. En
un evento
MouseEvent
habrá
que
(MouseEvent.MOUSE_CLICKED),
indicar
si
es
de
un
evento
de
clic
arrastre
(MouseEvent.MOUSEDRAGGED,...).
Además según el tipo de evento se pueden requerir más valores (posición del
cursor, etc.).
En general está técnica sirve para hacer pruebas, pero también se emplea para
otros detalles.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
178
Instituto de Educación Superior
“San Ignacio de Monterrico”
Ejemplo:
ventana v1=new ventana();
v1.setLocation(100,100);
v1.setSize(300,300);
v1.setVisible(true);
WindowEvent
we=new
WindowEvent(v1,WindowEvent.WINDOW_CLOSING);
v1.dispatchEvent(we);
Suponiendo que ventana sea una clase preparada para escuchar eventos de tipo
WindowsEvent, se crea el objeto de evento we. El envío del evento se realiza con
el método dispachEvent.
Adaptadores
Para facilitar la gestión de eventos en ciertos casos, Java dispone de las
llamadas clases adaptadores. Gracias a ellas, en muchos casos se evita tener
que crear clases sólo para escuchar eventos. Estas clases son clases de
contenido vacío pero que son muy interesantes para capturas sencillas de
eventos.
Todas poseen la palabra adapter en el nombre de clase. Por ejemplo esta es la
definición de la clase MouseAdapter:
public abstract class MouseAdapter implements MouseListener
{
public
void
mouseClicked(MouseEvent
e)
{}
public
void
mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent
e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
Es una clase que implementa el interfaz MouseListener, pero que no define lo
que hace cada método de captura. Eso se suele indicar de manera dinámica:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
179
Instituto de Educación Superior
“San Ignacio de Monterrico”
JFrame ventana =new JFrame(“prueba”);
ventana.setLocation(100,100); ventana.setSize(300,300);
ventana.setVisible(true);
ventana.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
System.out.println(“Hola”);
}};
En el ejemplo anterior al hacer clic en la ventana se escribe el mensaje Hola en la
pantalla. No ha hecho falta crear una clase para escuchar los eventos. Se la crea
de forma dinámica y se la define en ese mismo momento. La única función del
adaptador es capturar los eventos deseados.
Otro ejemplo (hola mundo en Swing):
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class HolaMundoSwing {
public static void main(String[] args) {
JFrame frame = new JFrame("HolaMundoSwing"); JLabel label = new
JLabel("Hola Mundo");
frame.getContentPane().add(label);
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
}
);
frame.pack();
frame.setVisible(true);
}
}
El resultado de ese famoso código es esta ventana:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
180
Instituto de Educación Superior
“San Ignacio de Monterrico”
El evento windowClosing está capturado por una clase adaptadora, cuya efecto
es finalizar el programa cuando se cierra la ventana.
Clases adaptadoras:
•
ComponentAdapter
•
ContainerAdapter
•
FocusAdapter
•
InternalFrameAdapter
•
KeyAdapter
•
MouseAdapter
•
MouseMotionAdapter
•
PrintJobAdapter
•
WindowAdapter
Mensajes hacia el usuario. clase JOptionPane
Una de las labores típicas en la creación de aplicaciones gráficas del tipo que
sea, es la de comunicarse con el usuario a través de mensajes en forma de
cuadro de diálogo. Algunos cuadros son extremadamente utilizados por su
sencillez (textos de aviso, error, confirmación, entrada sencilla de datos, etc.).
La clase JOptionPane deriva de JComponent y es la encargada de crear este tipo
de cuadros. Aunque posee constructores, normalmente se utilizan mucho más
una serie de métodos estáticos que permiten crear de forma más sencilla objetos
JOptionPane.
Cuadros de información
Son cuadros de diálogo que sirven para informar al usuario de un determinado
hecho. Se construyen utilizando los siguientes métodos estáticos:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
181
Instituto de Educación Superior
“San Ignacio de Monterrico”
método
static
showMessageDialog(
padre, Object mensaje)
static
uso
void Muestra un cuadro de diálogo en el
Component contenedor padre indicado con un
determinado mensaje
void Muestra un cuadro de diálogo en el
showMessageDialog(
Component contenedor padre indicado con un
padre, Object mensaje, String título, determinado mensaje, título y tipo.
int tipo)
static
showMessageDialog(
void Igual que el anterior pero se permite
Component indicar un icono para acompañar el
padre, Object mensaje, String título, mensaje
int tipo, Icon i)
Estos son los posibles creadores de este tipo de cuadro. El tipo puede ser una de
estas constantes:
•
JOptionPane.INFORMATION_MESSAGE.
•
JOptionPane.ERROR_MESSAGE.
•
JOptionPane.WARNING_MESSAGE. Aviso
•
JOptionPane.QUESTION_MESSAGE. Pregunta
•
JOptionPane.PLAIN_MESSAGE. Sin icono
Ejemplo:
JOptionPane.showMessageDialog(this, “Soy un mensaje normal”, “Cuadro 1”,
JOptionPane.INFORMATION_MESSAGE);
Formando Emprendedores De Calidad Para Un Mundo Empresarial
182
Instituto de Educación Superior
“San Ignacio de Monterrico”
SOCKETS
Introducción
Los sockets son un proceso de comunicación entre aplicaciones que están en
diferentes máquinas en una red. Para mayor precisión, un socket es un punto de
comunicación por el cual un proceso puede emitir o recibir información.
Los sockets se hicieron populares con Berckley Software Distribution, de la
universidad norteamericana de Berkley. Y estos han de ser capaces de utilizar el
protocolo de streams TCP (Transfer Contro Protocol) y el de datagramas UDP
(User Datagram Protocol).
Los sockets utilizan una serie de primitivas para establecer el punto de
comunicación, para conectarse a una máquina remota en un determinado puerto
que esté disponible, para escuchar en él, para leer o escribir y publicar
información en él, y finalmente para desconectarse.
Una ilustración de los pasos a seguir en este proceso de comunicación, es el
siguiente:
Formando Emprendedores De Calidad Para Un Mundo Empresarial
183
Instituto de Educación Superior
“San Ignacio de Monterrico”
Recepción y envío de datos (SocketGet y SocketSend)
A continuación mostraremos 2 aplicaciones, la primera (SocketGet) recibirá
valores que son enviados por la segunda aplicación (SocketSend). Ambas,
aplicaciones las puede ejecutar en su misma PC, o en diferentes PC, pero debe
tener en cuenta que debe ejecutar primero SocketGet, que es la que estará
atenta a los envíos del cliente SocketSend.
Formando Emprendedores De Calidad Para Un Mundo Empresarial
184
Instituto de Educación Superior
“San Ignacio de Monterrico”
Esta sería la estructura de archivos de SocketGet, donde el programa principal es
justamente la clase SocketGet, la que esta diseñada para habilitar puerto 8013,
con la intención de recibir por allí, los datos que un cliente le envía.
Los demás programas son los que generan la interfase visual donde la clase
SocketGet muestra los datos enviados por el cliente. Asimismo, la clase MsgBox
mostrará mensajes de error, si es que estos se produjeran.
Código de la clase SocketGet:
import java.net.*;
import java.io.*;
public class SocketGet extends Thread {
private ServerSocket fServerSocket=null;
private DataInputStream entrada=null;
Formando Emprendedores De Calidad Para Un Mundo Empresarial
185
Instituto de Educación Superior
“San Ignacio de Monterrico”
public SocketGet() {
super();
try { fServerSocket= new ServerSocket(8013);
} catch(IOException e) {
MsgBox.show(e.getMessage());
}
}
public void run() {
Socket elCliente;
try {
// acepta esperar cliente
elCliente= fServerSocket.accept();
entrada = new DataInputStream(
elCliente.getInputStream());
while(true) {
// bytes disponibles para leer
int bytesDisponibles= entrada.available();
if(bytesDisponibles>0) {
byte[] buffer = new
byte[bytesDisponibles];
entrada.read(buffer);
String line= new String(buffer);
PanelMain.texto.append(line.trim() +
"\n");
PanelMain.texto.setCaretPosition(
PanelMain.texto.getText().length());
} else {
sleep(1000);
}
}
} catch(IOException e) {
Formando Emprendedores De Calidad Para Un Mundo Empresarial
186
Instituto de Educación Superior
“San Ignacio de Monterrico”
MsgBox.show(e.getMessage());
} catch(InterruptedException e) {
MsgBox.show(e.getMessage());
}
}
}
Código de la clase PanelMain:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PanelMain extends JPanel {
public static JTextArea texto= new JTextArea();
SocketGet sGet= new SocketGet();
public PanelMain() {
setLayout(new BorderLayout());
add(new JScrollPane(texto), BorderLayout.CENTER);
sGet.start(); // Se queda recibiendo
}
}
Código de la clase PanelMain:
import javax.swing.*;
import java.awt.*;
Código de la clase Formulario:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Formulario extends JFrame {
Formando Emprendedores De Calidad Para Un Mundo Empresarial
187
Instituto de Educación Superior
“San Ignacio de Monterrico”
static int wF= 200, hF= 300;
public Formulario(String s) {
super(s);
getContentPane().add(new PanelMain());
setLocation((getToolkit().getScreenSize().width - wF)/2,
(getToolkit().getScreenSize().height - hF)/2);
}
static public void main(String[] arg) {
JFrame f= new Formulario("Rocoge");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize(wF, hF);
f.setVisible(true);
}
}
Código de la clase MsgBox:
import javax.swing.JOptionPane;
public class MsgBox extends JOptionPane {
public static final int ERROR = JOptionPane.ERROR_MESSAGE;
public static final int INFORMATION=
JOptionPane.INFORMATION_MESSAGE;
public static final int WARNING = JOptionPane.WARNING_MESSAGE;
public static final int QUESTION =
JOptionPane.QUESTION_MESSAGE;
public static final int PLAIN = JOptionPane.PLAIN_MESSAGE;
Formando Emprendedores De Calidad Para Un Mundo Empresarial
188
Instituto de Educación Superior
“San Ignacio de Monterrico”
//constantes enteras para botones de confirmación
public static final int DEFAULT= JOptionPane.DEFAULT_OPTION;
public static final int YES_NO= JOptionPane.YES_NO_OPTION;
public static final int YES_NO_CANCEL =
JOptionPane.YES_NO_CANCEL_OPTION;
public static final int OK_CANCEL =
JOptionPane.OK_CANCEL_OPTION;
//constantes enteras de retorno
//para mensajes de confirmación
public static final int YES= JOptionPane.YES_OPTION;
public static final int NO= JOptionPane.NO_OPTION;
public static final int OK= JOptionPane.OK_OPTION;
public static final int CANCEL= JOptionPane.CANCEL_OPTION;
public MsgBox() {
super();
}
// Solo el diálogo con su mensaje
public static void show(String mensaje) {
JOptionPane.showMessageDialog(null, mensaje, "Mensaje",
JOptionPane.INFORMATION_MESSAGE);
}
// Mensaje y título para el diálogo
public static void show(String mensaje, String titulo) {
JOptionPane.showMessageDialog(null, mensaje, titulo,
JOptionPane.INFORMATION_MESSAGE);
}
// Mensaje, título y figura para el diálogo
public static void show(String mensaje, String titulo,
Formando Emprendedores De Calidad Para Un Mundo Empresarial
189
Instituto de Educación Superior
“San Ignacio de Monterrico”
int tipo) {
JOptionPane.showMessageDialog(null, mensaje, titulo,tipo);
}
// Mensaje y tipo de confirmación para el diálogo
public static int confirma(String mensaje) {
return JOptionPane.showConfirmDialog(null, mensaje,
"Confirmación", DEFAULT);
}
// Mensaje, título y tipo de confirmación para el diálogo
public static int confirma(String mensaje, String titulo,
int tipo) {
return JOptionPane.showConfirmDialog(null, mensaje,
titulo, tipo);
}
}
Esta sería la estructura de archivos de SocketSend, donde el programa principal
es justamente la clase SocketSend, la que esta diseñada para enviar datos a una
PC según su IP y un puerto, de tal forma que si hubiese una aplicación servidora
atenta a ese puerto, entonces podrá recoger los datos por allí.
Los demás programas son los que generan la interfase visual donde la clase
SocketSend muestra los datos que envía al Servidor. Asimismo, la clase MsgBox
mostrará mensajes de error, si es que estos se produjeran.
Código de la clase SocketSend
import java.net.*;
import java.io.*;
public class SocketSend extends Thread {
private Socket socket;
Formando Emprendedores De Calidad Para Un Mundo Empresarial
190
Instituto de Educación Superior
“San Ignacio de Monterrico”
private DataOutputStream salida=null;
int contador=0;
public SocketSend() {
try {
// poner IP de la otra PC, o dejar así
// sobre la misma
socket= new Socket("127.0.0.1", 8013);
salida = new DataOutputStream(socket.getOutputStream());
} catch(IOException e) {
MsgBox.show(e.getMessage());
}
}
public void run() {
while(true)
try {
salida.writeBytes(String.valueOf(contador));
salida.flush();
PanelMain.texto.append("Se envia: " +
contador++ + "\n");
PanelMain.texto.getText(setCaretPosition(
PanelMain.texto.getText().length());
sleep(3000);
} catch(IOException e)
MsgBox.show(e.getMessage(
} catch(InterruptedException e) {
MsgBox.show(e.getMessage());
}
}
}
Formando Emprendedores De Calidad Para Un Mundo Empresarial
191
Instituto de Educación Superior
“San Ignacio de Monterrico”
Formando Emprendedores De Calidad Para Un Mundo Empresarial
192