curso de java bÁsico

136
CURSO DE JAVA BÁSICO Contents El Proceso de Abstracción , Lenguajes Computacionales y Objetos.2 Java y sus Características.....................................3 Interoperabilidad de Plataforma..............................4 Recolección de Basura ("Garbage Collection").................4 JDK/J2SE.......................................................5 Un Programa Java...............................................6 Programa Java Introducción Basico.java..........................7 Librerías ("Packages") y JAR's...............................8 Estructura de Directorios para utilizar Librerías ("Packages") 9 Utilización de Librerías ("Packages")......................10 Archivos JAR ("Java Archives")..............................14 Generación de Archivo JAR..................................14 Observar contenido de Archivo JAR..........................14 Extracción de Archivo JAR..................................15 La Variable CLASSPATH.......................................15 Observando la Variable Ambiental CLASSPATH.................15 Modificar la Variable Ambiental CLASSPATH..................16 Utilizando la variable CLASSPATH al invocarse java o javac y otro comando....................................................17 Calificadores: Public, Private, Protected, Static y Final.. .18 Objetos y Primitivos........................................20 Clase Capsulas.............................................24 Campos("Fields") y Métodos.................................26 Clase Fecha................................................27 Significado de return......................................28 Constructores..............................................28 Clase Arboles..............................................29 Constructor Obligatorio....................................29 Métodos y Parámetros por Referencia........................29

Upload: aquiles-edu-robledo-olivares

Post on 22-Oct-2015

104 views

Category:

Documents


0 download

TRANSCRIPT

CURSO DE JAVA BÁSICO

ContentsEl Proceso de Abstracción , Lenguajes Computacionales y Objetos.............................................2

Java y sus Características..............................................................................................................3

Interoperabilidad de Plataforma..............................................................................................4

Recolección de Basura ("Garbage Collection").........................................................................4

JDK/J2SE.......................................................................................................................................5

Un Programa Java........................................................................................................................6

Programa Java Introducción Basico.java..................................................................................7

Librerías ("Packages") y JAR's...................................................................................................8

Estructura de Directorios para utilizar Librerías ("Packages")..............................................9

Utilización de Librerías ("Packages")..................................................................................10

Archivos JAR ("Java Archives")...............................................................................................14

Generación de Archivo JAR................................................................................................14

Observar contenido de Archivo JAR...................................................................................14

Extracción de Archivo JAR..................................................................................................15

La Variable CLASSPATH..........................................................................................................15

Observando la Variable Ambiental CLASSPATH..................................................................15

Modificar la Variable Ambiental CLASSPATH......................................................................16

Utilizando la variable CLASSPATH al invocarse java o javac y otro comando.............17

Calificadores: Public, Private, Protected, Static y Final...........................................................18

Objetos y Primitivos...............................................................................................................20

Clase Capsulas....................................................................................................................24

Campos("Fields") y Métodos..............................................................................................26

Clase Fecha.........................................................................................................................27

Significado de return......................................................................................................28

Constructores.....................................................................................................................28

Clase Arboles......................................................................................................................29

Constructor Obligatorio......................................................................................................29

Métodos y Parámetros por Referencia...............................................................................29

Generación de Documentación (Vía Comentarios)................................................................30

Comentarios en Java..........................................................................................................30

Otros parámetros (@) para Comentarios en Java...............................................................32

Generación de Documentación..........................................................................................34

Herencias ("Inheritance").......................................................................................................35

Definición de Dos Clases y Herencias ("Inheritance").........................................................37

Condicionales if/else..............................................................................................................38

Uso de los Ciclos for y while..................................................................................................40

Sintaxis del Ciclo for...........................................................................................................40

Sintaxis del Ciclo while.......................................................................................................41

Uso de switch.........................................................................................................................47

Sintaxis del switch..............................................................................................................47

Operadores Matemáticos, Relacionales y Lógicos..................................................................50

Operadores Relacionales....................................................................................................51

Operadores Matemáticos...................................................................................................53

"Overloading" no es lo mismo que "Overriding"..................................................55

Operadores Lógicos............................................................................................................60

Definición de Errores y Bloques try/catch/finally..................................................................63

Sintaxis del Bloque try/catch..............................................................................................63

Sintaxis del Bloque try/catch/finally..................................................................................64

Datos de Entrada....................................................................................................................66

"Streams"...............................................................................................................................68

Arreglos..................................................................................................................................74

Polimorfismo..........................................................................................................................77

Uso de "Casting".....................................................................................................................81

Interfases y Clases Abstractas................................................................................................81

Clases Abstractas................................................................................................................81

Interfases............................................................................................................................84

Vectores, Hashtables y otras estructuras de Datos................................................................89

Collections Framework...........................................................................................................94

"Threads".............................................................................................................................106

Garbage Collection...............................................................................................................112

"Assertions"..........................................................................................................................113

Compilación..........................................................................................................................118

Ejecución..............................................................................................................................118

Palabras Reservadas.........................................................................................................119

Java 5 / JDK 5 ...................................................................................................................120

Clases genéricas para el "Collections Framework" y Arreglos..........................................120

Auto-boxing y auto-unboxing, entre primitivos y objetos....................................................123

Limitaciones de for en Java 5...............................................................................................127

Anotaciones en código fuente -- meta-datos.......................................................................127

Uso de Enumeraciones.........................................................................................................129

Capacidad para importar clases estáticas.............................................................................131

El Proceso de Abstracción , Lenguajes Computacionales y Objetos.Todo lenguaje de programación ofrece un proceso de abstracción sobre un concepto extraído del mundoreal, en otras palabras, un lenguaje permite un mapeo entre un problema espacial(real) hacia uno maquinal que será aquel ejecutado por una computadora. Es en esta capacidad de abstracción es que reside la facilidad/complejidad de generar programas funcionales capaces de resolver el problema espacial.

El lenguaje ensamblador es una abstracción de la maquina ejecutora ("procesador"); lenguajes comoFortran, Basic y C son abstracciones de este mismo lenguaje ensamblador, sin embargo, aunque estos últimos ofrecen un avance sobre el lenguaje ensamblador, estos aún requieren de procesos de abstracción considerables.

Lo anterior produce programas que son difíciles de escribir y actualizar por la simple razón que el problema espacial dista en grandes proporciones del mapeo maquinal; frente a estas deficiencias han surgido lenguajes más apegados al problema real tales como: PROLOG donde todo problema es mapeado a una cadena de decisiones o LISP donde todo problema es convertido a listas.

Aquí es donde los lenguajes orientados a objetos salen a relucir, esto se debe a que permiten llevar acabo un mapeo más directo con el problema espacial(real). Allan Kay, describió las 5 principales características de Smalltalk, uno de los primeros lenguajes orientados a objetos y uno de los lenguajes en los cuales esta basado Java:

Todo es un objeto: Considere un objeto una variable especial, no solamente guarda datos, sino también se pueden hacer solicitudes a este Objeto en sí. En teoría, cualquier elemento en el problema espacial(real) (edificios, servicios, automóviles, u otra entidad) puede ser representado como un objeto en un programa.

Un programa es un conjunto de Objetos, enviándose mensajes entre sí : Para realizar una solicitud a un Objeto es necesario enviarle un mensaje. Concretamente se puede pensar que un mensaje es una solicitud para llamar una función que pertenece a cierto objeto.

Cada Objeto tiene su memoria, conformada por otros Objetos : En otras palabras, es posible generar un tipo de Objeto nuevo agrupando Objetos existentes. De esta manera se pueden armar estructuras complejas en un programa, escondidas detrás de la simplicidad de Objetos.

Todo Objeto tiene su Tipo: En este sentido Tipo se refiere a Clase, donde cada Objeto es unainstancia de una Clase en cuestión.La característica más importante de una Clase es el tipo de mensajes que pueden ser enviados a ella.

Todo Objeto de un mismo tipo puede recibir los mismos mensajes : Esto implica que si un Objeto es del tipo Circulo, un Objeto del tipo Figura será capaz de recibir mensajes de Circulo, puesto que un Circulo es una Figura. Este concepto forma parte medular de todo lenguaje orientado a Objetos y será explorado en una sección especifica de este curso.

En la gráfica anterior se puede observar una Clase conformada por una Lampara (problema espacial real) a la cual pueden ser enviados distintos mensajes tales como: prender, apagar, girar y cambiar; para crear un Objeto de esta Clase y ser utilizado dentro de un programa es necesario generar una instancia del mismo, esto se demuestra a continuación:

Lampara candil = new Lampara();

candil.prender();

El primer renglón genera una instancia de la Clase Lampara la cual es asignada a la referencia candil; en la segunda linea, a través de esta referencia se manda llamar el método prender() definido en la mismaClase; a través de referencias es posible generar distintos Objetos del mismo tipo cada uno con su propio comportamiento:

Lampara candil = new Lampara();

Lampara manual = new Lampara();

Lampara jardin = new Lampara();

candil.prender();

candil.girar();

manual.apagar();

jardin.prender();

jardin.elevar();

candil.apagar();

Lo anterior demuestra una de las principales características de los lenguajes orientados a Objetos, sin embargo, a continuación se describen las características especificas del Lenguaje Java y porque es considerado uno de los más avanzados en su ramo.

Java y sus CaracterísticasEntre los lenguajes orientados a Objetos hoy en día Java figura entre los más utilizados comparado con sus homólogos, pre-aparición de Java el lenguaje C++ se encontraba en la cúspide para desarrollos de Software con orientación a Objetos, sin embargo, Java ha venido suplantando su uso debido a dos características muy especificas que C++ no ofrece.

Interoperabilidad de PlataformaEsta característica forma parte del lema Java: "Write once, run everywhere" (Escribalo una vez, ejecutelo en todos lados), a continuación se ilustra este concepto.

El proceso de creación es muy similar a la de otros lenguajes: una vez creado el Código Fuente es necesario compilarlo para generar un binario, este binario denominado Byte-Code en Java lleva por extensión .class, sin embargo, a diferencia de otros lenguajes este binario (Byte-Code) puede ser ejecutado en diversas plataformas obteniéndose el mismo resultado final.Esta interoperabilidad de ejecución se debe a que el Byte-Code es ejecutado en una maquina virtual llamada "Java Virtual Machine", es a través de este componente que se logra una ejecución idéntica en distintos ambientes.

Hoy en día, existen diversos JVM("Java Virtual Machines") para distintas plataformas y ambientes que oscilan desde los Sistemas Operativos Linux,Solaris,Windows,HP-UX hasta productos como Bases de DatosOracle y Servidores de Aplicaciones BEA; esto permite que código escrito en Java pueda ser ejecutado encualquier ambiente produciéndose los mismos resultados.

Recolección de Basura ("Garbage Collection")En la sección anterior se pudo observar como son generadas distintas instancias de Objetos a través de una referencia, sin embargo, el uso de esta metodología trae consigo otro pregunta : Que se hace con estas instancias una vez que ya no son utilizadas ? La importancia de este concepto se debe al uso de memoria en una computadora, es lógico que estas instancias de Objetos requieren memoria ("RAM") para mantener su información, ahora bien, también tiene sentido asumir que la memoria ("RAM") es limitada en todo sistema, esto trae consigo el problema denominado "fuga de memoria" ("memory leak").

Si alguna vez ha programado en el lenguaje C++ debe estar familiarizado con este concepto, el cual significa que los recursos de memoria ("RAM") han sido agotados en el sistema. Suponga que ha diseñado un programa que invoca la creación de miles de instancias , eventualmente su memoria ("RAM") puede fugarse si no ha tomado las precauciones necesarias para deshacerse/destruir estas instancias de Objetos.

En el mundo Java, esta administración explicita de instancias de Objetos no es necesaria debido al proceso de "Garbage Collection"; este mecanismo a través del JVM ("Java Virtual Machine") inspecciona constantemente la memoria empleada por Java, lo cual le permite ir eliminando las instancias de Objetos que han dejado de ser utilizadas en un programa, en el proceso despreocupándose de la destrucción de Objetos.

Aunque el concepto de recolección de basura ("Garbage Collection") posee una infinidad de detalles en su fondo, es conveniente saber que este proceso es llevado automáticamente por Java, y que es una facilidad no ofrecida en otros lenguajes como C++

JDK/J2SE.JDK ("Java Development Kit") y J2SE ("Java 2 Standard Edition") son nombres para el mismo componente utilizado en ambientes Java, el cual agrupa las diversas funcionalidades necesarias para desarrollar programas Java.

Hoy en día, existen JDK's/J2SE para diversos ambientes y plataformas, los cuales ofrecen lo siguiente:

Un compilador Java, capaz de generar Byte-Code. Un JVM ("Java Virtual Machine"), capaz de ejecutar Byte-Code. Un conjunto de Clases base utilizadas para generar programas Java. Otras utilerías para administrar código escrito en Java.

Para este curso se hace uso del JDK/J2SE de Sun Microsystems , aunque claro esta, cualquier programa generado en este JDK puede ser ejecutado otros ambientes, al igual que programas compilados en otros JDK's/J2SE pueden ser ejecutados en este ambiente. El conjunto de Clases base proporcionadas por el JDK/J2SE incluyen clases de uso común tales como : Manipulación de String's, Fechas y Números así como todas las funcionalidades base esperadas de un lenguaje computacional como: Ordenamiento y manipulación de Arreglos, Operaciones matemáticas complejas (Trigonométricas y Exponenciales) y escritura/lectura de "Streams".

Además de estas Clases base, el JDK/J2SE posee otra serie de Clases especializadas que también ofrecen la base para la creación de otros componentes Java que incluyen : Applets, Objetos CORBA , Fragmentos Auditivos, Soporte de Criptografía (Seguridad) y Manipulación de XML entre otras funcionalidades.

Aunado a las Clases base, el JDK/J2SE incluye una serie de ejecutables utilizados para diversas tareas como : Creación de ejecutables (Byte-Code), generación de documentación, creación de "Stubs" y "Skeletons" a través de IDL e inclusive una implementación de un ORB ("Object Request Broker") para interactuar con ambientes CORBA, entre otros ejecutables.

La estructura de directorios del JDK/J2SE es la siguiente:

JDK 1.4.2

___________|_________________

| | |

bin lib jre

| | ________|__________

java* tools.jar | |

javac* dt.jar bin lib

javap* | ________ ___|___ _________ ________ _______

javah* java* | | | | | |

javadoc* rt.jar ext security i386 applet fonts

charsets.jar | / \

| / \

localedata.jar server client

Como se puede observar, la estructura se encuentra dividida en tres grandes partes :

bin: Incluye los ejecutables para la generación de programas Java. lib: Contiene las librerías (Clases) base empleadas en la generación de la gran mayoría

de programas Java. jre: Incluye el ambiente necesario para ejecutar programas Java, el cual a su vez se

encuentra sub-dividido en distintos directorios.

Dependiendo de la manera en que haya instalado la documentación del JDK, se recomienda observarla en estos momentos para que se familiarice con sus Clases Base.

Un Programa Java.Para entrar en materia es necesario escribir nuestro primer programa Java, el cual ilustra la sintaxis así como estructura de un programa básico.

Código Fuente Basico.java

public class Basico {

public static void main(String args[]) {

System.out.println("Un despliegue de Datos");

}

}

Programa Java Introducción Basico.javaLa 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 el archivo Basico.java 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 el vocablo class así como un calificador de acceso, en este caso se utiliza el calificador public, el uso y variación de calificadores será descrito posteriormente cuando se aborde el tema de librerías ("packages") donde es de influencia su uso.

Dentro de la definición de la Clase se deben incluir los respectivos métodos que podrán ser invocados. En el caso del programa Basico.java únicamente es definido el método main, dicho método es invocado por "default" al ejecutarse el programa (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:

Primeramente se definen los calificadores del método en este caso siendo public static Posteriormente se define el valor de retorno del método: void, lo cual indica que no será

retornado ningún valor Le sigue el nombre del método: main Dentro de paréntesis se incluyen los parámetros de entrada para el método (String

args[]). Finalmente la Clase/Método System.out.println envía un mensaje a la pantalla

Metodo principal main

El método principal main de una Clase Java es inalterable, es este sentido inalterable se refiere a sus características:

Siempre debe incluir los calificadores : public y static. Nunca puede retornar un valor como resultado, por ende, siempre debe indicar el

valor void como retorno. Su parámetro de entrada siempre será un arreglo de String's (String[]) el cual es tomado

de la linea de comandos o una fuente alterna.

Aunque no es un requerimiento 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.

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 comandojava:

$ 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.

Librerías ("Packages") y JAR's.Conforme empieza a crecer un desarrollo de Software surge la necesidad de reutilizar ciertos componentes que ya han sido escritos, así como dar cierta estructura para mantener una organización de código; al igual que otros lenguajes, esta organización se lleva acabo mediante librerías, denominadas "packages" en el mundo Java .

Java toma una perspectiva nueva respecto a otros lenguajes, adoptando una convención a partir deDominios en Internet, esto es, nuestros programas y por ende librerías ("packages") estarán basados en el nombre de la empresa/dominio para la cual son diseñados, lo anterior permite evitar la tan conocidaColisión de Nombres en Software.

Esta Colisión de Nombres se da cuando dos Clases llevan el mismo nombre y ambas requieren ser utilizadas dentro de un programa en particular, empleando Dominios en Internet se garantiza que el nombre de la Clase/Librería sea única , puesto que es una característica de Dominios en Internet.

Para que puedan ser asignadas Clases a determinadas librerías es necesario indicar dicha Librería("Package") en la definición inicial de un programa, además de esto, es necesario crear una estructura de directorios para generar las determinadas Clases; lo anterior se ilustra mejor a través de un ejemplo:

Estructura de Directorios para utilizar Librerías ("Packages")

Para utilizar "Packages" en Java es necesario generar una estructura de directorios que lleven la misma secuencia de las librerías que desean diseñarse, observe:

+-com+-

|

+-osmosislatina+-

|

+-escritura+-

|

+-graficas+-

|

+-correo+-

|

+-errores+-

|

+-auxiliares+-

Lo anterior demuestra una estructura para las librerías del dominio com.osmosislatina, se debe mencionar que no existe ninguna restricción en el número de directorios anidados, esto es, es posible que existan librerías de 5 o 10 niveles de profundidad, esta sub-clasificación se va realizando en base a los requerimientos de las librerías y es dentro de cada sub-directorio donde son colocadas las distintas Clases.

Además de la generación de estos directorios, se debe incluir un calificador de la librería en el código fuente de cada Clase :

package com.osmosislatina.escritura;

Lo anterior sería incluido en todos los programas pertenecientes a la librería ("Package")com.osmosislatina.escritura, debe mencionarse que este calificador debe ser la primer declaración en todo programa Java (Salvo no vaya a ser incluido en librerías); recuerde que además de este calificador el programa debe estar colocado en la estructura de directorios correspondiente, lo anterior es sumamente importante al llevarse acabo la compilación para la generación de Byte-Code

Código Fuente Pantalla.java

La siguiente Clase define dos métodos que pueden ser utilizados para imprimir resultados a pantalla:

La siguiente Clase debe ser colocada bajo la estructura de directorioscom/osmosislatina/escritura, en base a su ambiente (Linux o Windows) por favor genere dicha estructura para colocar esta Clase

package com.osmosislatina.escritura;

public class Pantalla {

public static void sinSalto(String s) {

System.out.print(s);

}

public static void conSalto(String s) {

System.out.println(s);

}

}

Primeramente se declara la librería ("package") a la que será asignada el programa a través del vocablopackage.

Se define la Clase con los respectivos métodos sinSalto y conSalto. NOTA: Recuerde que este programa debe residir dentro del

directorio com/osmosislatina/escritura/. NOTA 2: Observe que esta Clase no posee el método principal main.

Para compilar esta Clase basta ejecutar el comando:

$ javac Pantalla.java

Lo anterior genera el archivo de ByteCode : Panatalla.class. En la siguiente sección será diseñado un programa que haga uso de esta librería ("package").

Utilización de Librerías ("Packages")

Para emplear Librerías("Packages") en un programa es necesario importarlas, esto es llevado acabo mediante el calificativo import como se demuestra a continuación :

import com.osmosislatina.graficas.Recuadro;

La declaración anterior permite que la clase Recuadro de la librería ("Package") com.osmosislatina.graficaseste disponible en el programa en cuestión, las características del uso de import son las siguientes:

Se debe declarar al inicio de un programa, antes de la definición de la clase, aunque

posterior a la definición de package (si existiese) .

Se pueden utilizar un numero ilimitado de import.

Es posible importar todas las clases de una librería a través de un asterisco (*).

Código Fuente MandaPantalla.java

La siguiente Clase manda llamar métodos definidos en la Clase Pantalla.java.

La siguiente Clase debe ser colocada bajo la estructura de directorioscom/osmosislatina/auxiliares, en base a su ambiente (Linux o Windows) por favor genere dicha estructura para colocar esta Clase

package com.osmosislatina.auxiliares;

import com.osmosislatina.escritura.*;

public class MandaPantalla {

public static void main(String args[]) {

Pantalla primera = new Pantalla();

primera.conSalto("Esto es un renglon CON salto de linea");

primera.conSalto("Esta linea tambien tiene salto");

primera.sinSalto("Linea Continua");

primera.sinSalto("Linea Continua");

primera.conSalto("Esta linea si tiene salto!");

primera.sinSalto("Termina sin salto");

System.out.println(" Se termina el uso de funciones");

}

}

Primeramente se declara la librería ("package") a la que será asignada el programa a través del vocablopackage.

Posteriormente se importan las Clases que serán utilizadas en el programa a través del vocablo import.

Se define el método main dentro del cual se genera una instancia de la Clase Pantalla (aquella importada) y sobre la que posteriormente se mandan llamar los distintos métodos a través de sureferencia.

La compilación (Generación de Byte-Code) de esta Clase requiere que esta sea llevada acabo bajo el directorio raíz de la estructura de Clases, esto es, aunque esta Clase radique dentro del directoriocom/osmosislatina/auxiliares es necesario llevar acabo la compilación desde la raíz (./com), lo anterior se debe a que esta Clase requiere ubicar (importar) las Clases de la Librería ("Package")com/osmosislatina/escritura; los detalles de esta rutina serán descritos en la sección de CLASSPATH.

Para ejecutar este programa también se requiere descender al directorio raíz de la librería ("Package"), razón que también esta relacionada con la variable CLASSPATH y será descrita a fondo en su sección.

Una vez acumuladas un número substancial de Clases en una Librería("Package") será necesario colocarlas en una estructura que permita una distribución fácil y práctica, esto es, si ha generado 100 o 500 clases sería muy impráctico distribuir la estructura de directorios a un tercero para ser utilizada, para este problema han sido diseñados los archivos JAR ("Java Archives").

Archivos JAR ("Java Archives")Una archivo JAR es simplemente un archivo comprimido que incluye una estructura de directorios con Clases, lo anterior permite:

Distribuir/Utilizar Clases de una manera eficiente a través de un solo archivo.

Declarar dichas Clases de una manera más eficiente en la variable CLASSPATH.

En todo JDK/J2SE se incluye el comando jar el cual permite generar, observar y descomprimir archivos JAR; a continuación se describen estos tres casos.

Generación de Archivo JAR

Para generar un archivo JAR es necesario colocarse bajo el directorio raíz de la estructura de directorios donde residen las Clases:

$ jar cvf osmosislatina.jar *

Al ejecutar el comando anterior se genera una archivo JAR llamado osmosislatina.jar con todos los archivos .class que residen en la estructura de directorios.

Observar contenido de Archivo JAR

Para observar el contenido de un archivo JAR, se utiliza un comando similar:

$ jar tvf osmosislatina.jar

El comando anterior despliega el contenido del archivo JAR osmosislatina.jar; NOTA: Este comando únicamente despliega el contenido, la extracción de un archivo JAR se describe a continuación.

Extracción de Archivo JAR

En ocasiones es conveniente extraer el contenido de un archivo JAR para llevar acabo modificaciones, esto se realiza a través del siguiente comando:

$ jar xvf osmosislatina.jar

El comando anterior genera una estructura de directorios con el contenido del archivo JARosmosislatina.jar. En la siguiente sección se describe como son utilizados estos archivos JAR en la compilación/ejecución de programas Java.

La Variable CLASSPATH.Al utilizar Clases y Librerías en ambientes Java surge otro tema en lo que concierne a su ubicación: Cuales Clases/Librerías están disponibles al momento de Compilar/Ejecutar programas Java ? , la respuesta a esta pregunta es la conocida variable CLASSPATH.

A través de esta variable se indica donde residen las Clases/Librerías("Packages") de nuestro ambiente, en ella se definen los directorios base de Clases así como cualquier archivo JAR que requiera ser cargado al compilar/ejecutar programas Java.

Para definir/alterar esta variable existen dos alternativas: Definirse al nivel de Sistema en variables de ambientales o al tiempo de invocarse un comando Java, ya sea java o javac.

Observando la Variable Ambiental CLASSPATH

Para observar la variable CLASSPATH en un ambiente Unix/Linux se utiliza el siguiente comando :

$ set | egrep CLASSPATH

CLASSPATH=/usr/local/jdk/lib/tools.jar:/root/mislibrerias/:.:

Para observar la variable CLASSPATH en un ambiente Windows NT/2000/XP se utiliza el siguiente comando :

Seleccione:

Start- Settings -Control Panel y el icono System.

En Windows NT:

Seleccione el Tab Environment

En Windows 2000/XP :

Seleccione el Tab Advanced

Posteriormente entre en Variables de Entorno ("Environment Variables").

Localize "CLASSPATH" en las variables del usuario ("User Variables") y

variables de Sistema ("System Variables").

Como se puede observar en el despliegue anterior, los valores de la variable CLASSPATH se encuentran separados por dos puntos (:) [ o punto y coma (;) para Windows], el valor de CLASSPATH declarado anteriormente significa :

El archivo JAR llamado tools.jar bajo el directorio /usr/local/jdk/lib estará accesible al compilar/ejecutar programas Java.

Las estructuras de directorios ubicados bajo /root/mislibrerias también serán localizadas al invocar elJVM ("Java Virtual Machine").

Finalmente la ultima declaración en CLASSPATH (:.:) indica que toda estructura de directorios residente en el directorio actual de compilación/ejecución será accesible.

Modificar la Variable Ambiental CLASSPATH

Para modificar el valor de la variable ambiental CLASSPATH en un sistema Unix/Linux se utilizaría el siguiente comando:

$ export CLASSPATH=/root/osmosislatina.jar:$CLASSPATH

Para modificar el valor de la variable ambiental CLASSPATH en un sistema Windows NT/2000/XP se utilizaría el siguiente comando:

C:> set CLASSPATH=C:\osmosislatina.jar;%CLASSPATH%;.;

El comando anterior modifica el valor de la variable CLASSPATH agregando el archivo JAR (osmosislatina.jar), el termino $CLASSPATH (%CLASSPATH% en Windows) indica el valor previo de la variable, lo cual permite mantener los valores pre-existentes.

Cabe mencionar que al modificar la variable CLASSPATH de esta manera, será únicamente el Shell existente el cual reflejará estos cambios, esto es, si usted posee otros programas que hacen uso de Java será necesario modificar el valor de CLASSPATH al nivel de sistema, lo anterior en Sistemas Unix se hace modificando el archivo /etc/profile o el perfil del usuario ~/.bashrc, de la siguiente manera:

CLASSPATH="/usr/local/mislibrerias"

export CLASSPATH

La modificación de la variable CLASSPATH a nivel de sistema para ambientes Windows es llevada acabo de la siguiente manera:

Seleccione:

Start- Settings -Control Panel y el icono System.

En Windows NT:

Seleccione el Tab Environment

En Windows 2000/XP :

Seleccione el Tab Advanced

Posteriormente entre en Variables de Entorno ("Environment Variables").

Localize o genere la variable "CLASSPATH" en variables de Sistema ("System Variables").

Utilizando la variable CLASSPATH al invocarse java o javac y otro comando.

En ocasiones es necesario cargar/ubicar ciertas librerías("packages")/clases que solo serán utilizadas ocasionalmente, por lo que modificar la variable CLASSPATH al nivel de ambiente sería innecesario, para esto pueden ser pasados parámetros al tiempo de invocar los comandos java o javac :

$ javac -classpath "$CLASSPATH:.:/lib/postgresql.jar" Conexion.java [Linux]

C:> javac -classpath "%CLASSPATH%;C:\osmosislatina.jar;.; Conexion.java [Windows]

o

$ javac -cp "$CLASSPATH:.:/lib/postgresql.jar" Conexion.java [Linux]

C:> javac -cp "%CLASSPATH%;C:\osmosislatina.jar;.; Conexion.java [Windows]

A través del flag classpath o cp se indica que al momento de llevarse acabo la compilación de la ClaseConexion.java, sea cargado el archivo JAR postgresql.jar además del valor actual ($CLASSPATH en Linux o%CLASSPATH% en Windows) de la variable ambiental CLASSPATH.

Este mismo mecanismo puede ser utilizado para los diversos comandos Java, lo cual permite ubicar/cargar librerías("packages")/Clases al momento de ejecución.

Calificadores: Public, Private, Protected, Static y Final.El uso de calificadores de acceso en Java tiene sus bases en el uso de librerías ("packages"), al ser diseñado un programa existen diversas funciones/métodos y variables dentro de éste, algunas de estas requerirán ser modificadas conforme incrementen las necesidades del programa, mientras otras permanecerán inmóviles, la importancia de estos cambios requiere que sean utilizados calificadores para permitir/negar el acceso a ciertos segmentos del programa, analicemos el siguiente caso:

Usted ya diseño 300 clases que están siendo re-utilizadas por otros programas, sin embargo, se le ha solicitado una modificación radical a estos objetos base, cuales métodos/campos de estas 300 Clases puede modificar sin quebrantar los otros programas que hacen uso de estas ? Aquí puede surgir un serio problema sino han sido utilizados los calificadores de acceso acordemente.

Ahora bien, además de los calificadores de acceso que permiten restringir el uso de métodos/campos a determinadas situaciones, también existen otros calificadores que

afectan directamente la creación y uso de instancias por clase estos calificadores son static yfinal.

public : Acceso libre .

El uso del calificador public significa que toda definición será accesible de cualquier punto, ya sea un método, campo o clase. Su uso implica un acceso global, desde luego el uso de este calificativo en Clases que serán modificadas constantmente es fuertemente desalentado, ya que puede quebrantar dependencias forjadas en versiones previas.

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.

protected : Clases Heredadas y misma Clase.

El uso de protected es utilizado bajo los conceptos de Herencias ("Inheritance"), aunque este tema será descrito en otra sección, mediante protected es posible accesar elementos de la Clase Hereditaria ("Inherited"), aunque no aquellos que utilicen el calificador private.

En otras palabras, si determinada Clase hijo hereda ("inherit") el comportamiento de una Clase padre, la Clase hijo tendrá acceso a todos aquellos campos/métodos definidos como protected en padre, pero no aquellos declarados como private en padre.

Ningún Calificador : Clase en Librería y misma Clase .

Finalmente, 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").

static : Una sola instancia .

El uso del vocablo static ha venido siendo utilizado en los métodos principales (main) de los programas escritos anteriormente, su uso esta relacionado directamente al uso de instancias en Clases; en ocasiones es necesario o conveniente generar elementos que tomen un mismo valor para cualquier número de instancias generadas o bien invocar/llamar métodos sin la necesidad de generar instancias, y es bajo estas dos circunstancias que es empleado el calificador static.

El primer uso de static puede ser poco evidente, pero tomemos el caso de la Clase mencionada al inicio de este curso de una Lampara, en caso de requerirse un elemento como un apagador pudiera resultar sobrado generar una instancia para cada Lampara, en este caso pudiera ser restringido un apagador a una sola instancia mediante el calificador static permitiendo que dicho elemento sea utilizado por todas las instancias de Lampara ahí generadas; desde luego la descripción anterior esta trivializada, sin embargo, conforme se avance en el presente curso serán ilustrados otros ejemplos con este mecanismo.

La segunda situación para el uso de static puede ser ejemplificada perfectamente a través del método mainJava, el método main puede ser llamado automáticamente al invocarse la respectiva Clase debido a que no se encuentra asociado con ningún tipo de instancia, esto implica que su comportamiento siempre será el mismo independientemente de la instancia que realza su llamada.

Dos aspectos característicos de utilizar el calificador static en un elemento Java son los siguientes :

No puede ser generada ninguna instancia (Uso de new) de un elemento static puesto que

solo existe una instancia.

Todos los elementos definidos dentro de una estructura static deben ser static ellos

mismos , o bien, poseer una instancia ya definida para poder ser invocados.

NOTA: Lo anterior no implica que no puedan ser generadas instancias dentro de un

elemento static; no es lo mismo llamar/invocar que crear/generar.

La teoría del concepto static puede ser un poco difícil de digerir, sin embargo, como fue mencionado anteriormente, conforme avance el curso serán descritos diversos fragmentos de código para dejar en claro su uso.

final : Una sola instancia y definitiva.

El calificador final estrechamente relacionado con el uso de static implica una asignación única y definitiva al elemento de una clase. A diferencia de static que implica una sola instancia, el termino finallleva dicha instancia a una definición única y como su nombre lo implica final.

Generalmente final es utilizado en aquellos elementos que no serán modificados al momento de ejecutarse ("Run-Time") un programa o clase , a través de final se asigna un valor que no podrá ser modificado bajo ninguna circunstancia al momento de ejecución ("Run-Time), en efecto logrando un nivel de eficiencia en ejecución.

De igual manera que la utilización de static el uso de final será ilustrado conforme se avanze en este curso.

Objetos y Primitivos.Al diseñar sus primeros programas en Java, pudo notar que todo gira alrededor de instancias de Objetos que son manipuladas a través de referencias:

Lampara candil = new Lampara();

candil.prender();

Al utilizar referencias en Java estas pueden ser inicializadas sin que pertenezcan a ninguna instancia de Objeto:

String let;

La declaración anterior genera una referencia, sin embargo, esta no es asociada con ninguna instancia de Objeto, para llevar acabo este proceso es necesario utilizar el vocablo new.

String let = new String("Algo sencillo")

La declaración anterior no solo genera una instancia nueva de la Clase String, sino le asigna un valor inicial ("Algo sencillo") el cual es asignado a la referencia let; este mecanismo de agregar valores al generar una instancia es llevada acabo a través de métodos denominados constructores los cuales serán descritos posteriormente.

El hecho de generar instancias de objetos es un proceso involucrado ( hablando en términos computacionales mas no en sintaxis ) , es por esto que Java ofrece un mecanismo simplístico para definir estructuras de datos como alternativa a utilizar Clases/Objetos, dichas estructuras son conocidas como primitivos.

Aunque estrictamente hablando un primitivo no es una clase a través de un primitivo se facilita el uso de valores comúnmente utilizados en lenguajes de programación, donde el generar una instancia de Clase resultaría en un proceso tardado y mecánico. En la siguiente tabla se describen los diversos primitivos disponibles en el lenguaje Java:

Tipo de Primitivo

Tamaño

Valor Mínimo

Valor Máximo

Valor Default

"Wrapper"

boolean - - - false Boolean

char 16-bit Unicode 0Unicode 216- 1

`\u0000' (null)

Character

byte 8-bit -128 +127 (byte) 0 Byte

short 16-bit -215 +215-1 (short) 0 Short

int 32-bit -231 +231-1 0 Integer

long 64-bit -263 +263-1 0L Long

float 32-bit IEEE754 IEEE754 0.0f Float

double 64-bit IEEE754 IEEE754 0.0d Double

void - - - - Void

Nótese que estos primitivos inician con letra minúscula, en la tabla anterior se describen los rangos de dichos primitivos así como sus respectivos "Wrappers"; estos "Wrappers" son Clases utilizadas como contra parte de primitivos:

char c = 'x';

Character C = new Character(c);

La primer linea genera un primitivo el cual es asignado a la variable c, posteriormente se genera unainstancia de la Clase Character (Nótese el uso del vocablo new); el uso de "Wrappers" (Objetos) para primitivos es empleado bajo ciertas circunstancias, sin embargo, vale mencionar que existen diversos mecanismos para convertir de Clases tipo Objeto ("Wrappers") a primitivos y viceversa, a continuación se describen los mecanismos para realizar este tipo de conversiones :

Código Fuente Capsulas.java

public class Capsulas {

public static void main(String[] args) {

// Inicializar Primitivos

int i1 = 12;

int i2 = 300;

long l1 = 200L; // sufijo para primitivo long

long l2 = 200l; // sufijo para primitivo long

long l3 = 200;

float f1 = 1e-39F; // sufijo para primitivo float

float f2 = 1e+11f; // sufijo para primitivo float

float f3 = 1;

double d1 = 32e46d; // sufijo para primitivo double

double d2 = 14D; // sufijo para primitivo double

double d3 = 1;

// Inicializar "Wrappers" con primitivos

Integer wi1 = new Integer(i1);

Long wl1 = new Long(l1);

Float wf1 = new Float(f1);

Double wd1 = new Double(d1);

// Convertir/Imprimir Valores de "Wrappers" como double (primitivo)

prt(wi1.doubleValue());

prt(wl1.doubleValue());

prt(wf1.doubleValue());

prt(wd1.doubleValue());

// Convertir/Imprimir Valores de "Wrappers" como float (primitivo)

prt(wi1.floatValue());

prt(wl1.floatValue());

prt(wf1.floatValue());

prt(wd1.floatValue());

// Convertir/Imprimir Valores de "Wrappers" como long (primitivo)

prt(wi1.longValue());

prt(wl1.longValue());

prt(wf1.longValue());

prt(wd1.longValue());

// Convertir/Imprimir Valores de "Wrappers" como integer (primitivo)

prt(wi1.intValue());

prt(wl1.intValue());

prt(wf1.intValue());

prt(wd1.intValue());

// Inicializar "String's" con valores numericos

String str1 = "7";

String str2 = "57";

String str3 = "3.23";

String str4 = "797.43";

// Inicializar "Wrappers" con "String's"

Integer wi2 = new Integer(str1);

Long wl2 = new Long(str2);

Float wf2 = new Float(str3);

Double wd2 = new Double(str4);

// Imprimir Valores

prt(wi2);

prt(wl2);

prt(wf2);

prt(wd2);

// Convertir/Imprimir Valores de "Wrappers" como String's

prt(wi2.toString());

prt(wl2.toString());

prt(wf2.toString());

prt(wd2.toString());

// Inicializar "Wrappers" con primitivos

Integer wi3 = new Integer(0);

Long wl3 = new Long(0);

Float wf3 = new Float(0);

Double wd3 = new Double(0);

// Convertir/Imprimir Valores de "Wrappers" a través de "String's"

// a su respectivo primitivo

prt(wi3.parseInt(str1));

prt(wl3.parseLong(str2));

prt(wf3.parseFloat(str3));

prt(wd3.parseDouble(str4));

}

static void prt(String s) {

System.out.println("Un String con valor " + s);

}

static void prt(Integer i) {

System.out.println("Un Integer (\"Wrapper\") con valor " + i);

}

static void prt(Long l) {

System.out.println("Un Long (\"Wrapper\") con valor " + l);

}

static void prt(Float f) {

System.out.println("Un Float (\"Wrapper\") con valor " + f);

}

static void prt(Double d) {

System.out.println("Un Double (\"Wrapper\") con valor " + d);

}

static void prt(int i) {

System.out.println("Un int (primitivo) con valor " + i);

}

static void prt(long l) {

System.out.println("Un long (primitivo) con valor " + l);

}

static void prt(float f) {

System.out.println("Un float (primitivo) con valor " + f);

}

static void prt(double d) {

System.out.println("Un double (primitivo) con valor " + d);

}

}

Clase Capsulas

Como en todo programa Java , primeramente se define la Clase a través del vocablo class.

Se define el método principal main y dentro de éste se generan diversos campos ("fields"), nótese que en algunas declaraciones se emplea un sufijo para definir el valor del primitivo (L,l,F,f,D,d), lo anterior se hace con la finalidad de asignar un espacio apropiado para futuras manipulaciones numéricas.

Uso de Sufijos

Aunque el uso de sufijos no es obligatorio, su uso se hace más evidente cuando los primitivos son empleados para realizar operaciones matemáticas con mayor precisión.

Tomemos un número definido como 14D, aunque el número catorce bien pudiera ser un entero (int) éste se esta definiendo como un número double a través del sufijo, esto permite que el número sea manipulado con otros primitivos double sin perder precisión, esta pérdida de precisión será ilustrada a continuación.

Posteriormente, se inicializan cuatro "Wrappers" a través de los primitivos definidos anteriormente.

Seguido se emplean una serie de declaraciones con el método prt para imprimir valores a pantalla; en esta serie de declaraciones se manda imprimir el valor de los "Wrappers" definidos anteriormente a través de métodos auxiliares xxxxValue().

Dichos métodos auxiliares se encuentran disponibles para todo "Wrapper" de primitivo y permiten restringir el rango del mismo, esto en términos computacionales es conocido como "Casting" (Concepto ilustrado a más detalle en otra sección de este curso); mediante esta utilización de "Casting" se puede ilustrar la pérdida de exactitud en primitivos, tal sería el caso al realizar un "Cast" de double a int.

Posteriormente son definidos cuatro "String's" con valores numéricos. Se declara otra serie de "Wrappers" que son inicializados a través de los "String's"

definidos anteriormente. Son impresos a pantalla (a través de prt) los valores de los "Wrappers", en la primer

serie como "Wrappers" directamente y en la segunda serie manipulados hacia "String's" mediante el método auxiliartoString() disponible en todo "Wrapper".

Se inicializan otra serie de "Wrappers" con primitivos 0 (cero) Son impresos a pantalla los valores de estos últimos "Wrappers" como primitivos a

través de los "String's" definidos previamente, dicha impresión de "String's" como primitivos se logra a través de los métodos auxiliares parseXXX.

Finalmente son definidos una serie de métodos prt que toman distintos argumentos de entrada ; el diseño de estos métodos prt es conocido como overloading, concepto que será ilustrado a detalle en una futura sección del curso.

El uso práctico para emplear "Wrappers" y/o primitivos será explorado en una futura sección de este curso.

Campos("Fields") y Métodos.

Cuando se define una Clase esta puede contener dos tipos de elementos: Campos ("Fields") o Métodos, los métodos ya fueron descritos en los ejemplos anteriores, sin embargo , un campo es un objeto o primitivo utilizado para guardar datos en cada instancia de un objeto :

Código Fuente Automoviles.java

public class Automoviles {

int cantidad;

boolean doble_traccion;

public static void main(String args[]) {

Automoviles ford = new Automoviles();

ford.cantidad = 3;

Automoviles vw = new Automoviles();

vw.cantidad = 2;

Automoviles jeep = new Automoviles();

jeep.doble_traccion = true;

System.out.println("Autos Ford: " + ford.cantidad + " Doble Traccion: " + ford.doble_traccion);

System.out.println("Autos Volkswagen : " + vw.cantidad + " Doble Traccion: " + vw.doble_traccion);

System.out.println("Autos Jeep : " + jeep.cantidad + " Doble Traccion: " + jeep.doble_traccion);

}

}

Clase Automoviles

Como en todo programa Java , primeramente se define la Clase a través del vocablo class.

Inmediatamente después son definidos dos campos ("fields") dentro de la clase, en este casoprimitivos; al ser definidos los campos al nivel de Clase (y no dentro de un método como el ejemplo anterior) se garantiza que los campos puedan ser manipulados de cualquier método de la Clase.

Se define el método principal main y dentro de este se generan tres instancias de la Clase; nótese que a través de las referencias de cada instancia son manipulados los valores de los campos("fields").

Posteriormente, se imprimen a pantalla los valores de las diversas instancias con sus respectivos campos("fields"); nótese que aquellas instancias que no asignaron valores a sus campos("fields"), reciben un valor "default" (primitivos)

En Java existen ciertos campos que son definidos como final, y aunque dicho calificativo puede ser empleado en métodos y Clases su uso más común es en campos, su significado puede ser descrito con las siguientes palabras: "No puede cambiar"; las razones por las que es utilizado final pueden ser: Eficiencia o Diseño.

Al utilizarse final en variables (primitivos) estas toman un valor constante al llevarse acabo la compilación, lo anterior permite mayor eficiencia al momento de ejecutarse un programa ("Run-Time"), ahora bien, cuando es utilizado final en referencias de Objetos se restringe que dicha referencia sea utilizada para apuntar hacia otro Objeto, esto permite realizar un diseño que prohibe a la referencia ser re-asignada, el uso del vocablo final será explorado en otra sección de este curso.

En ciertos métodos es utilizado el vocablo return que permite indicar el valor de retorno para un método:

Código Fuente Fecha.java

import java.util.*;

public class Fecha {

public Date hoy() {

Date a = new Date();

return a;

}

public static void main(String[] args) {

Fecha tiempo = new Fecha();

System.out.println(tiempo.hoy());

}

}

Clase Fecha

Se importan las Clases de la librería java.util.*, donde se encuentra la Clase Date empleada para manipular fechas en Java.

Como en todo programa Java , se define la Clase a través del vocablo class. Es definido un método llamado hoy que retorna la fecha actual mediante un

Objeto Date . Se define el método principal main y dentro de este se genera una instancia de la

Clase, mandándose llamar el método hoy.

Significado de return.

El uso del vocablo return no es obligatorio en la gran mayoría de métodos definidos en Java, sin embargo, tiene dos usos principales:

Primeramente es una manera de indicar que el método en cuestión ha terminado. Cuando en determinado método se manipula más de una instancia/primitivo del mismo

tipo que será retornado, se emplea el vocablo return para evitar ambigüedad.

Constructores.

Un Constructor es un método especial en Java empleado para inicializar valores en Instancias de Objetos, a través de este tipo de métodos es posible generar diversos tipos de instancias para la Clase en cuestión; la principal característica de este tipo de métodos es que llevan el mismo nombre de la clase, a continuación se describen varios ejemplos utilizando constructores:

Código Fuente Arboles.java.

public class Arboles {

public Arboles() {

System.out.println("Un árbol genérico");

}

public Arboles(String tipo) {

System.out.println("Un árbol tipo " + tipo);

}

public Arboles(int altura) {

System.out.println("Un árbol de " + altura + " metros");

}

public Arboles(int altura,String tipo) {

System.out.println("Un " + tipo + " de " + altura + " metros");

}

public static void main(String args[]) {

Arboles arbol1 = new Arboles(4);

Arboles arbol2 = new Arboles("Roble");

Arboles arbol3 = new Arboles();

Arboles arbol4 = new Arboles(5,"Pino");

}

}

Clase Arboles

Como en todo programa Java , primeramente se define la Clase a través del vocablo class.

Posteriormente son definidos 4 Constructores; nótese que cada uno recibe el mismo nombre de la Clase y posee distintos argumentos de entrada.

Dentro del método principal (main) son generadas cuatro instancias de la Clase, como se puede observar, al ser generada la instancia a través del vocablo new se pasa un parámetro, y es dependiendo de este parámetro que es llamado el Constructor correspondiente, el cual a su vez invoca la Clase System.out.println que imprime a pantalla.

Constructor Obligatorio...

En los ejemplos anteriores del curso se pudo notar que no se hizo uso de Constructor alguno, y la razón es que el compilador lleva acabo esta definición de Constructor vacío detrás de los escenarios, sin embargo, existe una situación en la que es necesario definir un Constructor vacío y esta es cuando se hace uso de otros constructores.

Lo anterior significa que si el compilador observa un método con el mismo nombre de la clase con argumentos (Constructor), deberá existir un método vacío por el mismo nombre, esto de cierta manera salvaguarda a un programador al momento de definir métodos que no vayan a ser definidos erróneamente como Constructores.

Métodos y Parámetros por Referencia.

Cuando son utilizados Constructores puede surgir la necesidad de inicializar algún campo perteneciente a la Clase, esto trae consigo otro detalle en lo que concierne la asignación de valores dentro de un método, la razón se debe a que dentro de un método puede surgir el requerimiento de accesar campos definidos en la Clase, observe:

public Class Cableado {

int longitud;

String tipo;

public Cableado(String tipo, int longitud) {

this.tipo = tipo

this.longitud = longitud;

}

....

....

}

En el caso anterior, dentro del constructor se desea asignar valores a los campos de una Clase, sin embargo, dentro del constructor son utilizados como parámetros de entrada los mismos valores, para tener acceso a los campos de la Clase se utiliza el vocablo: this, al emplear esta sintaxis se esta haciendo alusión a los campos de la Clase.

Aunque sería posible emplear distintos nombres para los valores de entrada, evitando el uso del vocablothis, la sintaxis con el vocablo this es ampliamente utilizada en el caso de Constructores mencionado anteriormente, así como Java Beans, un concepto utilizado en Java para ambientes Web (JSP's/Servlets).

Generación de Documentación (Vía Comentarios)En muchos lenguajes el mantener documentación acerca de las Clases/Métodos escritos es una tarea que requiere un esfuerzo adicional, ya que por una parte debe mantenerse el código funcional mientras se debe generar documentación acerca de la funciones; en muchas ocasiones esta documentación ni existe, en Java la generación de Documentación es llevada en conjunción del código a través de comentarios.

A través de comentarios colocados en cada Clase/Método es posible generar documentación de una manera inmediata y concisa en formato de documentos HTML / XHTML , a continuación se describe un ejemplo :

Comentarios en Java

Primeramente vale hacer mencionar los tipos de comentarios utilizados en Java, existen dos tipos de comentarios puros como tal :

/* Este es un comentario que expande

diversas lineas, sin embargo, este

no será incluido en la documentación */

// Este es un comentario de linea únicamente

// Tampoco será incluido en la documentación

Los dos tipos de comentarios anteriores son utilizados como guia dentro del código fuente, esto es, a partir de ellos no son generados ningún tipo de documentación, cualquier palabra entre /* y */ es considerada comentario, al igual que todo renglón que inicie con //.

Para que un comentario sea contemplado en la generación de documentación se utiliza la siguiente sintaxis

/** Un comentario para Clase */

public class pruebaDocs {

/** Un comentario para Variables */

public int i;

/** A comentario para método */

public void f() {}

}

Todo elemento colocado entre los elementos /** */ será contemplado en la generación de documentación, dependiendo de la ubicación de este comentario será generada la documentación, esto es, si aparece antes de un elemento class es considerado documentación para la Clase y así sucesivamente.

Existe otra Sintaxis especifica para definir los elementos dentro de los comentarios, la cual se ilustra a continuación:

import java.util.*;

/**

* Programa que Despliega la Fecha.

* @author Juan Sanchez

* @author [email protected]

* @version 1.7

*/

public class PruebaFecha {

/** Punto de Entrada único para Clase

* @param args Arreglo de String's

* @return Sin valor de retorno

* @exception exceptions Ningún error (Excepción) definida

*/

public static void main(String[] args) {

System.out.println("Hoy es: ");

System.out.println(new Date());

}

}

Los elementos que inician con el símbolo @ son utilizados para indicar parámetros específicos dentro de la documentación; los anteriores son algunos elementos que pueden ser utilizados al definir documentación.

En la siguiente sección serán ilustrados otros elementos @, así como el proceso para generar la documentación HTML / XHTML del código fuente Java.

Otros parámetros (@) para Comentarios en Java

Los siguientes parámetros también son utilizados al generar documentación para código Java:

Elemento Uso

@see <Nombre de la Clase>

Elemento utilizado para hacer referencia a otras Clases, genera un link HTML / XHTML hacia la Clase en cuestión.

@throws <Nombre de la

Clase>

Empleado para indicar la Clase utilizada al generarse un Error. La definición de Clases para Errores será descrita en otra sección.

@deprecated

Este parámetro es utilizado para indicar que una Clase ha sido repuesta por otra más reciente; al ser indicado este parámetro en una Clase/Método el compilador (javac) generará un aviso, indicando que la Clase/Método ha sido repuesto.

Además de los parámetros anteriores también es posible escribir lenguaje HTML / XHTML entre los elementos /** */ , lo anterior permite generar listas enumeradas, dar mayor estilo a la documentación y cualquier otro formato disponible en HTML / XHTML .

A continuación se ilustran dos Clases escritas anteriormente, con los respectivos comentarios para generar documentación:

Clase Pantalla.java

package com.osmosislatina.escritura;

/**

* Programa con métodos auxiliares para salto de linea

* y linea continua

* @author Juan Sanchez

* @author [email protected]

* @version 1.3

*/

public class Pantalla {

/** Método para linea continua

* @params Un String

* @return Sin valor de retorno

* @exception exceptions Ningún error (Excepción) definida

*/

public static void sinSalto(String s) {

System.out.print(s);

}

/** Método para salto de linea

* @params Un String

* @return Sin valor de retorno

* @exception exceptions Ningún error (Excepción) definida

*/

public static void conSalto(String s) {

System.out.println(s);

}

}

Clase MandaPantalla.java

package com.osmosislatina.auxiliares;

import com.osmosislatina.escritura.*;

/**

* Programa para demostración de import en la Clase

* @author Juan Sanchez

* @author [email protected]

* @version 1.6

* @see com.osmosislatina.escritura.Pantalla

*/

public class MandaPantalla {

/** Punto de Entrada único para Clase

* <ul>

* <li>Utiliza método sinsalto

* <li>Utiliza método consalto

* </ul>

* @param args Arreglo de String's

* @return Sin valor de retorno

* @exception exceptions Ningun error (Excepción) definida

*/

public static void main(String args[]) {

Pantalla primera = new Pantalla();

primera.conSalto("Esto es un renglón CON salto de linea");

primera.conSalto("Esta linea también tiene salto");

primera.sinSalto("Linea Continua");

primera.sinSalto("Linea Continua");

primera.conSalto("Esta linea si tiene salto!");

primera.sinSalto("Termina sin salto");

System.out.println(" Se termina el uso de funciones");

}

}

Generación de Documentación

El comando javadoc posee una gran cantidad de parámetros para generar documentación, a continuación se describe su uso más básico:

$ mkdir docs

$ javadoc -d docs/ -version -author -use *.java

com/osmosislatina/escritura/*.java com/osmosislatina/auxiliares/*.java

El primer paso antes de generar cualquier documentación Java es crear un directorio donde residirá la documentación, en este caso se ha generado un directorio llamado docs.

Posteriormente es invocado el comando javadoc con los siguiente parámetros:

-d docs: Indica el directorio donde será depositada la documentación (docs).

-version: Implica que al ser generada la documentación sea desplegado el numero de

versión, de ser definido en la Clase.

-author : En caso de ser definido el parámetro autor, este aparecerá en la

documentación.

-use: Es el parámetro utilizado para indicar los directorios donde residen las Clases

sobre las que será generada la documentación, en este caso se esta indicando el directorio presente (*.java) y los

directorios com/osmosislatina/escritura y com/osmosislatina/auxiliares.

Al terminar este proceso será generada la documentación de las distintas Clases dentro del directorio docs; para observar otros parámetros que puede tomar el comando javadoc basta ejecutar : javadoc -help.

Herencias ("Inheritance")Uno de las partes medulares en un lenguaje orientado a Objetos es el uso de Herencias ("Inheritance") , el cual permite reutilizar Clases existentes con todas las funcionalidades/comportamientos ya diseñadas en ellas; en Herencias ("Inheritance") el termino reutilizable toma otro sentido al utilizado en la secciones anteriores al importarse Clases (import), uno más complejo y poderoso, observe:

El primer diagrama UML ("Universal Markup Language") muestra 3 Clases(Guitarra,Piano,Saxofón) que heredan ("Inherit") de una Clase Base (Instrumento),mientras el segundo diagrama muestra 2 Clases (Circulo,Triangulo) que heredan ("Inherit") de una Clase Base (Figura); este mecanismo permite que nuevas Clases hereden el comportamiento de una Clase Base ya depurada y funcional, en el proceso teniendo acceso a los métodos/campos ya creados con anterioridad ; a continuación se describe un ejemplo que hace uso de Herencias ("Inheritance"):

Código Fuente MiniComponente.java

class Radio {

private String r = new String("Radio: ");

public void agrega(String a) { r += a; }

public void imprimir() { System.out.println(r); }

public void frecuencia() { agrega(" frecuencia() "); }

public void volumen() { agrega(" volumen() "); }

public void modelo() { agrega(" modelo() "); }

public static void main(String[] args) {

Radio x = new Radio();

x.frecuencia() ; x.volumen(); x.modelo();

x.imprimir();

}

}

public class MiniComponente extends Radio {

// Cambiar el metodo ("Override")

public void volumen() {

agrega( " MiniComponente.volumen() ");

super.volumen(); // Llamar la version de la Clase Base

}

// Agregar metodo

public void equalizador() { agrega(" equalizador() "); }

public static void main(String[] args) {

MiniComponente x = new MiniComponente();

x.frecuencia();

x.volumen();

x.modelo();

x.equalizador();

x.imprimir();

System.out.println("Probar la Clase Base: ");

Radio.main(args);

}

}

Definición de Dos Clases y Herencias ("Inheritance").

Como se puede observar, han sido definidas dos Clases en el mismo archivo fuente llamadoMiniComponente.java, el hecho que existan dos Clases en el mismo archivo fuente no influye en ningún factor, en este caso se opto por dicho diseño debido a razones didácticas, aunque bien se pudo haber realizado el diseño de este manera por razones administrativas, de cualquier manera, nótese que se sigue cumpliendo la característica de una Clase Java: debe ser definida al menos una Clase con el mismo nombre del archivo fuente.

Entrando al problema espacial ("real"), es posible afirmar que todo MiniComponente incluye un Radio (AM/FM) entre sus dispositivos , debido a esto se ha decidido reutilizar(Heredar) el comportamiento de una Clase Base de Radio, a continuación se describe la Clase Radio.

Clase Radio.

Como cualquier Clase Java se utiliza el vocablo class, sin embargo, no se utiliza ningún calificador de acceso, por lo que esta Clase solo será accesible internamente (O al paquete "package" si fuera incluida en alguno );esto es lo que se refiere como acceso amigable, descrito en la sección de Calificadores.

Se define un campo tipo String con la referencia r, el cual es asignado un valor inicial de "Radio: ".

El primer método llamado agrega, toma como valor de entrada un String y su función es concatenar diversos String's, nótese que se utiliza el operador +=; a través de este operador es posible concatenar diversos elementos, en esta definición se esta declarando que el String de entrada (a) debe ser concatenado con el String r el cual es el campo definido en la Clase.

Los métodos frecuencia, volumen y modelo hacen uso del método agrega concatenando distintos valores.

Finalmente el método imprimir hace uso de la Clase System.out.println para enviar a pantalla el valor del String r (el campo de la Clase).

En el método principal ( main ) se genera una instancia de la Clase Radio, posteriormente a través de la respectiva referencia se mandan llamar los distintos métodos de la Clase.

Una vez compilado el código fuente, es posible ejecutar la Clase Radio a través del comando java Radio y observar los resultados iniciales.

Clase MiniComponente.

Al definir esta Clase se utiliza el vocablo extends que implica Herencia ("Inheritance"), seguido de éste se indica la Clase que será heredada ("Inherited"), en este caso Radio.

El primer método definido se llama volumen, nótese que en la Clase Radio también existe un método con este nombre, dentro de esta Clase se esta modificando el comportamiento de dicho método, esto es conocido como override en lenguajes orientados a Objetos.

Dentro de este mismo método, inicialmente se hace una llamada al método agrega, si observa el resto de la composición de esta Clase notará que no existe ninguno por este nombre, sin embargo, dentro de la Clase Base existe dicho método, en este caso se esta haciendo uso de los métodos de la Clase Base.

La segunda llamada en este método es: super.volumen, el vocablo super indica una llamada hacia la Clase Base (Radio), a través de esta linea se esta realizando una llamada hacia el método volumendefinido en Radio; el vocablo super es utilizado en los casos donde surge una ambigüedad de nombres entre la Clase heredada y base.

Se define un método nuevo llamado equalizador, que también hace uso del método agrega.

Dentro del método principal main se genera una instancia de la Clase MiniComponente, seguido de diversas llamadas a distintos métodos, nótese que aquellos no definidos dentro de la Clase, son llamados con la implementación de la Clase Base.

En el último paso del método main se realiza una llamada hacia el otro método main definido en la ClaseRadio.

Una vez compilado el código fuente, es posible ejecutar la Clase MiniComponente a través del comando java MiniComponente y observar los resultados iniciales así como las variaciones de comportamiento en la ClaseRadio.

El uso de Herencias es ampliamente utilizado en diseños Java, inclusive las Clases Base del JDK lo utilizan extensamente, si observa la documentación de estas Clases, notará en la parte superior el árbol de Herencias ("Inheritance") para la Clase correspondiente, donde todas son descendientes de la Clase Basejava.lang.Object.

Condicionales if/else.En Java al igual que otros lenguajes son utilizados condicionales que permiten controlar el flujo de ejecución en un programa, los tres principales tipos son el conocido if/else,while y for; a continuación se describen varios ejemplos con el uso de if/else:

Código Fuente Condicional.java

public class Condicional {

static int analisis(int prueba, int objetivo) {

int resultado = 0;

if(prueba > objetivo)

resultado = +1;

else if(prueba < objetivo)

resultado = -1;

else

resultado = 0; // Prueba igual a Objetivo

return resultado;

}

public static void main(String[] args) {

System.out.println(analisis(7, 3));

System.out.println(analisis(3, 7));

System.out.println(analisis(7, 7));

}

}

Clase Condicional.java

Primeramente se declara la Clase através del vocablo class. Es definido un método llamado analisis el cual retorna una respuesta en base a las

operaciones del condicional if/else; una característica de este método es que utiliza el calificativo static, esto se debe a que es llamado directamente del método principal main el cual también es static; debido a que el vocablo static es empleado en métodos que no requieren instancias, si se realiza un llamado hacia determinado

método de uno static éste también deberá ser static, esto garantiza un posible error al invocar métodos estaticos.

Dentro del método principal (main) es invocado el método analisis en tres ocasiones, imprimiendo los respectivos resultados a pantalla.

Uso de llaves ({ }) en Ciclos.

Una característica del ciclo if/else así como los otros ciclos disponibles en Java es el uso de llaves { }, en el ejemplo anterior pudo notar que no fueron utilizadas llaves { } para delimitar las condicionales, al no emplearse este mecanismo, se asume que el ciclo o condicional tendrá efecto únicamente en la linea contigua a su inicio.Aunque es recomendable utilizar llaves ({ } ) para incrementar la legibilidad del código, es importante señalar que esta sintaxis de no utilizar llaves ({ } ) es valida y muy común (Lamentablemente) .

Existe otra variación de if/else que agrupa las condicionales en una sola expresión :

if (expresion Boolean ? operación verdadera : operación falsa)

A continuación se describe un ejemplo empleando esta variación de if/else.

Codigo Fuente Tercia.java

public class Tercia {

public static int trio(int r) {

return r < 10 ? r*3 : r*22;

}

public static void main(String args[]) {

System.out.println(trio(2));

System.out.println(trio(20));

}

}

Clase Tercia

Es definido un método llamado trio el cual hace uso de la variante if/else; la expresión boolean es r < 10, si esta expresión resulta verdadera es ejecutada la operación r*3, caso contrario r*22.

Dentro del método principal main es invocado el método trio en dos ocasiones, enviando a pantalla los respectivos resultados.

Uso de los Ciclos for y whileEl uso de Ciclos en lenguajes de programación es un concepto ampliamente utilizado, a continuación se describe el uso del ciclo for y while :

Sintaxis del Ciclo for.

La Sintaxis empleada por el ciclo for es la siguiente:

for (valores de entrada ; condición de terminación ; iteración por ciclo)

Las tres partes del ciclo se encuentran separadas por ; (punto y coma) La primer parte del ciclo especifica valores previo a su inicio. La segunda parte indica la condición de terminación para el ciclo, la cual esta

directamente relacionada con los valores iniciales. Finalmente, la última parte especifica como serán manipulados los valores iniciales en

cada iteración del ciclo.

Cada parte del ciclo debe incluir al menos un elemento, a la cual es posible agregar otros elementos a partir de una , (coma).

Código Fuente PruebaFor.java

public class PruebaFor {

public static void main(String[] args) {

for(int i = 1, j = i + 10; i < 5; i++, j = i * 2) {

System.out.println("i= " + i + " j= " + j);

}

}

}

Clase PruebaFor.

Se inicia este programa con la clásica definición de class. Se define el método principal ( main ), el uso de corchetes ([]) al lado de la Clase String

indica unArreglo de String's. Se define el ciclo for con las siguientes características:

Dos variables iniciales (primitivos) i y j. La condición para terminar el ciclo es definida cuando el valor de la variable i sea

menor 5. Cada iteración de ciclo el valor de la variable i debe ser incrementado en uno (++) y

el valor de j debe ser igualado a dos veces el valor de i. Dentro del Ciclo se utiliza la Clase System.out.println para imprimir los valores de las

variables i y j en cada iteración de ciclo.

Sintaxis del Ciclo while.

La Sintaxis empleada por el ciclo while es la siguiente:

while (condición de terminación)

Consta de un solo elemento que indica la condición de terminación para un Ciclo.

Código Fuente PruebaWhile.java

public class PruebaWhile {

public static void main(String[] args) {

double r = 0;

while(r < 0.99d) {

r = Math.random();

System.out.println(r);

}

}

}

Clase PruebaWhile.

Se inicia este programa definiendo la Clase en cuestión. Se define el método principal ( main ), el uso de corchetes ([]) al lado de la Clase String

indica unArreglo de String's. Se define un primitivo del tipo double con valor inicial de 0 (cero). El ciclo while utiliza la condición de terminación hasta que el valor de la variable d sea

asignado un valor mayor a 0.99; la d al final del numero indica un número (primitivo) del tipo double .

Dentro del ciclo, el valor de la variable es modificado con un numero aleatorio a través de la funciónMath.random (proporcionada en las Clases Base del JDK), que toma como valor de entrada un número (primitivo).

Es impreso a pantalla (vía System.out.println) el valor de la variable r.

En ciertas ocasiones surge la necesidad de interrumpir un ciclo si ocurre determinada condición, o bien, saltar una iteración en ciertas circunstancias, este comportamiento se logra a través de los vocablosbreak y continue, a continuación se describe su uso:

Código Fuente BreakYContinue.java

public class BreakYContinue {

public static void main(String[] args) {

for(int i = 0; i < 100; i++) {

if(i == 74) break; // Fuera de Ciclo

if(i % 9 != 0) continue; // Siguiente Iteración

System.out.println(i);

}

int i = 0;

// Un ciclo "infinito":

while(true) {

i++;

int j = i * 27;

if(j == 1269) break; // Fuera del Ciclo

if(i % 10 != 0) continue; // Regreso al Inicio del Ciclo

System.out.println(i);

}

}

}

Clase BreakYContinue

La Clase únicamente contiene código en su método principal (main) descrito a continuación :

Se inicia un ciclo for con cien iteraciones empleando la variable i, cada iteración es impreso el valor la variable a pantalla, salvo los siguientes casos: Si el valor de la variable i equivale a 74 se termina todo el ciclo debido al

vocablo break. Si la variable i no es múltiplo de 9 se interrumpe la iteración actual debido al

vocablo continue. Antes de iniciarse un ciclo while se reinicializa la variable i a cero. Dentro del ciclo se imprimen los valores de la variable i en cada iteración, excepto en

los siguientes casos: Si el valor de la variable j equivale a 1269 se termina todo el ciclo debido al

vocablo break. Si la variable i no es múltiplo de 10 se interrumpe la iteración actual debido al

vocablo continue.

Otra variación para interrumpir y saltar iteraciones en ciclos es a través de Etiquetas (Labels), este mecanismo logra un comportamiento similar al famoso, pero criticado "goto". Aunque su uso es permitido, es fuertemente desalentado debido a la poca legibilidad que resulta en el código.

Código Fuente LabelFor.java

public class LabelFor {

public static void main(String[] args) {

int i = 0;

externo: // Definicón de Label

for(; true ;) { // Ciclo Infinito

interno: // Definción de Label

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

prt("i = " + i);

if(i == 2) {

prt("continue");

continue;

}

if(i == 3) {

prt("break");

i++; // Incrementar antes del "Break"

break;

}

if(i == 7) {

prt("continue externo");

i++; // Incrementar antes del "Label"

//

continue externo;

}

if(i == 8) {

prt("break externo");

break externo;

}

for(int k = 0; k < 5; k++) {

if(k == 3) {

prt("continue interno");

continue interno;

}

}

}

}

}

static void prt(String s) {

System.out.println(s);

}

}

Resultados de Ejecutar Clase LabelFor

Los resultados de esta Clase son los siguientes:

$ java LabelFor

i = 0

continue interno

i = 1

continue interno

i = 2

continue

i = 3

break

i = 4

continue interno

i = 5

continue interno

i = 6

continue interno

i = 7

continue externo

i = 8

break externo

Codigo Fuente LabelWhile.java

public class LabelWhile {

public static void main(String[] args) {

int i = 0;

externo:

while(true) {

prt("Externo Ciclo while");

while(true) {

i++;

prt("i = " + i);

if(i == 1) {

prt("continue");

continue;

}

if(i == 3) {

prt("continue externo");

continue externo;

}

if(i == 5) {

prt("break");

break;

}

if(i == 7) {

prt("break externo");

break externo;

}

}

}

}

static void prt(String s) {

System.out.println(s);

}

}

Resultados de Ejecutar Clase LabelWhile

Los resultados de esta Clase son los siguientes:

$ java LabelWhile

Externo Ciclo while

i = 1

continue

i = 2

i = 3

continue externo

Externo Ciclo while

i = 4

i = 5

break

Externo Ciclo while

i = 6

i = 7

break externo

Uso de switch.A través de un Switch se posible definir una serie de tareas cada una asignada a una lista de valores, este tipo de estructura es ideal cuando se requieren condicionales que lleven más de dos valores, en lugar de anidar grupos de condicionales if/else es posible lograr el mismo comportamiento a través de un switch:

Sintaxis del switch.

La Sintaxis empleada por un switch es la siguiente:

switch (variable) {

case <posible valor> : Instrucciones : break;

case <posible valor> : Instrucciones : break;

case <posible valor> : Instrucciones : break;

case <posible valor> : Instrucciones : break;

case <posible valor> : Instrucciones : break;

default : Instrucciones ;

Dada una variable de entrada esta se define seguido del vocablo switch. Se abre una llave para iniciar los posibles valores que pueda tomar dicha variable. Los juegos de valores son iniciados con case seguido del posible valor de la variable,

posteriormente es definido un juego de instrucciones que serán ejecutados en caso de corresponder con el valor de la variable y finalmente (opcional) se utiliza vocablo break para salir de ciclo case.

Un valor opcional es la definición de la linea default, cuyas instrucciones serán ejecutadas en caso que la variable del switch no coincida con los valores definidos.

Código Fuente Meses.java

public class Meses {

public static void main(String[] args) {

int month = 8;

switch (month) {

case 1: System.out.println("Enero"); break;

case 2: System.out.println("Febrero"); break;

case 3: System.out.println("Marzo"); break;

case 4: System.out.println("Abril"); break;

case 5: System.out.println("Mayo"); break;

case 6: System.out.println("Junio"); break;

case 7: System.out.println("Julio"); break;

case 8: System.out.println("Agosto"); break;

case 9: System.out.println("Septiembre"); break;

case 10: System.out.println("Octubre"); break;

case 11: System.out.println("Noviembre"); break;

case 12: System.out.println("Diciembre"); break;

}

}

}

Clase Meses

Se define un primitivo con valor de 8, el cual es utilizado como variable para un switch. Dentro del switch son definidos valores del 1 al 12, los cuales tienen como instrucción

imprimir a pantalla el valor del mes correspondiente.

Cuando se describió la Sintaxis de un switch se mencionó que el uso de break al final de cada declaración (case) era opcional, esto se debe a la posibilidad que exista más de un valor que deba ejecutar las mismas instrucciones, a continuación se describe este caso:

Código Fuente Meses2.java

public class Meses2 {

public static void main(String[] args) {

int mes = 10;

int año = 2002;

int numDias = 0;

switch (mes) {

case 1:

case 3:

case 5:

case 7:

case 8:

case 10:

case 12:

numDias = 31;

break;

case 4:

case 6:

case 9:

case 11:

numDias = 30;

break;

case 2:

if ( ((año % 4 == 0) && !(año % 100 == 0))

|| (año % 400 == 0) )

numDias = 29;

else

numDias = 28;

break;

}

System.out.println("Numero de Dias = " + numDias);

}

}

Clase Meses2

En esta Clase el uso de switch varía, ya que son agrupados distintos valores, al omitir el uso del vocablobreak; el uso de break en un switch es critico ya que al no ser definido puede ocasionar un comportamiento errático, ya que al no indicarse se continúan evaluando los distintos valores restantes en un switch, posiblemente causando un comportamiento no deseado.

Finalmente, vale mencionar que las datos que se puede evaluar a través de un switch son solo primitivos, esto es, no se puede definir un juego de String's y comparar sus valores, para este tipo de situaciones (String u otro Objeto) se requiere usar condicionales if/else anidadas.

Operadores Matemáticos, Relacionales y Lógicos.

En Java al igual que otros lenguajes existen diversos operadores que son empleados en diferentes secciones de un programa, a lo largo de los distintos programas escritos en este Curso se han empleado su gran mayoría, sin embargo, en esta sección se describirán detalles más precisos acerca de su funcionamiento.

Antes de entrar en el uso de Operadores, a continuación se describe un ejemplo que demuestra el comportamiento de Asignación de valores para el lenguaje Java:

Código Fuente Asignacion.java

class Numero {

int i;

}

public class Asignacion {

public static void main(String[] args) {

Numero n1 = new Numero();

Numero n2 = new Numero();

n1.i = 15;

n2.i = 45;

System.out.println("a) n1.i: " + n1.i +

", n2.i: " + n2.i);

n1 = n2;

System.out.println("b) n1.i: " + n1.i +

", n2.i: " + n2.i);

n1.i = 27;

System.out.println("c) n1.i: " + n1.i +

", n2.i: " + n2.i);

}

}

Clase Asignacion

En el código fuente son definidas dos Clases, una de ellas (Numero) simplemente contiene un campo, mientras la segunda (Asignacion) incluye la lógica central del programa.

Dentro del método principal (main) de la Clase principal (Asignacion) es definida la siguiente secuencia de eventos:

Primeramente se generan dos instancias de la Clase Numero a las cuales son asignadas

las referenciasn1 y n2.

A través de dichas referencias son asignados los valores correspondientes al campo i de

la Clase.

Se imprime a pantalla el valor de cada campo de las dos instancias.

Se asigna el valor de n2 a la instancia de n1.

Se imprime a pantalla el valor de cada campo de las dos instancias.

Se modifica el valor del campo en la instancia n1 al valor de 27.

Se imprime a pantalla el valor de cada campo de las dos instancias.

Aunque el comportamiento del programa anterior parece predecible, para muchas personas no familiarizadas con lenguajes orientados a Objetos puede resultar una sorpresa, la ejecución del programa anterior da como resultado:

a) n1.i: 15, n2.i: 45

b) n1.i: 45, n2.i: 45

c) n1.i: 27, n2.i: 27

Los primeros resultados no presentan sorpresa alguna, puesto que simplemente se imprimen los valores recién asignados; la segunda impresión puede resultar confusa y esto se debe a la asignación n1 = n2.

Al utilizar la sintaxis n1 = n2, se esta indicando que la referencia n1 apunte hacia aquella de n2; al ser llevada acabo esta asignación el valor n1.i cambia efectivamente hacia el mismo de n2.i. El comportamiento anterior se debe a que fueron utilizadas referencias en la asignación de valores, lainstancia de n1 fue efectivamente perdida, a partir de ese punto ambas referencias apuntan hacia lainstancia del Objeto n2; este mecanismo en Java es

denominado aliasing y suele ser un poco difícil de entender para aquellos que inician en este lenguaje.

La tercer impresión es llevada acabo después de la asignación n1.i = 27, al llevar acabo este proceso el valor n1.i será modificado, así como el valor de n2.i, puesto que ambas referencias apuntan hacia la misma instancia.

Para evitar aliasing y mantener las dos instancias de Objetos se utilizaría una asignación más explicita :n1.i = n2.i; en la siguiente sección se observará un comportamiento de equivalencia que también puede resultar inusual para un programador no familiarizado con el lenguaje Java.

Operadores Relacionales.

A través de operadores relacionales es posible llevar acabo un proceso de comparación entre dos elementos en un programa, siendo su característica principal el generar un resultado verdadero o falso; dichos operadores relacionales se muestran a continuación :

Operador Significado

< Menor que

> Mayor que

<= Menor que o Igual

>= Mayor que o Igual

== Igual

!= No Igual

Muchos de estos Operadores han sido utilizados a lo largo de este curso, a continuación se demuestra un uso muy común de estos operadores:

Código Fuente Equivalencia.java

public class Equivalencia {

public static void main(String[] args) {

Integer n1 = new Integer(75);

Integer n2 = new Integer(75);

System.out.println(n1 == n2);

System.out.println(n1 != n2);

}

}

Clase Equivalencia

La Clase anterior genera dos instancias de la Clase Integer, las cuales son inicializadas con un valor de 75.

Es llevada acabo una comparación entre ambas referencias.

Aunque el comportamiento del programa anterior parece predecible, los resultados de este programa son falso (false) y verdadero (true), este resultado se debe a que la comparación llevada acabo por el programa es en base a referencias, para llevar acabo el comportamiento deseado se utiliza la siguiente variación:

public class Equivalencia2 {

public static void main(String[] args) {

Integer n1 = new Integer(75);

Integer n2 = new Integer(75);

System.out.println(n1.equals(n2));

}

}

En el ejemplo anterior se hace uso del método equals, a través de este método disponible en toda Clase es llevado acabo una comparación entre el valor del Objeto en sí ; cabe mencionar que los operadores relacionales "==" y "!=" funcionan acordemente ( o como aparentemente se pensaría ) con primitivos, es solo cuando se utilizan Clases cuando es necesario utilizar el método "equals" para llevar acabo una comparación al nivel de Objetos.

Operadores Matemáticos.

En Java se encuentran disponibles los mismos operadores matemáticos que en otros lenguajes, mencionados a continuación :

Operador Significado

+ Suma

- Resta

= Igual

* Multiplicación

/ División

% Modulo (El residuo de una División)

Java también ofrece una sintaxis para llevar acabo una operación matemática y asignación en una sola simbología, esta es llevada acabo mediante: "+=","-=","*=","/="; además de esta sintaxis también existen los operadores de auto-incremento y auto-decremento que son representados por "++" y "--" respectivamente.

En los siguientes ejemplos se describe el uso de los diversos operadores matemáticos:

Código Fuente OperadoresMat.java

import java.util.*;

public class OperadoresMat {

// Metodo para Imprimir Mensaje a Pantalla

static void prt(String s) {

System.out.println(s);

}

// Metodo para Imprimir un String y Entero ("Overload")

static void prt(String s, int i) {

prt(s + " = " + i);

}

// Metodo para Imprimir un String y Float ("Overload")

static void prt(String s, float f) {

prt(s + " = " + f);

}

public static void main(String[] args) {

// Generar un numero Aleatorio

Random rand = new Random();

int i, j, k;

// '%' Limita a un valor maximo de 99

j = rand.nextInt() % 100;

k = rand.nextInt() % 100;

// Imprimir Valores de "j" y "k"

prt("j",j); prt("k",k);

i = j + k; prt("j + k", i);

i = j - k; prt("j - k", i);

i = k / j; prt("k / j", i);

i = k * j; prt("k * j", i);

i = k % j; prt("k % j", i);

j %= k; prt("j %= k", j);

// Prueba para numero Float

float u,v,w;

v = rand.nextFloat();

w = rand.nextFloat();

prt("v", v); prt("w", w);

u = v + w; prt("v + w", u);

u = v - w; prt("v - w", u);

u = v * w; prt("v * w", u);

u = v / w; prt("v / w", u);

// Operadores Adicionales

u += v; prt("u += v", u);

u -= v; prt("u -= v", u);

u *= v; prt("u *= v", u);

u /= v; prt("u /= v", u);

}

}

Clase OperadoresMat

A diferencia de otras Clases diseñadas en este Curso, esta Clase contiene tres métodos con el mismo nombre cada uno con distintos valores de entrada, este comportamiento en Java es conocido comooverloading; al ser invocado dicho método ocurrirá el comportamiento apropiado en base a los parámetros de entrada.

"Overloading" no es lo mismo que "Overriding"

El concepto descrito en esta sección es llamado overloading que como su nombre lo implica, es una sobrecarga del nombre de un método, esto es, pueden existir diversos métodos con el mismo nombre pero distintos parámetros de entrada.

Lo anterior es muy distinto al concepto de overriding descrito en Herencias ("Inheritance"), en donde se puede modificar el comportamiento de un método con los mismos parámetros de entradaen Clases que hereden su comportamiento.

La ejecución de este programa genera números aleatorios a través de la Clase Random, posteriormente se realizan diversas operaciones con primitivos empleando los distintos operadores matemáticos; un posible resultado (debido al uso de números aleatorios) es el siguiente:

$ java OperadoresMat

j = -19

k = 14

j + k = -5

j - k = -33

k / j = 0

k * j = -266

k % j = 14

j %= k = -5

v = 0.7287964

w = 0.7703622

v + w = 1.4991586

v - w = -0.041565776

v * w = 0.5614372

v / w = 0.94604385

u += v = 1.6748402

u -= v = 0.9460438

u *= v = 0.68947333

u /= v = 0.9460438

Código Fuente AutoInc.java

public class AutoInc {

public static void main(String[] args) {

int i = 1;

prt("i : " + i);

prt("++i : " + ++i); // Pre-Incremento

prt("i++ : " + i++); // Post-Incremento

prt("i : " + i);

prt("--i : " + --i); // Pre-Decremento

prt("i-- : " + i--); // Post-Decremento

prt("i : " + i);

}

// Metodo para Impresion

static void prt(String s) {

System.out.println(s);

}

}

Clase AutoInc

La clase hace uso de los distintos operadores para Auto-Incremento y Auto-Decremento de variables; el resultado de dicho programa se muestra a continuación:

$ java AutoInc

i : 1

++i : 2

i++ : 2

i : 3

--i : 2

i-- : 2

i : 1

Además de estos operadores matemáticos, el lenguaje también posee una Clase estándar (en el JDK) para realizar operaciones más complejas, esta Clase bajo el nombre de java.lang.Math ofrece operaciones trigonométricas, exponenciales y otras más; a

continuación se describen algunos ejemplos empleando dicha clase :

Código Fuente Calificaciones.java

public class Calificaciones {

public static void main(String[] args) {

int matematicas = 10;

int gramatica = -2;

double historia = 9.55;

double biologia = 7.3;

// Valores Absolutos

prt("|" + matematicas + "| es " + Math.abs(matematicas));

prt("|" + gramatica + "| es " + Math.abs(gramatica));

prt("|" + historia + "| es " + Math.abs(historia));

prt("|" + biologia + "| es " + Math.abs(biologia));

// Redondeo de cifras al siguiente numero entero

prt(historia + " redondeado es " + Math.round(historia));

prt(biologia + " redondeado es " + Math.round(biologia));

// El techo ("ceiling") de un numero es el minimo entero

// mayor o igual al mismo numero

// Un numero entero es su propio techo ("ceiling")

prt("El techo de " + matematicas + " es " + Math.ceil(matematicas));

prt("El techo de " + gramatica + " es " + Math.ceil(gramatica));

prt("El techo de " + historia + " es " + Math.ceil(historia));

prt("El techo de " + biologia + " es " + Math.ceil(biologia));

// El piso ("floor") de un numero es el entero mayor

// mayor o igual al mismo numero .

// Un numero entero es su propio piso ("floor")

prt("El piso de " + matematicas + " es " + Math.floor(matematicas));

prt("El piso de " + gramatica + " es " + Math.floor(gramatica));

prt("El piso de " + historia + " es " + Math.floor(historia));

prt("El piso de " + biologia + " es " + Math.floor(biologia));

// min() retorna el menor de los argumentos proporcionados

prt("min(" + matematicas + "," + gramatica + ") es " + Math.min(matematicas,gramatica));

prt("min(" + historia + "," + biologia + ") es " + Math.min(historia,biologia));

prt("min(" + matematicas + "," + historia + ") es " + Math.min(matematicas,historia));

prt("min(" + biologia + "," + gramatica + ") es " + Math.min(biologia,gramatica));

// max() retorna el mayor de los argumentos proporcionados

prt("max(" + matematicas + "," + gramatica + ") es " + Math.max(matematicas,gramatica));

prt("max(" + historia + "," + biologia + ") es " + Math.max(historia,biologia));

prt("max(" + matematicas + "," + historia + ") es " + Math.max(matematicas,historia));

prt("max(" + biologia + "," + gramatica + ") es " + Math.max(biologia,gramatica));

}

// Metodo para Impresion

static void prt(String s) {

System.out.println(s);

}

}

Clase Calificaciones

La clase hace uso de distintos métodos para realizar operaciones de redondeo y comparación sobre valores primitivos.

Codigo Fuente Trilog.java

public class Trilog {

public static void main(String[] args) {

// Convertir un angulo de 45 grados a radianes

// Math.PI equivale a la constante PI (3.14159)

double angulo = 45.0 * 2.0 * Math.PI/360.0;

prt("cos(" + angulo + ") es " + Math.cos(angulo));

prt("sin(" + angulo + ") es " + Math.sin(angulo));

// Metodos Trigonometricos Inversos

// Valores en Radianes

// Debido a que los metodos asin y atan emplean radianes

// utilizar el metodo toDegrees para convertir a grados

double radianes = 0.707;

int i = 0;

prt("acos(" + radianes + ") es " + Math.toDegrees(Math.acos(radianes)));

prt("asin(" + radianes + ") es " + Math.toDegrees(Math.asin(radianes)));

prt("atan(" + radianes + ") es " + Math.toDegrees(Math.atan(radianes)));

// exp(x) equivale a "e (2.71828...)" elevado a la "x" potencia

prt("exp(1.0) es " + Math.exp(1.0));

prt("exp(10.0) es " + Math.exp(10.0));

prt("exp(0.0) es " + Math.exp(0.0));

// log(y) equivale al logaritmo natural (base e) de "y"

// Math.E equivale a la constante e (2.7182)

prt("log(1.0) is " + Math.log(1.0));

prt("log(10.0) is " + Math.log(10.0));

prt("log(Math.E) is " + Math.log(Math.E));

// pow(x, y) equivale a "x" elevado a la "y" potencia

prt("pow(2.0, 2.0) is " + Math.pow(2.0,2.0));

prt("pow(10.0, 3.5) is " + Math.pow(10.0,3.5));

prt("pow(8, -1) is " + Math.pow(8,-1));

// sqrt(x) equivale a la raiz cuadrada de "x"

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

prt(

"La raiz cuadrada de " + i + " is " + Math.sqrt(i));

}

}

// Metodo para Impresion

static void prt(String s) {

System.out.println(s);

}

}

Clase Trilog

La clase hace uso de distintos métodos para realizar operaciones trigonométricas, logarítmicas y con exponentes sobre valores primitivos.

Operadores Lógicos.

Los operadores lógicos para el lenguaje Java son los siguientes:

Operador Significado

|| O (OR)

&& Y (AND)

! Negación (NOT)

A continuación se describe un ejemplo que hace uso de estos operadores :

Código Fuente OperaLogico.java

public class OperaLogico {

static boolean prueba1(int val) {

System.out.println("prueba1(" + val + ")");

System.out.println("resultado: " + (val < 1));

return val < 1;

}

static boolean prueba2(int val) {

System.out.println("preuba2(" + val + ")");

System.out.println("resultado: " + (val < 2));

return val < 2;

}

static boolean prueba3(int val) {

System.out.println("prueba3(" + val + ")");

System.out.println("resultado: " + (val < 3));

return val < 3;

}

public static void main(String[] args) {

// Primer prueba "Corto-Circuito"

if(prueba1(0) && prueba2(2) && prueba3(2))

System.out.println("Primera Expresion es Verdadera");

else

System.out.println("Primera Expresion es Falsa");

// Segunda Prueba Condicional

if(prueba1(4) || prueba2(1))

System.out.println("Segunda Expresion es Verdadera");

else

System.out.println("Segunda Expresion es Falsa");

// Tercer Prueba Negacion

if(65 != 65)

System.out.println("Tercer Expresion es Falsa");

else

System.out.println("Tercer Expresion es Verdadera");

}

}

Clase OperaLogico

La clase hace uso de los distintos operadores lógicos en Java ; dentro de dicha Clase son definidos tres métodos los cuales retornan un valor boolean en base al numero int que sea empleado como parámetro.

Dentro del método principal ( main ) son generadas las siguientes pruebas: La primer declaración utiliza tres elementos para llevar acabo una comparación , este

ejemplo demuestra una cualidad lógica llamada corto-circuito;al iniciar el proceso de comparación mediante los elementos && y encontrarse uno de estos falso, se termina la evaluación comparativa, en el caso anterior a pesar que el último elemento es verdadero este no es evaluado debido a que el segundo ya dio como resultado falso.

La segunda declaración hace uso del operador || (OR), en este caso debido a que un elemento da como resultado verdadero, toda la declaración resulta verdadera.

Finalmente se hace uso del operador de negación (!).

La ejecución de este programa da como resultado:

$ java OperaLogico

prueba1(0)

resultado: true

preuba2(2)

resultado: false

Primera Expresion es Falsa

prueba1(4)

resultado: false

preuba2(1)

resultado: true

Segunda Expresion es Verdadera

Tercer Expresion es Verdadera

Definición de Errores y Bloques try/catch/finally.

Aunque el tiempo ideal para encontrar errores es al momento de compilar un programa, esto no siempre es posible y por esta razón han sido diseñados mecanismos para encontrarlos al momento de ejecución ("Run-Time").La metodología más común para trabajar con errores en lenguajes de programación es interrumpir el flujo de ejecución e invocar otro código que maneje el error en cuestión; en el lenguaje Java a la ocurrencia de errores se le denomina "Exceptions" y al procesamiento de errores "Trap" (atrapar).

Como habría de esperarse, al encontrarse un error ("Exception") en Java es necesario invocar una Clase que realiza las respectivas tareas, para mandar llamar este tipo de Clases es necesario utilizar el vocablothrows; suponiendo que desea atrapar un error si determinada variable no ha sido inicializada, se utilizaría la siguiente sintaxis:

if(t == null)

throw new NullPointerException();

Al utilizar una definición similar a la anterior, se asume que ya se conoce el tipo de error que puede ser generado en el programa, sin embargo, este no siempre es el caso y por esta razón en Java se define una Zona Especifica donde puede ser generado un error y a su vez atrapado, esta Zona "Protegida" es el conocido bloque try/catch/finally, a continuación se describe la sintaxis de este bloque:

Sintaxis del Bloque try/catch

try {

// Código que pueda generar Errores ("Exception's")

} catch(Tipo1 id1) {

// Manejar "Exception's" para la Clase Tipo1

} catch(Tipo2 id2) {

// Manejar "Exception's" para la Clase Tipo2

} catch(Tipo3 id3) {

// Manejar "Exception's" para la Clase Tipo3

}

La primer sección del Bloque es iniciada con el vocablo try y una llave ({ ) que indica el principio de la zona protegida, dentro de esta primer sección es colocado todo código que pueda generar algún tipo de error, al terminar esta sección se define un grupo de estructuras con el elemento catch.

Como se puede observar en la ilustración, cada vocablo catch es seguido de un paréntesis que contiene una Clase así como una referencia, estos son considerados los Datos de Entrada para cada sección catch; el tipo de Clases definidas dentro de cada sección catch depende del numero de "Exceptions" que pueden ser generadas por el programa; lo anterior permite definir un numero ilimitado de "Exceptions" cada uno con su propio código para manejar el error.

Para todo Bloque try/catch debe ser definida al menos una sección catch; si son definidos más de dos Clases para errores ("Exceptions") estas deben ser declaradas de más a menos especifica, siendo la menos especifica la Clase java.lang.Throwable; es de esta Clase que son Heredadas ("Inherited") todo "Exception" en Java, esto será descrito en la siguiente sección de este curso.

Sintaxis del Bloque try/catch/finally

Para los Bloques try/catch existe una variación que consiste en agregar una sección denominada finally, dicha estructura estaría compuesta de la siguiente manera:

try {

// Código que pueda generar Errores ("Exception's")

} catch(Tipo1 id1) {

// Manejar "Exception's" para la Clase Tipo1

} catch(Tipo2 id2) {

// Manejar "Exception's" para la Clase Tipo2

} catch(Tipo3 id3) {

// Manejar "Exception's" para la Clase Tipo3

} finally {

// Actividades que siempre ocurren

}

La utilización de finally es empleada dentro de un Bloque try/catch para realizar tareas que deben ser ejecutadas independientemente del comportamiento de errores.

Cuando se atrapa un error ("Exception") un programa queda inconcluso, es esta terminación abrupta la que puede causar que algún recurso/mecanismo permanezca asignado o mal utilizado; a través de una secciónfinally se garantiza que sea ejecutado un juego de

instrucciones independientemente del tipo de error que pueda ocurrir; un uso muy común para una sección finally consiste en liberar conexiones hacia Bases de Datos que pudieran haber sido asignadas en la sección try.

Además de los errores ("Exceptions") ofrecidos en las Clases Base del JDK, es posible definir errores propietarios para llevar un control más estricto sobre la generación de errores:

Código Fuente DefinirErrores.java

class MiError extends Exception {

public MiError() {}

public MiError(String msg) {

super(msg);

}

}

public class DefinirErrores {

public static void f() throws MiError {

System.out.println("Generando Error explícitamente en f()");

throw new MiError("Originado en f()");

}

public static void g() throws MiError {

System.out.println("Generando Error explícitamente g()");

throw new MiError("Originado en g()");

}

public static void main(String[] args) {

try {

f();

} catch(MiError e) {

e.printStackTrace(System.err);

}

try {

g();

} catch(MiError e) {

e.printStackTrace(System.err);

}

}

}

En el archivo fuente anterior son diseñadas dos Clases : MiError que define un error ("Exception") propietario y DefinirErrores que hace uso de esta Clase.

Clase MiError

Esta clase hereda ("Inherit") el comportamiento de Exception, esto permite utilizar funcionalidades ya definidas en esta Clase. NOTA: Aunque la clase base de todo error ("Exception") esjava.lang.Throwable, generalmente se opta por java.lang.Exception ya que posee mayores funcionalidades(métodos); inclusive si observa la documentación del JDK, notará que la Clasejava.lang.Exception hereda ("inherit") el comportamiento de la Clase java.lang.Throwable, en efecto teniendo acceso a cualquiera de los métodos/campos disponibles en la Clase Base.

Son definidos dos constructores para esta Clase: Uno de estos recibe un String como valor de entrada y a su vez manda llamar la

Clase Base (Exception) a través del vocablo super. Otro es el constructor sin argumentos que debe ser declarado al emplearse más de

un constructor.

Clase DefinirErrores

Esta Clase define dos métodos llamados f() y g(), una característica especifica de estos métodos es que utilizan el vocablo throws seguido de una Clase, esta sintaxis es obligatoria para los métodos que puedan generar errores, debe hacerse fuerte énfasis que esto es independiente de las declaraciones hechas en los bloques try/catch, es simplemente una sintaxis para los métodos capaces de generar errores.

Dentro de cada método f() y g() es generado un error con la clase MiError explícitamente.

En el método principal (main) son definidos dos bloques try/catch, en uno de estos es llamado el métodof() y en otro el método g().

Al ser llamada la función de cada bloque, se genera un error del tipo MiError puesto que cada función invoca explícitamente este error.

Cuando se detecta (atrapa) el error se entra en la sección catch donde es invocada la funciónprintStackTrace vía la referencia e.NOTA: El método printStackTrace fue heredado ("Inherited") de la clase Exception.

La Clase/método System.err representa el "Stream" de salida para cualquier error generado en un programa Java.

Datos de Entrada

A lo largo de los diversos métodos definidos en los distintos programas, se ha podido observar que estos definen sus Datos de Entrada seguido del nombre del mismo dentro de un paréntesis (calificadores nombre_del_metodo (datos_de_entrada)).

La sintaxis de los datos de entrada se compone de dos partes: El tipo de Clase/Primitivo, seguido de la referencia empleada dentro dentro del método; por "default" los métodos principales (main) en Java deben especificar un Arreglo de String's en su definición, esto con

la intención de recibir parámetros al momento de ser llamada la Clase, a continuación se describe un ejemplo que hace uso de este mecanismo:

Código Fuente Nombres.java

public class Nombres {

public static void main(String[] args) {

System.out.println("Su nombre es: " + args[0]);

System.out.println("Sus apellidos son: " + args[1] + " " + args[2]);

}

}

Clase Nombres.

Se inicia este programa con la clásica definición de Clase. Se define el método principal ( main ), el uso de corchetes ([]) al lado de la Clase String

indica unArreglo de String's. Se imprimen dos mensajes a pantalla a través de la Clase System.out.println.

El primer mensaje imprime el primer valor del arreglo de entrada, a través de su referencia args; nótese que este primer valor es indicado con el numero cero, esta es Sintaxis de arreglos la cual será descrita a fondo en otra sección.

El segundo mensaje imprime el segundo y tercer valor del arreglo.

Ejecución de Clase Nombres.

Los valores de entrada para el método main son tomados directamente de la linea de ejecución del programa, por lo que una vez compilada la Clase (javac Nombres.java) la ejecución seria llevada acabo de la siguiente manera:

$ java Nombres Ana Rodriguez King

En este caso el primer valor del arreglo de String's seria el Nombre Ana, mientras el segundo y tercero serian Rodriguez y King respectivamente; inclusive si no proporciona ningún argumento será generado un error ("Exception"), a continuación se describe este mismo programa con un bloque try/catch para manejar el error de una manera apropiada :

Código Fuente NombresBlock.java

public class NombresBlock {

public static void main(String[] args) {

try {

System.out.println("Su nombre es: " + args[0]);

System.out.println("Sus apellidos son: " + args[1] + " " + args[2]);

} catch (ArrayIndexOutOfBoundsException ex) {

System.out.println("Esta tratando de accesar datos fuera del Arreglo");

} catch (Exception ex) {

System.out.println("Ocurrió un error genérico");

}

}

}

Clase NombresBlock.

La única diferencia entre esta Clase y aquella descrita anteriormente es el Bloque try/catch .

Al estar siendo ejecutadas las instrucciones de la sección try, si es generado un error, se inicia la inspección sobre las secciones catch hasta que el error ("Exception") coincida con una declaración.

En esta caso, si son omitidos algunos parámetros de entrada para el programa, dentro del bloque try se estaría tratando de accesar elementos no definidos en el arreglo, este tipo de error ("Exception") es precisamente ArrayIndexOutOfBoundsException, por lo que serán ejecutadas las instrucciones definidas dentro de este bloque.

Ahora bien, nótese que las instrucciones del bloque catch con la Clase Exception nunca son ejecutadas, lo anterior se debe a que el programa termina al ser atrapado ("catch") en el bloque catch inicial; la única manera en que pudieran ser ejecutadas las instrucciones de este último bloque sería si el programa generara otro error ("Exception") distinto a ArrayIndexOutOfBoundsException.

El uso de Datos de Entrada descrito anteriormente es trivial, ya que es realizado antes de iniciarse la ejecución de un programa, en la siguiente sección será descrito el uso de "Streams" para la lectura/escritura de datos en archivos y otros elementos.

"Streams"

Un "Stream" es un nombre genérico otorgado a un flujo de caracteres en un programa de computo, puede estar compuesto por los valores residentes en un archivo texto, datos introducidos interactivamente por un usuario o datos que desean ser colocados en determinado archivo.

Un "Stream" es la representación más cruda de un juego de datos, pero una manera muy universal de accesar/guardar datos, inclusive la clase System.out.println utilizada en la

gran mayoría de los programas de este curso también es un "Stream"; en Java existen diversas Clases utilizadas en conjunción de "Streams", a continuación se describen estas Clases :

java.io.InputStream y java.io.OutputStream

Las principales clases para representar "Streams" son : InputStream y OutputStream, como su nombre lo indica la primera de estas es utilizada para "Streams" de Entrada (Input) mientras la segunda "Streams" de Salida (Output); a partir de estas dos Clases son derivadas todas las Clases que hacen uso de datos de entrada y salida.

Algunas Clases que heredan ("Inherit") el comportamiento de InputStream son : FileInputStream yPipedInputStream y algunas Clases que heredan ("Inherit") de OutputStream son: ByteArrayOutputStream yObjectOutputStream; existen otra gran cantidad de Clases que heredan ("Inherit") el comportamiento de estas Clases bases, inclusive la Clase/Método System.out.println es heredada ("Inherited") de la Clasejava.io.PrintStream la cual a su vez desciende de java.io.OutputStream.

Se recomienda observar la documentación incluida en el JDK para conocer el uso,comportamiento y métodos de las diversas Clases heredadas ("Inherited") de InputStream y OutputStream.

java.io.Writer y java.io.Reader

Existen otras dos variaciones para las Clases InputStream y OutputStream que son : Writer y Reader, la principal diferencia entre estas Clases es que las primeras ofrecen lo que es conocido como byte-orientated I/O, mientras Writer y Reader ofrecen character-based I/O, en otras palabras las ClasesInputStream y OutputStream solamente soportan "Streams" de 8-bits byte, mientras las Clases Writer yReader soporta "Streams" de 16-bits.

La importancia de 16-bits radica en Java utilizando Unicode, al utilizarse 8-bits no es posible emplear muchos caracteres disponibles en Unicode, además debido a que las Clases Writer y Reader son una adición más reciente al JDK, estas poseen mayor velocidad de ejecución a diferencia de sus contra partesInputStream y OutputStream.

Ambas variaciones siguen en uso hoy en día, inclusive existen dos Clases que permiten una conversión hacia estas Clases más recientes:

InputStreamReader convierte un InputStream a Reader OutputStreamWriter convierte un OutputStream a Writer

En la siguiente sección serán descritos ejemplos que hacen uso de "Streams".

El uso de "Streams" en programación es un tema muy extenso y en ocasiones considerado muy abstracto, por esta razón son descritos dos ejemplos en las siguientes secciones:

Código Fuente Calculo.java

import java.io.*;

public class Calculo {

public static void main(String[] args) throws IOException {

String s;

int lineas = 1;

try {

System.out.print("Que archivo desea analizar ? ");

BufferedReader stdin = new BufferedReader(

new InputStreamReader(System.in));

String archivo = stdin.readLine();

BufferedReader sarchivo = new BufferedReader(

new FileReader(archivo));

while (( s = sarchivo.readLine()) != null)

lineas++;

System.out.println("El archivo " + archivo +

" contiene " + lineas + " renglones de Codigo.");

} catch (IOException exc) {

System.err.println(exc);

System.out.println("Asegurese de haber proporcionado " +

" la extension del archivo (\".java\")");

} finally {

System.out.println("");

System.out.println("Es la mejor manera de aprender Streams, practicando!");

}

}

}

Clase Calculo

Esta Clase esta diseñada para interrogar al usuario sobre determinado archivo, leer su contenido y finalmente indicar al usuario el número de renglones que contiene; consta únicamente de su método principal (main) y un bloque try/catch/finally.

De inicio son definidos dos campos para utilizarse dentro del método principal (main) . Posteriormente se define el bloque try/catch/finally con las siguientes características: try

Se despliega una pregunta al usuario para que proporcione el nombre del archivo a leer.

Es definida una Clase del tipo BufferedReader la cual permite archivar datos temporales en memoria ("Buffer")

El argumento que recibe al ser generada la Clase anterior es otra Clase llamada InputStreamReader, la cual permite leer datos de un "Stream", en este caso siendo System.in que representa el shell de ejecución.

Se extrae el nombre del archivo a través del método readLine() y es asignado a una referencia deString.

Es generada una nueva instancia de la Clase BufferedReader a la cual será asignada el archivo en cuestión.

El argumento que recibe al ser generada la Clase anterior es otra Clase llamada FileReader, la cual contiene los mecanismos necesarios para leer un Archivo, y en este caso recibiendo el nombre del archivo.

Se entra en un ciclo while el cual permanecerá activo hasta que el resultado del método readLine()sobre la referencia definida anteriormente equivalga a null. NOTA: El método readLine retorna un resultado null ya que el archivo no contenga lineas.

Dentro de cada iteración de ciclo se incrementa la variable (primitivo) lineas la cual representa el número de lineas en el archivo.

Finalmente se envía a pantalla el resultado obtenido del ciclo. catch

Dentro de está sección del Bloque se intenta atrapar un error ("Exception") del tipo IOException, este tipo de error es generado al no poderse iniciar lectura/escritura en un programa.

En este programa el error sería generado al intentarse leer un archivo inexistente proporcionado por el usuario.

En caso de generarse este error, se envía a pantalla una sugerencia así como el error completo.

finally Esta sección del Bloque describe instrucciones que siempre deben ser ejecutadas

independientemente de ser generado un error ("Exception") o no. Se imprime a pantalla un mensaje cada vez que sea ejecutado el programa, ocurra o

no ocurra un error ("Exception").

En la siguiente sección será descrito el uso de "Streams" para escribir hacia archivos.

Código Fuente LeerArchivo.java

import java.io.*;

public class LeerArchivo {

public static void main(String[] args) throws IOException {

// Leer el Codigo Fuente de este Archivo

BufferedReader in = new BufferedReader(

new FileReader("LeerArchivo.java"));

String s, s2 = new String();

while ((s = in.readLine()) != null)

s2 += s + "\n";

in.close();

// Leer Datos Interactivamente

BufferedReader stdin = new BufferedReader(

new InputStreamReader(System.in));

System.out.print("Introduzca cualquier dato: ");

s2 += "Usted agrego la linea \" " + stdin.readLine() +

" \" en la linea de comandos";

// Depositar Datos en un Archivo de Texto

try {

BufferedReader leer = new BufferedReader(

new StringReader(s2));

PrintWriter escribir = new PrintWriter(

new BufferedWriter(new FileWriter("Archivo_Stream.txt")));

int lineaNo = 1;

while ((s = leer.readLine()) != null)

escribir.println(lineaNo++ + ": " + s);

escribir.close();

} catch (EOFException e) {

System.out.println("Final de Stream");

}

}

}

Clase LeerArchivo

Esta Clase esta diseñada para leer el archivo de Código Fuente presente, leer un renglón proporcionado al momento de ejecutarse el programa y finalmente depositar todo el contenido en otro archivo; al igual que el ejemplo anterior consta únicamente de su método principal (main) y un bloque try/catch.

Lectura de Datos

En la primer sección es definida una Clase BufferedReader, la cual lee el archivo de Código Fuente, nótese que no es necesario colocar esta declaración dentro de un bloque try/catch puesto que ya se sabe que el archivo existe.

Se define un ciclo while que permanecerá activo hasta que el resultado del método readLine() sobre la referencia definida anteriormente equivalga a null. NOTA: El método readLine retorna un resultado nullya que el archivo no contenga lineas.

Dentro de cada iteración, es concatenado cada renglón leído del "Stream" y colocado en la variable s2.

Es definida otra Clase BufferedReader la cual es empleada para datos introducidos de la consola (System.in).

El valor introducido por el usuario es concatenado a la variable s2 que contiene el archivo de código fuente.

Escritura de Datos

Se inicia un Bloque try/catch. Se declara una instancia de la Clase BufferedReader , la cual recibe como parámetro de

entrada elString definido en la sección de lectura. Es definida una Clase PrintWriter que permite escritura de "Streams", esta Clase

recibe como parámetro de entrada la Clase BufferedWriter la cual a su vez recibe la Clase FileWriter, esta última toma como parámetro el archivo que será generado (Archivo_Stream.txt).

Se genera un ciclo while, el cual en cada iteración asigna los valores de cada renglón leído hacia la referencia de la Clase PrintWriter.

Se define un error ("Exception") del tipo EOFException el cual sería generado en caso de llegar a un fin prematuro del archivo.

Arreglos.Un uso muy común en diseños de Software es agrupar distintos valores relacionados entre sí en una sola variable, lo cual permite una manipulación más eficiente de datos; en el

lenguaje Java existen diversos mecanismos para agrupar datos en conjunto, una de ellas que ha venido siendo utilizada a lo largo de los distintos programas en este curso es denominada: Arreglo.

A través de un Arreglo es posible generar agrupaciones de cualquier tipo de Objeto simplemente agregando una simbología de corchetes , ya sea seguido de la definición de Clase o bien la referencia en cuestión, a continuación se describen diversos ejemplos más amplios de Arreglos:

Código Fuente Arreglo.java

En el siguiente código fuente se demuestra el uso de Arreglos con Clases Generadas (Propias) y primitivos, debido a la extensión del ejemplo su descripción es dividida en dos secciones.

class Clip {}

public class Arreglo {

public static void main(String[] args) {

// Arreglos de Objetos

Clip[] a; // Referencia Nula

Clip[] b = new Clip[5];

Clip[] c = new Clip[4];

// Inicialización Dinámica con Ciclo

for(int i = 0; i < c.length; i++)

c[i] = new Clip();

// Inicialización al Crearse Arreglo

Clip[] d = {

new Clip(), new Clip(), new Clip()

};

// Inicialización Dinámica

a = new Clip[] {

new Clip(), new Clip()

};

System.out.println("a.length=" + a.length);

System.out.println("b.length = " + b.length);

// Las referencia dentro del Arreglo

// son automáticamente inicializadas a "null"

for(int i = 0; i < b.length; i++)

System.out.println("b[" + i + "]=" + b[i]);

System.out.println("c.length = " + c.length);

System.out.println("d.length = " + d.length);

a = d;

System.out.println("a.length = " + a.length);

Primeramente se define una Clase denominada Clip la cual simplemente consta de su declaración, seguido se encuentra la definición de la Clase principal Arreglo que contiene solo su método principal (main), dentro de este método se define lo siguiente:

En la primer sección son definidas tres referencias en manera de Arreglo para la Clase Clip; la primer declaración únicamente define la referencia, mientras las siguientes lineas definen dos referencias asignándose un arreglo de 5 y 4 Objetos respectivamente.

Posteriormente son inicializados los distintos arreglos; en el primer caso se emplea un ciclo el cual asigna un Objeto a cada valor del arreglo, seguido se genera un nuevo arreglo el cual es inicializado en la misma declaración, finalmente se lleva acabo la inicialización de la referencia a

En esta ultima sección son impresos los distintos valores de los respectivos arreglos, en estas declaraciones se hace uso del método interno length, dicho método esta disponible para cualquier tipo de arreglo e indica la longitud del mismo; además de esto son impresos los valores del arreglo b, los cuales por "default" son asignados un valor de null; además de lo anterior, un detalle relevante es la asignación a = d , como fue demostrado en un ejemplo anterior, este tipo de asignación hace que lareferencia a apunte hacia los Objetos de la referencia d, por lo que el último valor del arreglo(referencia) a equivale a 3.

A continuación se describe el resto del Código Fuente para el programa:

// Arreglo de Primitivos

int[] e; // Referencia Nula

int[] f = new int[5];

int[] g = new int[4];

// Inicialización Dinámica con Ciclo

for(int i = 0; i < g.length; i++)

g[i] = i*i;

// Inicialización al Crearse Arreglo

int[] h = { 11, 47, 93 };

// Error Compilación : Variable(Arreglo) no inicializada

// System.out.println("e.length=" + e.length);

System.out.println("f.length = " + f.length);

// Los primitivos dentro del Arreglo

// son inicializados a cero:

for(int i = 0; i < f.length; i++)

System.out.println("f[" + i + "]=" + f[i]);

for(int i = 0; i < g.length; i++)

System.out.println("g[" + i + "]=" + g[i]);

System.out.println("h.length = " + h.length);

e = h;

System.out.println("e.length = " + e.length);

e = new int[] { 1, 2 };

System.out.println("e.length = " + e.length);

}

}

En la primer sección son definidas tres variables en manera de Arreglo para primitivos int; la primer declaración únicamente define la variable, mientras las siguientes lineas definen dos variables asignándose un arreglo de 5 y 4 primitivos respectivamente.

Posteriormente son inicializados los distintos arreglos; en el primer caso se emplea un ciclo el cual asigna valores al arreglo g, seguido se genera un nuevo arreglo el cual es inicializado en la misma declaración.

En esta ultima sección son impresos los distintos valores de los respectivos arreglos: La primer declaración (nótese el uso de comentarios) intenta imprimir la longitud del

arreglo e, esto generaría un error de compilación debido a que el arreglo e no ha sido inicializado.

Posteriormente se imprime el tamaño del arreglo f. Se imprimen los valores de los arreglos f y g, seguido de la longitud del arreglo h, los

cuales como cualquier primitivo int son asignados un valor de 0 (cero) por "default".

Se asigna el valor del arreglo h al arreglo e. Debido a la asignación anterior, es posible utilizar el método length sobre el

arreglo e, el cual tomará el mismo valor del arreglo h. Son redefinidos-reinicializados los valores del arreglo e con dos nuevos primitivos. Finalmente se imprime la nueva longitud del arreglo e.

Al final de esta sección serán descritas otras metodologías para agrupar datos que poseen distintas ventajas a emplear un Arreglo.

PolimorfismoEl concepto de Polimorfismo es uno de los fundamentos para cualquier lenguaje orientado a Objetos, las mismas raíces de la palabra pueden ser una fuerte pista de su significado: Poli = Multiple, morfismo= Formas , esto implica que un mismo Objeto puede tomar diversas formas.

A través del concepto de Herencias ("Inheritance") es posible ilustrar este comportamiento:

El poder manipular un Objeto como si éste fuera de un tipo genérico otorga mayor flexibilidad al momento de programar con Objetos, el término Polimorfismo también es asociado con un concepto llamado Late-Binding (Ligamiento Tardío), observe el siguiente fragmento de código:

Figura a = new Circulo();

Figura b = new Triangulo();

Inicialmente se puede pensar que este código generaría un error debido a que el tipo de referencia es distinta a la instancia del objeto, sin embargo, el fragmento anterior es correcto y demuestra el concepto de Polimorfismo; para asentar este tema se describe un ejemplo más completo:

Código Fuente Musica.java

import java.util.*;

class Instrumento {

public void tocar() {

System.out.println("Instrumento.tocar()");

}

public String tipo() {

return "Instrumento";

}

public void afinar() {}

}

class Guitarra extends Instrumento {

public void tocar() {

System.out.println("Guitarra.tocar()");

}

public String tipo() { return "Guitarra"; }

public void afinar() {}

}

class Piano extends Instrumento {

public void tocar() {

System.out.println("Piano.tocar()");

}

public String tipo() { return "Piano"; }

public void afinar() {}

}

class Saxofon extends Instrumento {

public void tocar() {

System.out.println("Saxofon.tocar()");

}

public String tipo() { return "Saxofon"; }

public void afinar() {}

}

// Un tipo de Guitarra

class Guzla extends Guitarra {

public void tocar() {

System.out.println("Guzla.tocar()");

}

public void afinar() {

System.out.println("Guzla.afinar()");

}

}

// Un tipo de Guitarra

class Ukelele extends Guitarra {

public void tocar() {

System.out.println("Ukelele.tocar()");

}

public String tipo() { return "Ukelele"; }

}

public class Musica {

// No importa el tipo de Instrumento,

// seguirá funcionando debido a Polimorfismo:

static void afinar(Instrumento i) {

// ...

i.tocar();

}

static void afinarTodo(Instrumento[] e) {

for(int i = 0; i < e.length; i++)

afinar(e[i]);

}

public static void main(String[] args) {

Instrumento[] orquesta = new Instrumento[5];

int i = 0;

// Up-casting al asignarse el Arreglo

orquesta[i++] = new Guitarra();

orquesta[i++] = new Piano();

orquesta[i++] = new Saxofon();

orquesta[i++] = new Guzla();

orquesta[i++] = new Ukelele();

afinarTodo(orquesta);

}

}

Clase Musica

En el código fuente de Musica.java son diseñadas diversas Clases que demuestran el uso de Polimorfismo:

Instrumento: Es utilizada como la Clase Base para el resto de Clases y en ella son definidos tres métodos: tocar,tipo y afinar.

Guitarra: Hereda ("Inherit") de la Clase Instrumento y redefine ("Override") los métodos de ésta.

Piano: Hereda ("Inherit") de la Clase Instrumento y redefine ("Override") los métodos de ésta.

Saxofon: Hereda ("Inherit") de la Clase Instrumento y redefine ("Override") los métodos de ésta.

Guzla: Hereda ("Inherit") de la Clase Guitarra y redefine ("Override") los métodos de ésta.

Ukelele: Hereda ("Inherit") de la Clase Guitarra y redefine ("Override") los métodos de ésta.

Las definiciones de la Clase principal Musica son descritas en los siguientes incisos:

El primer método definido en esta Clase llamado afinar toma como valor de entrada una referencia del tipo Instrumento, sobre la cual es invocado el método tocar.

Un segundo método nombrado afinarTodo toma como valor de inicia un arreglo de Instrumento, el cual es procesado por un ciclo que a su vez manda llamar el método afinar con los respectivos valores del arreglo.

Dentro del método principal se define lo siguiente:

Primeramente se genera un arreglo de Instrumento para 5 Objetos. Se inicializa un primitivo i con un valor de cero. A través de la referencia orquesta son asignados distintos Objetos al arreglo, nótese

que aunque el arreglo es de tipo Instrumento es posible asignar los Objetos: Guitarra,Piano,Saxofon,Guzla,Ukelele.

Finalmente se invoca el método afinarTodo con la referencia que representa el arreglo deInstrumento.

Al ser ejecutado este programa se obtienen los siguientes resultados:

$ java Musica

Guitarra.tocar()

Piano.tocar()

Saxofon.tocar()

Guzla.tocar()

Ukelele.tocar()

Lo interesante de este resultado reside en la manera que fue realizada la invocación de métodos, el método que realiza las llamadas hacia las diversas clases es afinar dentro de la Clase Musica, observe que este método toma como valor inicial una referencia del tipo Instrumento y se invoca el método tocar.

Sin embargo, al ser invocado el método tocar a pesar de ser a través de una referencia Instrumento es invocado el método de Clase acordemente, esto es, se llama el método de la Clase especifica; lo anterior es una labor de Polimorfismo ya que ocurre el comportamiento adecuado a pesar de realizarse las llamadas a través de una clase general (Instrumento).

El uso de Polimorfismo posee ciertos detalles que no fueron descritos en el ejemplo anterior, uno de estos, que también tiene implicaciones en otros componentes es: Casting.

Uso de "Casting"El termino "Casting" viene de la palabra "Cast" que significa Molde, por lo que el termino literal es Hacer un Molde, en Polimorfismo se lleva acabo este proceso de "Casting" implícitamente, una Guitarra se coloca en el molde de un Instrumento, un Triangulo en el molde de una Figura, sin embargo, en ciertas ocasiones se requiere realizar un tipo de "Casting" que no es considerado seguro en términos computacionales.

Anteriormente se mencionó que el "Casting" llevado acabo con Polimorfismo es implícito, esto se debe a que no se requiere de sintaxis especial, simplemente se convierte una Guitarra a un Instrumento, sin embargo, para llevar una transformación en sentido opuesto se requiere de sintaxis adicional para mantener la seguridad de transformación; analicemos: mientras se puede asegurar que un Triangulo es unaFigura ("Up-Casting"), una Figura no necesariamente es un Triangulo, claro esta que lo puede ser, pero en Java se requiere definir explícitamente esta operación ("Down-Casting").

El uso practico de "Down-Casting" será explorado en la sección de Vectores y "Hashtables".

Interfases y Clases Abstractas.

Clases Abstractas.

Al ser utilizado Herencias ("Inheritance") y/o Polimorfismo es muy común que en la Clase Base existan métodos diseñados únicamente con el propósito de ofrecer una guia para las Clases heredadas, en Java existe un vocablo que permite prohibir el uso de métodos en Clases Base, este calificativo es : abstract.Al ser definido un método como abstract se restringe que éste sea llamado directamente, cuando una Clase contiene un método de este tipo a ésta se le llama: Clase Abstracta.

Al ser definida una Clase, además de ser declarados los métodos/campos como abstract también es necesario utilizar el vocablo abstract en la definición de cada Clase.

Código Fuente Musica2.java

import java.util.*;

abstract class Instrumento {

public abstract void tocar();

public String tipo() {

return "Instrumento";

}

public abstract void afinar();

}

class Guitarra extends Instrumento {

public void tocar() {

System.out.println("Guitarra.tocar()");

}

public String tipo() { return "Guitarra"; }

public void afinar() {}

}

class Piano extends Instrumento {

public void tocar() {

System.out.println("Piano.tocar()");

}

public String tipo() { return "Piano"; }

public void afinar() {}

}

class Saxofon extends Instrumento {

public void tocar() {

System.out.println("Saxofon.tocar()");

}

public String tipo() { return "Saxofon"; }

public void afinar() {}

}

// Un tipo de Guitarra

class Guzla extends Guitarra {

public void tocar() {

System.out.println("Guzla.tocar()");

}

public void afinar() {

System.out.println("Guzla.afinar()");

}

}

// Un tipo de Guitarra

class Ukelele extends Guitarra {

public void tocar() {

System.out.println("Ukelele.tocar()");

}

public String tipo() { return "Ukelele"; }

}

public class Musica2 {

// No importa el tipo de Instrumento,

// seguirá funcionando debido a Polimorfismo:

static void afinar(Instrumento i) {

// ...

i.tocar();

}

static void afinarTodo(Instrumento[] e) {

for(int i = 0; i < e.length; i++)

afinar(e[i]);

}

public static void main(String[] args) {

// Declarar un Arreglo SIN INSTANCIAS es valido en Clases Abstractas

Instrumento[] orquesta = new Instrumento[5];

// Generar una INSTANCIA de una la Clase Abstracta no es valido

// Instrumento nuevo = new Instrumento();

int i = 0;

// Up-casting al asignarse el Arreglo

orquesta[i++] = new Guitarra();

orquesta[i++] = new Piano();

orquesta[i++] = new Saxofon();

orquesta[i++] = new Guzla();

orquesta[i++] = new Ukelele();

afinarTodo(orquesta);

}

}

Clase Musica2

La Clase anterior es idéntica aquella definida en el ejemplo de Polimorfismo, sin embargo, la Clase Base deInstrumento fue definida como abstract, algunos detalles de esta definición :

Nótese que los métodos definidos como abstract no contienen ningún tipo de código dentro de ellos, inclusive no declaran ni llaves ({ }).

Cuando es definido más de un método como abstract, es necesario que la Clase como tal sea definida también como abstract.

La característica de hacer una Clase/Método abstract reside en que no puede ser generada una instanciade la misma, este comportamiento se demuestra en el método principal (main)

Aunque dentro del método sea generado un Arreglo de esta Clase abstracta, recuerde que un arreglo es únicamente un contenedor de Objetos, esto permite que sea generado sin ningún error.

Dentro de comentarios se encuentra la generación de una instancia del tipo Instrumento la cual generaría un error al ser compilada la Clase.

Una de las características de las Clases que Heredan("Inherit") de una Clase abstracta, es que éstas deben definir los mismos métodos definidos en la Clase Base; en Java existe otro mecanismo que permite llevar acabo diseños donde se parte de una Estructura o Cápsula.

Interfases.

Una Interfase es una Clase abstracta llevada al extremo, la cual permite pre-definir el uso de métodos/campos en futuras clases tal como: Una jerarquía de Instrumentos restringido al uso de una Interfase Instrumento, o bien, una estructura de Figuras al uso de la Interfase Figura.

Una Interfase básicamente dice: "Todas las Clases que implementen esta Interfase deben contener su misma estructura", para definir una Interfase se utiliza el vocablo interface y para especificar que una Clase debe utilizar determinada Interfase se utiliza el vocablo implements.

Al ser utilizada una Interfase en una Clase se esta indicando: "La Interfase dicta la forma/estructura ahora se va describir como debe funcionar", en el siguiente ejemplo se describe el uso de Interfases:

Código Fuente Musica3.java

import java.util.*;

interface Instrumento {

// Constante al compilar, automáticamente static y final

int i = 5;

// Métodos Automáticamente Públicos

void tocar();

String tipo();

void afinar();

}

class Guitarra implements Instrumento {

public void tocar() {

System.out.println("Guitarra.tocar()");

}

public String tipo() { return "Guitarra"; }

public void afinar() {}

}

class Piano implements Instrumento {

public void tocar() {

System.out.println("Piano.tocar()");

}

public String tipo() { return "Piano"; }

public void afinar() {}

}

class Saxofon implements Instrumento {

public void tocar() {

System.out.println("Saxofon.tocar()");

}

public String tipo() { return "Saxofon"; }

public void afinar() {}

}

// Un tipo de Guitarra

class Guzla extends Guitarra {

public void tocar() {

System.out.println("Guzla.tocar()");

}

public void afinar() {

System.out.println("Guzla.afinar()");

}

}

// Un tipo de Guitarra

class Ukelele extends Guitarra {

public void tocar() {

System.out.println("Ukelele.tocar()");

}

public String tipo() { return "Ukelele"; }

}

public class Musica3 {

// No importa el tipo de Instrumento,

// seguirá funcionando debido a Polimorfismo:

static void afinar(Instrumento i) {

// ...

i.tocar();

}

static void afinarTodo(Instrumento[] e) {

for(int i = 0; i < e.length; i++)

afinar(e[i]);

}

public static void main(String[] args) {

// Declarar un Arreglo SIN INSTANCIAS es válido en Clases Abstractas

Instrumento[] orquesta = new Instrumento[5];

int i = 0;

// Up-casting al asignarse el Arreglo

orquesta[i++] = new Guitarra();

orquesta[i++] = new Piano();

orquesta[i++] = new Saxofon();

orquesta[i++] = new Guzla();

orquesta[i++] = new Ukelele();

afinarTodo(orquesta);

}

}

Clase Musica3

La Clase anterior es idéntica aquella definida en los últimos dos ejemplos, sin embargo, la Clase Base deInstrumento fue definida como una interfase a través del vocablo interface, algunos detalles de esta definición :

Nótese que los métodos definidos dentro de la interfase no contienen ningún tipo de código dentro de ellos, inclusive no declaran ni llaves ({ }).

A diferencia de los métodos definidos en Clases Abstractas, los métodos definidos en Interfases no utilizan ningún calificador de acceso (public, private, protected).

Cuando una Clase hace uso de una Interfase esta utiliza la palabra implements, además es necesario definir todos los métodos definidos en la Interfase dentro de la Clase.

En la siguiente sección se describe el uso de Interfases para Herencias Múltiples ("Multiple Inheritance").

Una característica especifica de Interfases que no es posible a través de Clases Abstractas es el uso de Herencias Múltiples ("Multiple Inheritance"), este concepto reside en diseñar Clases que adoptan el comportamiento de más de una Clase, a continuación se describe su uso:

Código Fuente Cardiologo.java

import java.util.*;

interface PuedeCurar {

void curar();

}

interface PuedeConsultar {

void consultar();

}

interface PuedeRecetar{

void recetar();

}

class Cirujano {

public void operar() { System.out.println("Cirujano.operar()"); }

}

class Medico extends Cirujano

implements PuedeCurar, PuedeConsultar, PuedeRecetar {

public void curar() { System.out.println("Medico.curar()"); }

public void consultar() { System.out.println("Medico.consultar()"); }

public void recetar() { System.out.println("Medico.recetar()"); }

}

class Cardiologo {

static void r(PuedeCurar x) { x.curar(); }

static void s(PuedeConsultar x) { x.consultar(); }

static void t(PuedeRecetar x) { x.recetar(); }

static void u(Cirujano x) { x.operar(); }

public static void main(String[] args) {

Medico m = new Medico();

r(m);

s(m);

t(m);

u(m);

}

}

En este archivo fuente son definidas tres Interfases y tres Clases las cuales son descritas a continuación:

Interfases PuedeCurar,PuedeConsultar,PuedeRecetar.

Cada una de estas interfases define un método el cual deberá ser implementado en las respectivas Clases que hagan uso de las interfases.

Clase Cirujano.

Esta Clase simplemente define un método llamado operar que imprime un mensaje a pantalla.

Clase Medico.

Contiene la mayor parte de código funcional de este archivo fuente:

Es definida al igual que cualquier otra Clase con el vocablo class. Se utiliza el vocablo extends para heredar ("Inherit") el comportamiento de la

Clase Cirujano.

Seguido se implementan las interfases PuedeCurar,PuedeConsultar,PuedeRecetar a través de implements, esto obliga a que sean definidos los métodos de las diversas interfases.

Son escritas las diversas implementaciones de los métodos de cada interfase, los cuales envían un mensaje a pantalla.

Clase Cardiologo.

Dentro del método principal (main) de esta Clase se genera una instancia de la Clase Medico.

A través de dicha referencia son invocados los diversos métodos locales r,s,t,u. El método r manda llamar la función curar, nótese que aunque el dato de entrada

aparece comoPuedeCurar (Interfase) la invocación se lleva acabo directamente de la Clase Medico.

El método s manda llamar la función consultar, nótese que aunque el dato de entrada aparece comoPuedeConsultar (Interfase) la invocación se lleva acabo directamente de la Clase Medico.

El método t manda llamar la función recetar, nótese que aunque el dato de entrada aparece comoPuedeRecetar (Interfase) la invocación se lleva acabo directamente de la Clase Medico.

El método u invoca la función operar disponible en la Clase Cirujano.

Cabe mencionar que el uso de Interfases es ampliamente utilizado en EJB's ("Enterprise Java Beans") uno de los principales componentes de J2EE ("Java 2 Enterprise Edition").

Vectores, Hashtables y otras estructuras de Datos.Algunas de las principales características de Arreglos en Java son las siguientes:

Es necesario definir el tamaño de un Arreglo antes de ser utilizado, este se hace con la intención de administrar el uso de memoria, pero a su vez presenta la restricción de utilizar Arreglos en estructuras de datos cambiantes.

Un arreglo únicamente puede incluir Objetos/primitivos del mismo tipo, si bien esto permite un control más estricto sobre estructura de datos, restringe una posible mezcla de elementos comunes que difieran en su Clase.

A diferencia de un Arreglo, un Vector es un tipo de Objeto que permite almacenar objetos (referencias) de cualquier tipo, que además de ofrecer estas facilidades, también es posible que su tamaño incremente conforme las necesidades del programa lo requieran; aparentemente este es un contenedor de Objetos ideal para toda situación, sin embargo, su uso puede ser un poco deficiente ya que requiere mayor uso de memoria que un arreglo de Objetos, el siguiente ejemplo demuestra el uso de esta Clase :

Código Fuente ArticulosOficina.java

En el siguiente código fuente se demuestra el uso de Vectores:

import java.util.Vector;

class Clip {}

class Lapiz {}

class Pluma {}

class Silla {}

class SillaReclinable {}

public class ArticulosOficina {

public static void main(String[] args) {

// Un arreglo de Clips

Clip[] c = new Clip[5];

// Un objeto de Lapiz

Lapiz l = new Lapiz();

// Un arreglo de Plumas

Pluma[] p = new Pluma[7];

// Un arreglo de Sillas

Silla[] s = {

new Silla(), new Silla(), new Silla()

};

// Un Objeto Tipo String

String deptF = new String("Finanzas");

String deptV = new String("Ventas");

// Definir Dos Vectores

Vector ciudad1 = new Vector(3);

Vector ciudad2 = new Vector(4,5);

//Iniciar asignación de Objetos a Vectores

//Un Vector puede tomar cualquier tipo de Objeto

ciudad1.add(deptF);

ciudad1.add(c);

ciudad1.add(l);

ciudad2.add(deptV);

ciudad2.add(c);

ciudad2.add(l);

System.out.println("Vector ciudad1 tiene " + ciudad1.size() +

" elementos y una capacidad de " + ciudad1.capacity());

System.out.println("Vector ciudad2 tiene " + ciudad2.size() +

" elementos y una capacidad de " + ciudad2.capacity());

// Asignar otros valores para ver incremento de capacidad

ciudad1.add(p);

ciudad1.add(s);

ciudad2.add(p);

ciudad2.add(s);

System.out.println("Después de agregar otros elementos: ");

System.out.println("Vector ciudad1 tiene " + ciudad1.size() +

" elementos y una capacidad de " + ciudad1.capacity());

System.out.println("Vector ciudad2 tiene " + ciudad2.size() +

" elementos y una capacidad de " + ciudad2.capacity());

String deptExtraido = (String) ciudad1.elementAt(0);

Silla[] sillaExtraida = (Silla[]) ciudad1.elementAt(4);

System.out.println("El departamento de " + deptExtraido +

" en la ciudad1 tiene " + sillaExtraida.length + " sillas");

SillaReclinable[] sr = new SillaReclinable[12];

ciudad2.insertElementAt(sr,4);

deptExtraido = (String) ciudad2.elementAt(0);

// La siguiente extracción generaría un error

// porque este elemento del Vector ya no es una Clase Silla

// sillaExtraida = (Silla[]) ciudad2.elementAt(4);

SillaReclinable[] sillaRecExtraida = (SillaReclinable[]) ciudad2.elementAt(4);

System.out.println("El departamento de " + deptExtraido +

" en la ciudad2 tiene " + sillaRecExtraida.length + " sillas");

}

}

Primeramente se definen varias Clases que constan únicamente de su declaración : Clip, Lapiz, Pluma, Silla, SillaReclinable seguido se encuentra la definición de la Clase principal ArticulosOficina que contiene solo su método principal (main), dentro de este método se define lo siguiente:

Se definen diversas instancias de las Clases declaradas con anterioridad, unas de ellas en manera de arreglos y otras como un solo objeto.

Posteriormente se definen dos vectores, a través de distintos constructores; la primer instancia indica que su espacio inicial será para tres objetos, mientras la segunda instancia para cuatro objetos, donde el 5 indica la capacidad de incremento (delta) cuando se requiera espacio para más objetos.

Son asignados tres objetos a los Vectores y se imprime a pantalla el tamaño y capacidad de ambos.

Son asignados otros dos objetos a los Vectores y se imprime a pantalla su tamaño y capacidad, con lo que puede corroborarse la manera en que incrementan su tamaño.

Son extraídos del primer Vector dos elementos, nótese que dicha extracción requiere que sea realizado un "Casting" explicito, lo cual es llevado acabo a través de paréntesis ( ) ; esto se debe a que la extracción es llevada acabo de una Clase genérica (Vector) hacia una más especifica ya sea String o Silla[] ("Down-Casting").

Es reasignado un valor del segundo vector a través del método insertElement, el cual toma como valor de entrada el índice a substituirse.

Son extraídos del segundo Vector dos elementos, para posteriormente imprimirse a pantalla sus valores.

Otro elemento que es ampliamente empleado en programación es la utilización de listas de valores con determinados conceptos, esto es conocido como key-value lists y en el mundo Java recibe el nombre de "Hashtable", a continuación se describe un ejemplo de "Hashtable's":

Código Fuente Direccion.java

import java.util.*;

public class Direccion {

public static void main(String[] args) {

Hashtable direccion = new Hashtable();

Integer ocho = new Integer(8000);

direccion.put("calle","Primavera");

direccion.put("numero", ocho);

direccion.put("colonia"," La Silla ");

direccion.put("ciudad"," Monterrey ");

direccion.put("estado"," Nuevo Leon ");

direccion.put("pais","Mexico");

String miciudad = (String) direccion.get("ciudad");

String miestado = (String) direccion.get("estado");

String micalle = (String) direccion.get("calle");

Integer minumero = (Integer) direccion.get("numero");

System.out.println("Direccion : " + micalle + " " + minumero);

System.out.println("Lugar: " + miciudad + "," + miestado);

}

}

Clase Direccion

Esta Clase únicamente contiene su método principal (main) el cual contiene las siguientes funcionalidades:

Se define una Clase Hashtable con la referencia direccion. Posteriormente se define un número Integer; este es un de los casos donde deben ser

utilizados"Wrappers" para primitivos, puesto que el elemento será integrado a un contenedor ("Hashtable") en forma de Objeto es necesario convertir ("Wrap") el primitivo como Objeto.

A través del método put son colocados diversos juegos de datos en el "Hashtable", el primer elemento indica el nombre ("key") del elemento, mientras el segundo indica el Objeto/Valor de este nombre ("key"); nótese los Objetos/Valores pueden ser de cualquier tipo, en este ejemplo se colocan diversos Strings y la referencia del Objeto Integer.

Mediante el método get son extraídos algunos valores del "Hashtable" mediante el correspondiente nombre ("key").

Nótese que dicha extracción requiere que sea realizado un "Casting" explicito, lo cual es llevado acabo a través de paréntesis ( ) ; esto se debe a que la extracción es llevada acabo de una Clase genérica ("Hashtable") hacia una más especifica ya sea String o Integer ("Down-Casting").

Finalmente son impresos a pantalla algunos valores extraídos del "Hashtable".

Además del uso de Vectores y "Hashtables", también existen otra serie de Clases para el almacenaje de Objetos, cada una de éstas posee sus ventajas y desventajas para agrupar

elementos comunes; mayores detalles sobre estas estructuras de datos serán descritas a continuación en el "Collection Framework" de Java.

Collections Framework.Mientras en la sección anterior se describió el uso de estructuras de datos para agrupar Objetos, tales como Arreglos, Vectores y Hashtables, estas estructuras también conocidas como Collections fueron incorporadas en las versiones iniciales de Java, sin embargo, en las distribuciones más recientes de la plataforma (1.2) han sido agregadas otra serie de estructuras para manipular grupos de Objetos, lo que ha llevado a conformar un Collections Framework.

A través de un Collection Framework se uniformiza la manera en que son manipulados grupos de Objetos, donde las características principales de éste Collection Framework son:

Interfases : Son definidas una serie de Interfases que permiten extender o generar Clases

especificas para manipular objetos en forma grupal ("Collections").

Implementaciones : Proporciona una serie de implementaciones (Clases) concretas en las

que se pueden almacenar objetos en forma grupal ("Collections").

Algoritmos : Ofrece una serie de métodos estándar para manipular los objetos dentro de

una implementación ("Collection"), como ordenamiento o búsqueda.

A continuación se describe la estructura de Interfases definidas en el Collection Framework:

I. Collection: Esta interfase representa un grupo general de objetos, donde es posible que el grupo contenga objetos duplicados.

List: Es un tipo de Collection que permite duplicados, sin embargo, contiene mecanismos paraindexar sus valores, esto permite extraer elementos en base a su posición (Similar a un Vector).

Set: También es un tipo de Collection que prohibe el uso de objetos duplicados. SortedSet: Es un tipo de Set que mantiene un orden natural sobre sus valores,

y ofrece una serie de métodos avanzados para ordenamiento.II. Map: Permite el almacenamiento de listas de valores (key-value lists) y no puede

contener valores duplicados (key's). SortedMap: Es un tipo de Map que mantiene un orden natural sobre sus valores

(key's), y ofrece una serie de métodos avanzados para ordenamiento.

Es a partir de esta estructura de Interfases que se derivan una serie de implementaciones proporcionadas en el JDK, a continuación se describen diversos ejemplos que hacen uso de las Clases proporcionadas en el JDK diseñadas alrededor de las interfases que forman la parte medular del Collection Framework:

De entrada vale recordar que las interfases List, Set y SortedSet son descendientes de la interfase globalCollection, por lo que el concepto de Polimorfismo aplica para todas las Clases mencionadas a continuación.

Las Clases del JDK que implementan la interfase List son: ArrayList y LinkedList

Las Clases del JDK que implementan la interfase Set son: HashSet y LinkedHashSet, mientras que la claseTreeSet implementa la sub-interfase SortedSet.

Código Fuente Producto.java

class Producto

{

Producto(String s, int i)

{

nombre = s;

cantidad = i;

}

String nombre;

int cantidad;

}

Primeramente se define una Clase que será utilizada como el principal objeto en las agrupaciones ("Collections") descritas a continuación, esta Clase se encuentra compuesta por dos campos y un constructor para la asignación de valores.

Código Fuente Mercado.java

import java.util.*;

public class Mercado

{

public static void main(String args[])

{

// Definir 5 instancias de la Clase Producto

Producto m = new Producto("Pan", 6);

Producto n = new Producto("Leche", 2);

Producto o = new Producto("Manzanas", 5);

Producto p = new Producto("Brocoli", 2);

Producto q = new Producto("Carne", 2);

// Definir un ArrayList

ArrayList mandado = new ArrayList();

// Colocar Instancias de Producto en ArrayList

mandado.add(m);

mandado.add(n);

mandado.add(o);

mandado.add(p);

// Indica el indice de inserción

mandado.add(1, q);

mandado.add(q);

// Imprimir contenido de ArrayLists

System.out.println(" - Lista de mandado con " + mandado.size() + " elementos");

// Definir Iterator para extraer/imprimir valores

for( Iterator it = mandado.iterator(); it.hasNext(); ) {

Producto x = (Producto)it.next();

System.out.println(x.nombre + " : " + x.cantidad);

}

// Eliminar elemento de ArrayList

mandado.remove(2);

System.out.println(" - Lista de mandado con " + mandado.size() + " elementos");

// Definir Iterator para extraer/imprimir valores

for( Iterator it2 = mandado.iterator(); it2.hasNext();) {

Producto x = (Producto)it2.next();

System.out.println(x.nombre + " : " + x.cantidad);

}

// Eliminar todos los valores del ArrayList

mandado.clear();

System.out.println(" - Lista de mandado final con " + mandado.size() + " elementos");

}

}

Como primer paso se importan las librerías de java.util.* donde se concentran la gran mayoría de las Clases del "Collection Framework".

Posteriormente se inicia la declaración de la Clase seguido de su método principal main. Se definen 5 instancias de la Clase Producto diseñada anteriormente, donde cada

instancia recibe sus valores de acuerdo a la definición del Constructor. Seguido son agregadas estas instancias al ArrayList mediante el método add, para

posteriormente imprimir el número de Objetos en el "Collection" mediante el método size.

Se declara una instancia Iterator la cual facilita la extracción de objetos en "Collections", para así extraer los valores del ArrayList e imprimirlos a pantalla.

Una vez impresos los valores del ArrayList es eliminado el objeto con índice número 2, e impreso de nuevo el tamaño del ArrayList con el método size.

Se define otro ciclo de extracción a través de Iterator para el ArrayList, y finalmente se eliminan todos los valores del "Collection" mediante el método clear.

Codigo Fuente Mercado2.java

import java.util.*;

public class Mercado2

{

public static void main(String args[])

{

// Definir 5 instancias de la Clase Producto

Producto m = new Producto("Pan", 6);

Producto n = new Producto("Leche", 2);

Producto o = new Producto("Manzanas", 5);

Producto p = new Producto("Brocoli", 2);

Producto q = new Producto("Carne", 2);

// Definir un HashSet

HashSet mandado = new HashSet();

mandado.add(m);

mandado.add(n);

mandado.add(o);

mandado.add(p);

mandado.add(q);

// Doble inserción de Objeto/referencia q

mandado.add(q);

// Imprimir contenido de HashSet

// Aunque son insertados 6 elementos, el HashSet solo contiene 5

// Lo anterior se debe a que un Set no permite elementos duplicados (q)

System.out.println(" - Lista de mandado con " + mandado.size() + " elementos");

// Definir Iterator para extraer/imprimir valores

for( Iterator it = mandado.iterator(); it.hasNext();) {

Producto x = (Producto)it.next();

System.out.println(x.nombre + " : " + x.cantidad);

}

// No es posible eliminar elementos por indice

// Un HashSet no ofrece este mecanismo, solo eliminación por valor de Objeto

// Eliminar todos los valores del ArrayList

mandado.clear();

System.out.println(" - Lista de mandado final con " + mandado.size() + " elementos");

}

}

Esta Clase es muy similar a la anterior (Mercado.java) , solo que en este caso se emplea un HashSet para agrupar los elementos, a continuación se describen las diferencias entre emplear este "Collection" y el inicial (ArrayList).

Como primer paso se importan las librerías de java.util.* donde se concentran la gran mayoría de las Clases del "Collection Framework".

Posteriormente se inicia la declaración de la Clase seguido de su método principal main. Se definen 5 instancias de la Clase Producto diseñada anteriormente, donde cada

instancia recibe sus valores de acuerdo a la definición del Constructor. Seguido son agregadas estas instancias al HashSet mediante el método add, para

posteriormente imprimir el número de Objetos en el "Collection" mediante el método size.

Note que el número de elementos impresos no corresponde al mismo número insertado, lo anterior se debe a que en dos ocasiones se realizó la operación de inserción sobre la referencia q, esta es una característica específica de todo Set al no permitir elementos duplicados.

Posteriormente es definida una instancia Iterator la cual facilita la extracción de objetos en "Collections", para así extraer los valores del HashSet e imprimirlos a pantalla.

A diferencia de un ArrayList, en un HashSet no pueden ser eliminados valores individualmente ya que este tipo de "Collection" carece de un índice.

Finalmente se eliminan todos los valores del "Collection" mediante el método clear.

Codigo Fuente Mercado3.java

import java.util.*;

public class Mercado3

{

public static void main(String args[])

{

// Definir 5 instancias de String

String m = new String("Pan");

String n = new String("Leche");

String o = new String("Manzanas");

String p = new String("Brocoli");

String q = new String("Carne");

String r = new String("Manzanas");

// Definir un TreeSet

TreeSet mandado = new TreeSet();

mandado.add(m);

mandado.add(n);

mandado.add(o);

mandado.add(p);

mandado.add(q);

mandado.add(r);

// Imprimir contenido de TreeSet

// Aunque son insertados 6 elementos, el TreeSet solo contiene 5

// Lo anterior se debe a que un TreeSet no permite elementos duplicados,

// a pesar que son empleados Objetos distintos, el TreeSet detecta que el

// elemento "Manzanas" es duplicado

System.out.println(" - Lista de mandado con " + mandado.size() + " elementos");

// Definir Iterator para extraer/imprimir valores

for( Iterator it = mandado.iterator(); it.hasNext();) {

// Notese que el orden del TreeSet refleja un orden descendente

// en sus elementos independientemente del orden de inserción.

// Debido al uso de String's esto refleja un orden alfabético

String x = (String)it.next();

System.out.println(x);

}

// No es posible eliminar elementos por indice

// Un TreeSet no ofrece este mecanismo, solo eliminación por valor de Objeto

// Eliminar todos los valores del TreeSet

mandado.clear();

System.out.println(" - Lista de mandado final con " + mandado.size() + " elementos");

}

}

Esta Clase es muy similar a la inicial (Mercado.java) , solo que en este caso se emplea un TreeSet para agrupar los elementos, a continuación se describen las diferencias entre emplear este "Collection" y los otros dos mencionados en esta sección .

Como primer paso se importan las librerías de java.util.* donde se concentran la gran mayoría de las Clases del "Collection Framework".

Posteriormente se inicia la declaración de la Clase seguido de su método principal main. Se definen 5 instancias de String's, donde cada instancia recibe determinado valor; en

este caso no se definieron instancias de la Clase Producto para ilustrar la característica principal de un TreeSet (Descrita a continuación) .

Seguido son agregadas estas instancias de String's al TreeSet mediante el método add, para posteriormente imprimir el número de Objetos en el "Collection" mediante el método size.

Es definida una instancia Iterator la cual facilita la extracción de objetos en "Collections", para así extraer los valores del TreeSet e imprimirlos a pantalla.

Note que en la impresión del TreeSet ocurre lo siguiente: Los elementos son impresos por orden alfabético independientemente del orden de inserción, esta es una característica única de un TreeSet, además de no incluirse ningún elemento duplicado, en este caso se descartó una instancia del String"Manzanas" (Esta última característica pertenece a todo Set).

Y finalmente se eliminan todos los valores del "Collection" mediante el método clear.

as Clases del JDK que implementan la interfase Map son: HashMap, IdentityHashMap y WeakHashMap, mientras que la clase TreeMap implementa la sub-interfase SortedMap.

Código Fuente Agenda.java

import java.util.*;

public class Agenda

{

public static void main(String args[])

{

// Definir un HashMap

HashMap global = new HashMap();

// Insertar valores "key"-"value" al HashMap

global.put("Doctor", "(+52)-4000-5000");

global.put("Casa", "(888)-4500-3400");

global.put("Hermano", "(575)-2042-3233");

global.put("Hermana", "(421)-1010-0020");

global.put("Suegros", "(334)-6105-4334");

global.put("Oficina", "(304)-5205-8454");

global.put("Ana C.", "(756)-1205-3454");

global.put("Luis G.", "(55)-9555-3270");

global.put("Oficina 2", "(874)-2400-8600");

// Definir Iterator para extraer/imprimir valores

for( Iterator it = global.keySet().iterator(); it.hasNext();) {

String s = (String)it.next();

String s1 = (String)global.get(s);

System.out.println(s + " : " + s1);

}

// Definir un arreglo con valores determinados

String args1[] = {

"Casa", "Ana C.", "Luis G."

};

// Eliminar los valores del HashMap contenidos en el Arreglo anterior

for(int i = 0; i < args1.length; i++) {

global.remove(args1[i]);

}

System.out.println("---- Agenda Modificada ----");

// Definir Iterator para extraer/imprimir valores

for( Iterator it2 = global.keySet().iterator(); it2.hasNext();) {

String s = (String)it2.next();

String s1 = (String)global.get(s);

System.out.println(s + " : " + s1);

}

}

}

Como primer paso se importan las librerías de java.util.* donde se concentran la gran mayoría de las Clases del "Collection Framework".

Posteriormente se inicia la declaración de la Clase seguido de su método principal main. Se define una instancia de la Clase HashMap en donde son colocados 10 juegos de

valores (key-value) a través del método put.

Se declara una instancia de Iterator la cual facilita la extracción de objetos en "Collections", para así extraer los valores del HashMap e imprimirlos a pantalla.

Posteriormente se define un arreglo de String's, con tres valores idénticos a las llaves (key's) delHashMap.

Es definido un ciclo por el número de elementos en el Arreglo declarado anteriormente, en cada iteración de este ciclo se invoca el método remove del HashMap, para así eliminar el correspondiente juego de valores (key-value).

Y finalmente se define otro ciclo de extracción a través de Iterator, para imprimir a pantalla el contenido final del HashMap.

Codigo Fuente Agenda2.java

import java.util.*;

public class Agenda2

{

public static void main(String args[])

{

// Definir un TreeMap

TreeMap global = new TreeMap();

// Insertar valores "key"-"value" al HashMap

global.put("Doctor", "(+52)-4000-5000");

global.put("Casa", "(888)-4500-3400");

global.put("Hermano", "(575)-2042-3233");

global.put("Hermana", "(421)-1010-0020");

global.put("Suegros", "(334)-6105-4334");

global.put("Oficina", "(304)-5205-8454");

global.put("Ana C.", "(756)-1205-3454");

global.put("Luis G.", "(55)-9555-3270");

global.put("Oficina 2", "(874)-2400-8600");

// Definir Iterator para extraer/imprimir valores

for( Iterator it = global.keySet().iterator(); it.hasNext();) {

// Nótese que el orden del TreeMap refleja un orden descendente

// en sus elementos independientemente del orden de inserción.

// Debido al uso de String's esto refleja un orden alfabético

String s = (String)it.next();

String s1 = (String)global.get(s);

System.out.println(s + " : " + s1);

}

// Definir dos TreeMap's nuevos en base de determinadas restricciones

SortedMap am = global.subMap("A", "M");

SortedMap nz = global.tailMap("N");

System.out.println("---- Agenda A-M ----");

// Definir Iterator para extraer/imprimir valores

for( Iterator it2 = am.keySet().iterator(); it2.hasNext();) {

String s = (String)it2.next();

String s1 = (String)am.get(s);

System.out.println(s + " : " + s1);

}

System.out.println("---- Agenda N-Z ----");

for( Iterator it3 = nz.keySet().iterator(); it3.hasNext();) {

String s = (String)it3.next();

String s1 = (String)nz.get(s);

System.out.println(s + " : " + s1);

}

}

}

Esta Clase es muy similar a la anterior (Agenda.java) , solo que en este caso se emplea un TreeMap para agrupar los elementos, a continuación se describen las diferencias entre emplear este "Collection" y el inicial (HashMap).

Como primer paso se importan las librerías de java.util.* donde se concentran la gran mayoría de las Clases del "Collection Framework".

Posteriormente se inicia la declaración de la Clase seguido de su método principal main. Se define una instancia de la Clase TreeMap en donde son colocados 10 juegos de

valores (key-value) a través del método put. Es definida una instancia Iterator la cual facilita la extracción de objetos en

"Collections", para así extraer los valores del HashMap e imprimirlos a pantalla.

Note que en la impresión del TreeMap ocurre lo siguiente: Los elementos son impresos por orden alfabético independientemente del orden de inserción, esta es una característica única de un TreeMap.

Posteriormente se definen dos referencias de la interfase SortedMap , en la primera de éstas se colocan los resultados del método submap("A", "M") del TreeMap, dicho método indica un sub-grupo del TreeMapcon valores (key's) de la letra "A" hasta la letra "M"; la segunda interfase SortedMap contiene los resultados del método tailMap("N") que indica un sub-grupo del TreeMap con valores (key's) de la letra "N" en adelante.

Finalmente se definen dos ciclos de extracción a través de Iterator, para imprimir a pantalla el contenido final de estos últimos SortedMap's.NOTA : Recuerde que SortedMap es la interfase de donde proviene la Clase TreeMap, por esta razón es válida la equivalencia.

"Threads"Los diversos programas que han sido diseñados en este curso poseen una característica común que consiste en realizar sus tareas de una manera monolítica, sin embargo, en ciertos diseños surge la necesidad de fragmentar la ejecución de un programa en diversos sub-procesos o tareas, y es precisamente a través de "Threads" que se logra este comportamiento .

Un "Thread" permite distribuir el tiempo de ejecución y recursos asignados a determinados bloques de un programa, aunque abstracto en definición, una vez en contexto su comprensión es sencilla. La clásica ilustración de "Threads" es en programas con interfase gráfica, tomemos el caso de un "Browser"(Navegador) escrito en Java, mientras la ejecución del "Browser" es un programa en sí, dentro de éste se encuentran diversos "Threads" (Sub-procesos) latentes que nos permiten estar descargando un archivo o navegando un página simultáneamente, esto es, somos capaces de realizar varias tareas a pesar que el "Browser" es un programa monolítico.

Existen dos maneras para trabajar con "Threads" en una Clase Java :

Heredar ("Inherit") de la Clase Thread : Esta Clase proporcionada con el JDK contiene todos los mecanismos necesarios para crear y ejecutar "Threads".

Emplear la Interfase Runnable : Esta interfase también proporcionada con el JDK contiene los métodos necesarios para operar con "Threads", aunque claro esta, como toda otra interfase deben ser implementados sus métodos.

Los ejemplos descritos a continuación ilustran los conceptos más comunes de "Threads" en Java :

Código Fuente Camino.java

public class Camino extends Thread {

public Camino(String str) {

super(str);

}

public void run() {

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

System.out.println("Km " + i + " : " + getName());

try {

sleep((long)(Math.random() * 2500));

} catch (InterruptedException e) {}

}

System.out.println("Llego a la meta! " + getName());

}

}

Primeramente se define la Clase principal, la cual hereda ("inherit") su comportamiento dejava.lang.Thread, lo anterior nos otorga acceso a los métodos definidos en esta última Clase.

Posteriormente se define un constructor que toma como argumento un "String", esto permitirá nombrar el respectivo "Thread", ya que al llamar super(str) se esta invocando el constructor de la Clase Base heredada, en este caso Thread.

El método run() es el corazón de cualquier "Thread" y contiene las tareas de ejecución, en este caso se esta reimplementando ("Overriding") dicho método con la siguiente lógica: Se inicia un ciclo , donde en cada iteración se imprime el nombre del "Thread" (getName) y se "duerme" (sleep) el mismo "Thread" por un determinado tiempo, una vez terminado el ciclo se imprime otro mensaje a pantalla

A continuación se describe otra Clase que ejecuta la lógica de esta Clase diseñada con "Threads".

Código Fuente Competidores.java

public class Competidores {

public static void main (String[] args) {

new Camino("Conejo").start();

new Camino("Tortuga").start();

new Camino("Zorro").start();

}

}

Definida la Clase y su método principal (main()), se inicializan tres instancias de las Clase Caminodiseñada anteriormente.

A través del método start() declarado en cada instancia, se esta inicializando el "Thread" (Camino) en sí; el método start() corresponde a la Clase java.lang.Thread al igual que run()

Resultados

Al ejecutar la Clase anterior (Competidores) se estarían ejecutando tres "Threads" simultáneamente, inclusive por los resultados de ejecución podrá notar que el acceso se lleva acabo de esta manera.

Otro detalle interesante al ejecutar esta Clase es que los resultados siempre son distintos, esto se debe a que los "Threads" se ejecutan conforme existen recursos del Sistema disponibles, sin embargo, vale mencionar que existen mecanismos más avanzados para manejo de "Threads" que permiten asignar prioridades o interrumpir "Threads" en determinado momento.

A continuación se describen una serie de Clases que hacen uso del concepto de Sincronización en "Threads" .

Código Fuente CampoDeTiro.java

public class CampoDeTiro {

public static void main(String[] args) {

Pistola arma = new Pistola();

Cargar c = new Cargar(arma, 1);

Descargar d = new Descargar(arma, 1);

c.start();

d.start();

}

}

Primeramente se define una Clase ejecutable que contiene las declaraciones necesarias para invocar las Clases diseñadas con "Threads".

Una vez definido el método principal main se genera una instancia de la Clase Pistola. Posteriormente se generan instancias de las Clases Cargar y Descargar, nótese que

estas declaraciones toman como parámetro de entrada la instancia de la Clase Pistola definida con anterioridad.

Finalmente, se invoca el método run() sobre las instancias de las Clases Cargar y Descargar, dicho método corresponde al clásico método necesario para activar "Threads".

Código Fuente Pistola.java

public class Pistola {

private int cartucho;

private boolean enposicion = true;

public synchronized void disparar(int cartucho) {

while (enposicion == false) {

try {

// Esperar a apuntar

wait();

} catch (InterruptedException e) { }

}

enposicion = false;

notifyAll();

}

public synchronized void apuntar() {

while (enposicion == true) {

try {

// Esperar a disparar

wait();

} catch (InterruptedException e) { }

}

enposicion = true;

notifyAll();

}

}

Definida la Clase, se declaran dos campos ("fields"), uno de tipo int y otro boolean . Posteriormente se definen dos métodos llamados disparar y apuntar, estos métodos

contienen el calificador synchronized empleado en la sincronización de "Threads".

Significado/Uso de synchronized

Cuando es empleado el vocablo synchronized se esta indicando una zona restringida para el uso de "Threads", esta zona restringida para efectos prácticos puede ser considerada un candado("lock") sobre la instancia del Objeto en cuestión.

Lo anterior implica que si es invocado un método synchronized únicamente el "Thread" que lo invoca tiene acceso a la instancia del Objeto, y cualquier otro "Thread" que intente accesar esta misma instancia tendrá que esperar hasta que sea terminada la ejecución del método synchronized.

Debido a que el uso de synchronized esta directamente relacionado con "Threads", dentro de este tipo de métodos también es posible definir otra serie de mecanismos que permiten una coordinación efectiva de "Threads", esta serie de mecanismos reside en los métodos wait, notify y notifyAll.

El método wait permite paralizar temporalmente la ejecución de un "Thread" bajo determinada circunstancia, mientras los métodos notify y notifyAll permiten enviar notificaciones individuales y a todo "Thread" respectivamente, sobre la terminación de un "Thread" especifico.

En ambos métodos (disparar y apuntar) se evalúa el valor del campo enposicion, y en base al valor de éste se entra en un bloque try/catch invocando el método wait de "Threads", para posteriormente modificar el campo y finalmente invocar el método notifyAll para notificar a todo "Thread" la terminación del método y por ende la liberación ("lock") sobre la instancia del Objeto.

Codigo Fuente Cargar.java

public class Cargar extends Thread {

private Pistola arma;

private int cartucho;

public Cargar(Pistola arma, int cartucho) {

this.arma = arma;

this.cartucho = cartucho;

}

public void run() {

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

arma.apuntar();

System.out.println("Apuntar #" + this.cartucho

+ " bala: " + i);

}

}

}

Se define la Clase principal que hereda ("inherit") el comportamiento de java.lang.Thread .

Posteriormente se definen dos campos, seguidos de un constructor empleado en la asignación de estos valores.

Finalmente se reimplementa ("Override") el método run(), para que contenga una ciclo, así como invocar el método apuntar() (De la Clase Pistola) e imprimir un mensaje a la consola.

Código Fuente Descargar.java

public class Descargar extends Thread {

private Pistola arma;

private int cartucho;

public Descargar(Pistola arma, int cartucho) {

this.arma = arma;

this.cartucho = cartucho;

}

public void run() {

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

arma.disparar(i);

System.out.println("Descargar #" + this.cartucho

+ " bala: " + i);

}

}

}

Se define la Clase principal que hereda ("inherit") el comportamiento de java.lang.Thread .

Posteriormente se definen dos campos, seguidos de un constructor empleado en la asignación de estos valores.

Finalmente se reimplementa ("Override") el método run(), para que contenga una ciclo, así como invocar el método disparar() (De la Clase Pistola) e imprimir un mensaje a la consola.

Resultados

Al ejecutar la Clase CampoDeTiro se estarían ejecutando dos "Threads" simultáneamente, sin embargo, a diferencia del ejemplo descrito anteriormente notará que el orden de ejecución será llevado acabo de manera ordenada, esto es, el resultado producido siempre será idéntico.

Lo anterior se debe al uso del vocablo synchronized y los diversos eventos disponibles para manipulación de "Threads" (notifyAll y wait).

Además del mecanismo empleado en los ejemplos anteriores para utilizar "Threads", existe otra manera de emplearlos en el lenguaje Java y ésta es a través de la interfase java.lang.Runnable; observe el siguiente fragmento :

public class Reloj extends Applet implements Runnable {

}

El fragmento anterior ilustra la declaración inicial de un Applet que hará uso de "Threads", en este caso resulta necesario heredar ("inherit") el comportamiento de otra Clase (Applet), por lo que el uso dejava.lang.Thread queda restringido al no poder heredar vía extends más de una Clase.

Lo anterior aplica como regla general en todo uso de "Threads": Mientras no sea necesario heredar ("inherit") el comportamiento de otra Clase se debe emplear java.lang.Thread, sin embargo, si se requiere heredar el comportamiento de otra Clase y tener acceso a "Threads" es necesario emplear la interfasejava.lang.Runnable para garantizar la implementación correcta de un "Thread".

Garbage CollectionEl proceso de "Garbage Collection" mencionado en las primeras secciones de este curso, se refiere a la limpieza de instancias (Objetos) las cuales han dejado de ser utilizadas en un programa Java. Este proceso llevado acabo directamente por el JVM ("Java Virtual Machine") permite liberar recursos, en su mayoría de memoria ("RAM") para ser reutilizados por el sistema, sin embargo, este proceso trae consigo dos preguntas :

Que instancias/objetos son elegidos y liberados por el proceso de "Garbage Collection" ? : Toda instancia/referencia que sea asignada un valor de null es elegible para "Garbage Collection", si la instancia/referencia no tiene un valor de null ésta no es elegible para "Garbage Collection". Ahora bien, nótese el énfasis en elegible, esto implica que aunque la instancia/referencia sea asignada con un valor de null ésta no será liberada ("Garbage Collected") inmediatamente, sino hasta que el proceso de "Garbage Collection" sea iniciado.

Quien invoca el proceso de "Garbage Collection" ? : El JVM ("Java Virtual Machine") se hace cargo de iniciar el proceso de "Garbage Collection" únicamente cuando éste (según sus algoritmos internos) determine que su memoria esta en proceso de agotamiento, es hasta entonces que es liberada la memoria de las instancias/referencias que se han declarado como elegibles para "Garbage Collection".

System.gc y Runtime.gc

Existe un método llamado gc() dentro de las Clases java.lang.System y java.lang.Runtime cuyas iniciales significan "Garbage Collection" y puede ser llamado directamente dentro de un programa Java.

Sin embargo, a pesar de su nombre, el hecho de invocar este método directamente (en cualquiera de las Clases) no implica que se realice el proceso de "Garbage Collection" inmediatamente, lo único que logra es expeditar (acelerar) los mecanismos para iniciar el proceso de "Garbage Collection"; el momentopreciso en que es invocado el proceso de "Garbage Collection" depende del JVM ("Java Virtual Machine"), según fue descrito anteriormente.

"Assertions"Un "Assertion" o aseveración es un mecanismo empleado en lenguajes de programación para garantizar el cumplimiento de ciertas reglas/normas al momento de ejecutarse un programa. Mientras el proceso de compilación garantiza que la sintaxis escrita en un programa sea la correcta , el uso de "Assertions" es empleado para garantizar que determinados datos introducidos al flujo de un programa cumplan con ciertos requisitos.

Ahora bien, aunque también pudieran ser utilizados bloques try/catch para capturar este tipo de errores, el uso de bloques try/catch implica anticipar un tipo de error (Clase) además de escribir código extenso, esto a diferencia de una aseveración ("Assertion") donde es posible definir una condición específica que debe cumplir un programa para proceder con su ejecución. Los siguientes ejemplos demuestran el uso de este mecanismo para programas Java

Existen dos tipos de sintaxis para emplear aseveraciones ("assertions") en Java:

assert (Expresión con resultado verdadero|falso [boolean]) : (Expresión Descriptiva del error) ;

"o"

assert (Expresión con resultado verdadero|falso [boolean]) ;

Ambas declaraciones inician con la palabra reservada assert utilizada para definir una aseveración en Java.

Posteriormente se debe definir una expresión que dará como resultado una expresión booleana (verdadero o falso), dicha expresión es precisamente la que será aseverada por Java, si esta expresión resulta ser verdadera el ciclo de ejecución del programa continuará normalmente, sin embargo, si resulta falsa la expresión, la ejecución del programa será interrumpida indicando el error de aseveración ("assertion").

La única diferencia que existe entre las declara iones antes mencionadas, es que la primera de éstas define una expresión adicional que permite agregar información extra acerca de la aseveración levantada por el programa, dicha declaración puede ser desde una variable hasta un método que de como resultado un valor no nulo (void), este resultado es convertido a un String que es pasado al constructor de la aseveración para ser desplegado como información adicional al momento de ejecución; la segunda declaración de aseveración simplemente verifica la validez de la primer expresión sin proporcionar detalles específicos del error.

Código Fuente Sumatoria.java

import java.util.*;

public class Sumatoria {

public int sumatoria(List numeros) {

int suma = 0;

for(Iterator it = numeros.iterator(); it.hasNext();) {

suma += ( (Integer)it.next() ).intValue();

}

assert suma <= 500: suma;

return suma;

}

public static void main(String[] args) {

List numeros = new ArrayList();

int inicial = 0;

// Convertir Dato de Entrada a primitivo int

// Bloque try/catch utilizado en caso de no ser posible conversion

try {

inicial = Integer.parseInt(args[0]);

} catch (NumberFormatException ex) {

System.out.println("El dato proporcionado no puede ser convertido a un numero");

System.exit(-1);

} catch (Exception ex) {

System.out.println("Ocurrio un error " + ex);

System.exit(-1);

}

// Ciclo para llenar Lista

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

numeros.add(new Integer(inicial+i));

}

Sumatoria factorial = new Sumatoria();

int resultado = factorial.sumatoria(numeros);

System.out.println("La suma de los 10 numeros a partir del numero proporcionado[" + inicial + "] es : " + resultado);

}

}

Una vez definida la clase principal se declara un método llamado sumatoria que define una aseveración para los datos introducidos en él.

El método sumatoria toma como dato de entrada una Lista de números sobre la cual se realiza una iteración sumando cada uno de sus elementos. Posteriormente se define una aseveración ("assertion") sobre el valor de esta suma, si dicho valor resulta menos a 500 unidades el programa continua en ciclo normal de ejecución, en caso contrario se levanta una aseveración con el valor determinado dentro del ciclo.

Dentro del método principal (main) primeramente se define un bloque try/catch que intenta convertir el argumento del programa a un número entero (int).Si la conversión es exitosa, mediante un ciclo se colocan este valor y los 10 consiguientes en un Lista.

System.exit()

El método exit de la clase System implica que un programa debe ser terminadoinmediatamente. Aunque el uso de bloques try/catch y aseveraciones ("assertions") permiten que un programa llegue a su fin prematuramente y bajo cierto control, el uso deSystem.exit() implica una terminación inmediata.

En el ejemplo anterior, aunque el bloque try/catch es suficiente para terminar la ejecución del programa en caso de no poderse convertir el valor introducido a un entero, éste no es lo suficiente para terminar la ejecución del método main, de no ser por la llamada a System.exit(-1) el método imprimiría a pantalla la última declaración de este método.

El valor -1 del método es únicamente una convención de sintaxis, ya que puede tomar cualquier valor de número entero produciéndose los mismos resultados.

Posteriormente, se define una instancia de la clase Factorial seguido de la invocación del métodosumatoria que toma como argumento la lista generada previamente.

Finalmente se imprime a pantalla el resultado obtenido del método sumatoria.

El siguiente ejemplo también hace uso de aseveraciones invocando un método en caso de no cumplirse la aseveración.

Codigo Fuente Invernadero.java

public class Invernadero {

private String[] citricos = {"Naranjo","Limon","Mandarina","Toronja","Naranjo"};

private int[] alturas = {7, 4, 6, 7, 18};

public String controlDeCalidad() {

for (int i = 0; i < alturas.length; i++) {

assert ( alturas[i] > 5 && alturas[i] > 20) : determinarArbol(i);

}

return "Control de Calidad OK";

}

public String determinarArbol(int noDeArbol) {

int alturaFueradeControl = alturas[noDeArbol];

String tipoDeArbol = citricos[noDeArbol];

String resultado = "Control de Calidad no se cumple en un " + tipoDeArbol + " con " + alturaFueradeControl + " de altura";

return resultado;

}

public static void main(String[] args) {

Invernadero arboles = new Invernadero();

System.out.println(arboles.controlDeCalidad());

}

}

Como primer paso se definen dos campos en forma de arreglos para String's con valores predeterminados.

Posteriormente se define un método llamado controlDeCalidad dentro del cual se define un ciclo que itera sobre los valores del arreglo altura, en cada iteración se ejecuta una aseveración para verificar que el valor de altura cumpla con ciertos requisitos, en caso de no cumplirse la aseveración ("assertion") se invoca el método determinarArbol con el número de iteración en el que no se cumplió la aseveración.

El método determinarArbol toma como dato de entrada un número entero, dicho número representa la iteración en la cual fue interrumpido el método controlDeCalidad y por consiguiente el elemento del arreglo alturas que no cumplió la condición. Una vez en el método se extraen los valores de los arreglosalturas y citricos en base al número proporcionado, para posteriormente conformar un String el cual es retornado como resultado, en este caso, al argumento de error en la aseveración.

Finalmente se define el método principal (main) que genera una instancia de la clase Invernaderoseguido de la invocación del método controlDeCalidad que realiza la verificación de los valores proporcionados en los arreglos definidos como campos.

En la siguiente sección se describen las medidas que deben ser tomadas para habilitar aseveraciones ("assertions") al momento de compilar y ejecutar programas.

En Java, el uso de "Assertions" es una nueva adición a partir del JDK 1.4, por lo que su uso requiere de ciertas medidas al compilar y ejecutar programas para garantizar compatibilidad con versiones anteriores. A continuación se describen los diversos pasos que deben tomarse para activar aseveraciones al momento de compilar y ejecutar un programa.

El colocar aseveraciones ("assertions") en código es sólo el primer paso para que éstas sean interpretadas en la ejecución de programas, a continuación se describen los pasos que deben tomarse al compilar y ejecutarse programas que incluyen aseveraciones.

Compilación

Como pre-requisito se debe emplear un JDK en su versión 1.4, lo anterior ya que todo JDK previo a esta versión no incluye apoyo para aseveraciones ("assertions").

Además de emplear un JDK 1.4, es necesario que al momento de realizar la compilación de clases se proporcione un flag de compilación para indicar que se trata de código diseñado para las funcionalidades de un JDK 1.4 y por ende de "Assertions".

$ javac -source 1.4 Sumatoria.java

El indicar este flag al compilar código que incluye aseveraciones es obligatorio para garantizar compatibilidad con versiones anteriores de Java. Puesto que el uso de aseveraciones es un concepto antiguo en lenguajes de programación, es factible que diseños antiguos de Java incluyan en su estructura aseveraciones y con esto empleen la ya palabra reservada assert.

Vale mencionar que Sun Microsystems ha indicado que el uso de éste flag es temporal mientras se estabiliza el uso de aseveraciones y es actualizado el código escrito en versiones anteriores.

Ejecución

Para que el código compilado con aseveraciones sea evaluado con estas estructuras es necesario emplear otro flag al momento de ejecutar programas:

$ javac -ea Sumatoria

La declaración anterior activa cualquier aseveración que se encuentre presente en el código de la ClaseSumatoria; aunque el uso de este flag puede parecer innecesario, su uso se debe

a que en diversas ocasiones el uso de aseveraciones ("assertions") es empleado únicamente como mecanismo de calidad en etapas de desarrollo y en versiones de producción resultan innecesarias estas mismas aseveraciones. Es por esto que a través de este flag se ofrece la facilidad de activar aseveraciones que generalmente incrementan el tiempo de ejecución en un programa.

Algunas variaciones para habilitar aseveraciones a nivel de librería ("package") son descritos a continuación :

$ javac -ea:com.osmosislatina.matematicas Sumatoria

$ javac -ea:com.osmosislatina.matematicas -da:com.osmosislatina.matematicas.impresion Sumatoria

La primer declaración ejecutaría la Clase Sumatoria con aseveraciones activadas únicamente en la libreríacom.osmosislatina.matematicas.

La segunda declaración ejecutaría la Clase Sumatoria con aseveraciones activadas en la libreríacom.osmosislatina.matematicas,sin embargo, desactivando todas aquellas incluidas en la libreríacom.osmosislatina.matematicas.impresion.

Palabras Reservadas

En Java al igual que otros lenguajes de computo existen varias palabras que se encuentran reservadas para uso exclusivo en sus estructuras internas, lo anterior implica que estos

términos no pueden ser utilizados para nombres de campos, métodos, clases u otras estructuras de datos; la mayoría de estas palabras han sido descritas en el contenido del presente curso, sin embargo, a continuación se ilustra una tabla concisa que contiene dichas palabras:

abstract assert boolean break byte case catch

char class const * continue default do double

else extends final finally float for goto *

if implements import instanceof int interface long

native new null package private protected public

return short static strictfp super switch synchronized

this throw throws transient try void volatile

while

* Aunque no utilizada por el lenguaje Java, es palabra reservada.

goto y const: Son identificadores ampliamente utilizados en C++, en Java se encuentran restringidas estas palabras para facilitar la detección de errores en sintaxis de este tipo.

true, false y null: Aunque aparentemente pudieran ser consideradas palabras reservadas, éstas representan simplemente literales (valores) boolean's y nulo; lo anterior es importante únicamente para efectos de exámenes de certificación Java.

Java 5 / JDK 5 .

La nueva versión de Java -- Java 5 / JDK 5 -- ha incorporado una serie de funcionalidades y sintaxis que tendrán un efecto directo sobre el tiempo de desarrollo en aplicaciones de este lenguaje, incrementado así su productividad como desarrollador.

En forma de atajos, mayores salvaguardas para detectar errores, o simplemente en la simplificación de escribir código, los principales cambios se encuentran enunciados en las siguientes secciones

Clases genéricas para el "Collections Framework" y Arreglos.

El uso de clases genéricas es una funcionalidad que permite garantizar el tipo de elementos colocados en una colección Java -- esto es, una de las clases del "Collections Framework" -- y a su vez evitar el proceso explicito de extracción necesario para manipular los objetos que en él se contienen.

Observe el uso de un ArrayList -- parte del "Collections Framework" -- sin utilizar clases genéricas :

import java.util.*;

public class Idiomas

{

public static void main(String args[])

{

// Definir un ArrayList

List lenguajes = new ArrayList();

lenguajes.add("ingles");

lenguajes.add("castellano");

lenguajes.add("aleman");

lenguajes.add(new StringBuffer("Error solo hasta ejecutarse programa!"));

// Definir Iterator para extraer/imprimir valores

for( Iterator it = lenguajes.iterator(); it.hasNext(); ) {

String x = (String)it.next();

System.out.println(x);

}

}

}

Primeramente observe que en el ArrayList son insertados una serie de String's y al final es colocado unStringBuffer, esto tipo de error no es detectado por el compilador, inclusive la única manera en que es detectada esta falla es hasta que se intentan accesar los valores del mismo ArrayList, una detección simple en esta clase sencilla, pero un proceso que se puede tornar complicado si los valores son accesados en diversas librerías. Además, note que al momento de iterar sobre la colección de objetos también es necesario hacer un "cast" explicito hacia String sobre todo valor.

Ahora intente compilar esta misma clase utilizando la versión JDK 5, y observará un mensaje como el siguiente :

Note: Idiomas.java uses unchecked or unsafe operations.

Note: Recompile with -Xlint:unchecked for details.

El compilador del JDK 5 detecta que la clase contiene operaciones inseguras, en este caso, no definir el tipo de objetos esperados en la clase del "Collections Framework". Si recompila la clase con la opción -Xlint, observará una lista detallada de las operaciones consideradas inseguras por Java 5.

Ahora observemos la misma clase modificada para utilizar clases genericas :

import java.util.*;

public class Idiomas

{

public static void main(String args[])

{

// Definir un ArrayList

List<String> lenguajes = new ArrayList<String>();

lenguajes.add("ingles");

lenguajes.add("castellano");

lenguajes.add("aleman");

//lenguajes.add(new StringBuffer("Error detectado al compilar"));

// Definir Iterator para extraer/imprimir valores

for( Iterator<String> it = lenguajes.iterator(); it.hasNext(); ) {

String x = it.next(); // No existe "cast" explicito

System.out.println(x);

}

}

}

La nomenclatura utilizada por clases genéricas es <Tipo de Clase>, misma que debe ser utilizada en la referencia de la colección, al inicializar la clase y al momento de extraerla en el iterador. Al emplear este mecanismo, cualquier intento de colocar un tipo de objeto distinto aquel definido en la clase genérica, resulta en un error al momento de compilar.

El proceso para utilizar clases genéricas en listas de valores (key-value lists) -- interfase Map -- es muy similar al de listas, observemos el siguiente ejemplo:

import java.util.*;

public class Agenda

{

public static void main(String args[])

{

// Definir un HashMap

Map<String, Object> global = new HashMap<String, Object>();

// Insertar valores "key"-"value" al HashMap

global.put("Doctor", "(+52)-4000-5000");

global.put("Casa", "(888)-4500-3400");

global.put("Hermano", "(575)-2042-3233");

// Definir Iterator para extraer/imprimir valores

for( Iterator<String> it = global.keySet().iterator(); it.hasNext();) {

String s = it.next();

Object s1 = global.get(s);

System.out.println(s + " : " + s1);

}

}

}

La nomenclatura de < > permanece idéntica, sin embargo, debido a que se trata de listas de valores, es agregado primeramente el tipo de clase para el identificador ("key") y posteriormente el tipo de clase para el valor. Nótese también que como parámetro de clase genérica es utilizado la clase universal Object, esto es un indicador de la flexibilidad con que pueden ser empleadas clases genéricas en un diseño, pudiendo ser cualquier tipo de clase para restringir el tipo de objetos colocados en una colección.

NOTA: Tome en cuenta que al utilizar la clase Object como clase genérica estaría perdiendo todas las salvaguardas ofrecidas por esta funcionalidad, lo anterior se debe a que todo objeto es derivado directamente de esta clase universal. Al definir una colección con clase genérica Object no recibiría ningún tipo de error ya que todo objeto proviene de la jerarquía de herencias en Object.

Auto-boxing y auto-unboxing, entre primitivos y objetos.El uso de auto-boxing y auto-unboxing tiene sus raíces en el uso de primitivos Java. A pesar que la mayoría de los diseños en Java giran alrededor de manipular objetos, el uso de primitivos como boolean, char , byte, int , long, float y double permanecen en amplio uso.

Un caso típico donde son utilizados primitivos es aquel donde se realizan operaciones matemáticas, esto se debe en parte a la sencillez de generar un primitivo, ya que no requiere inicialización de objeto mediantenew, mismo factor que favorece eficiencia de ejecución comparado con usar un objeto. Sin embargo, a pesar de las ventajas presentadas al utilizar primitivos, existen casos -- como el de guardar primitivos en una colección de objetos -- que requieren conversiones continuas de un primitivo hacia un objeto y viceversa.

Este tipo de conversiones en versiones previas a Java 5, son realizadas mediante métodos proporcionados en clases "Wrappers" de cada primitivo, un proceso mecánico y tedioso, a pesar de ser obvio el comportamiento de conversión deseado. Ante estos procesos laboriosos de conversión, fueron integrados los mecanismos de auto-boxing y auto-unboxing para permitir la conversión de valores primitivos hacia sus respectivos objetos "Wrappers" de manera directa y sin mayores complicaciones.Observemos un ejemplo.

public class Carrera {

public static void main(String[] args) {

// Contador de ciclos como objeto con asignacion primitivo

Integer contador = 0;

// Parametros

double kilometros = 2.5;

int tiempo = 2;

while(true) {

// Utilizando auto-incremento sobre objeto Integer

System.out.print("Vuelta: " + contador++ + " ");

// Operacion entre objeto Integer y primitivo double

System.out.print("Distancia: " + contador*kilometros + " ");

// Operacion entre objeto Integer y primitivo int

System.out.println("Tiempo: " + contador*tiempo + " ");

// Comparacion sobre objeto Integer

if ( contador > 5) break;

}

}

}

Nótese que en la clase anterior no fue necesario hacer ningún tipo de conversión explicita entre un primitivo y un objeto en varias ocasiones. La primera de estas es la asignación de un entero (int) directamente a un objeto Integer sin utilizar el vocablo new. Posteriormente dentro del ciclo while es aplicado el operador de auto-incremento directamente al mismo objeto Integer, además de ser realizadas dos operaciones sobre el objeto en sí: una multiplicación con un primitivo double y otra con un primitivoint. Finalmente es realizada una operación de comparación sobre el objeto Integer. Observemos otro ejemplo de sumatoria realizado en una colección de números Double :

import java.util.*;

public class Sumatoria

{

public static void main(String args[])

{

List<Double> calificaciones = new ArrayList<Double>();

// Agregando primitivo double directamente en collecion de Double

calificaciones.add(9.20);

calificaciones.add(8.41);

calificaciones.add(8.92);

calificaciones.add(9.31);

calificaciones.add(7.20);

// Inicializar variable sumatoria

double sumatoria = 0.0;

for( Iterator<Double> it = calificaciones.iterator(); it.hasNext(); ) {

// Operacion entre objeto Double y primitivo double

sumatoria += it.next();

}

System.out.println("Promedio " + (sumatoria/calificaciones.size()) );

}

}

El primer uso de auto-boxing en la clase anterior se lleva acabo al asignar los primitivos doubledirectamente al metodo add() de la lista, de no ser por esta funcionalidad automática de conversión, se hubiera presentado un error ya que la lista espera un objeto de tipo Double como dato de entrada. Posteriormente, se lleva acabo un proceso de auto-unboxing al realizar una sumatoria sobre los elementos de la colección, este proceso se lleva acabo porque la variable sumatoria es un primitivo tipo doublemientras los elementos extraídos se encuentran en formato de objeto Double.

Si aun no se convence de la ventajas de auto-boxing y auto-unboxing, intente compilar las dos clases presentadas en esta sección con un versión previa a Java 5 y notará la sintaxis extensa que debe agregar para que funcionen dichas clases. También puede consultar el uso de "Wrappers" y primitivos en Java   de nuestro curso, donde se describe el proceso de conversión manual entre primitivos y objetos, aplicable a versiones JDK 1.4 y anteriores.

Ciclos de iteración simplificados.

Los ciclos de iteración se encuentran entre las estructuras más comunes en programación, para Java 5 se ha incorporado una sintaxis nueva para simplificar su uso.

Vayamos directo a ilustrar un ejemplo con esta funcionalidad :

public class Numeros {

public static void main(String[] args) {

int[] factorial = new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };

// Nueva sintaxis ciclo for en Java 5

for (int digito : factorial) {

System.out.println(digito);

}

}

}

Nótese la manera en que esta estructurada la iteración sobre el arreglo de números enteros (int's) :

for ( [Tipo de referencia] [Nombre de referencia dentro del ciclo] : [ Arreglo o colección para iterar] ) {

}

Este caso de inspeccionar o procesar todos los elementos dentro de un arreglo o colección del "Collections Framework" es clásico en programas Java. Mediante esta sintaxis se evita la definición de un contador y condicionales para terminar ciclos, simplemente basta declarar el arreglo o colección en sí y una variable, para que cada iteración sea asignado un elemento del grupo a dicha variable para ser procesada.Veamos otro uso de este ciclo for en una lista :

import java.util.*;

public class Idiomas

{

public static void main(String args[])

{

// Definir un ArrayList

List<String> lenguajes = new ArrayList<String>();

lenguajes.add("ingles");

lenguajes.add("castellano");

lenguajes.add("aleman");

// Nueva sintaxis ciclo for en Java 5

for( String tipo : lenguajes ) {

System.out.println(tipo);

}

}

}

De la misma manera en que es utilizado este ciclo en arreglos es aplicado a clases del "Collections Framework", al inspeccionar los elementos dentro de la colección lenguajes, es definida la colección en sí y una variable donde se coloca un elemento del grupo en cada iteración para ser procesado. Nótese la ausencia de la clase Iterator y sus métodos hasNext() y next() comunmente empleados en este tipo de ciclos.

Limitaciones de for en Java 5Aunque esta sintaxis de ciclo for resulta igualmente válida y concisa para procesar elementos en un arreglo o colección, posee las siguientes limitaciones :

Incapacidad de acceso al contador : En determinadas situaciones resulta conveniente accesar el contador del ciclo, ya sea para fines de aplicar condicionales o salir de él prematuramente, al utilizar el ciclo for con la nueva sintaxis de Java 5, no se tiene acceso a esta información.

Eliminar o agregar elementos : Otra limitante del ciclo for en Java 5 es la inhabilidad de agregar o eliminar objetos dentro de un arreglo o colección de manera selectiva durante el proceso de iteración.

Si requiere emplear alguna de estas funcionalidades en su programa, simplemente tendrá que depender de la sintaxis común y antiguamente conocida en versiones previas a Java 5.

Anotaciones en código fuente -- meta-datos.Anotaciones en código fuente es otra de las funcionalidades incorporadas en Java 5. A través de anotaciones se tiene la posibilidad de utilizar información complementaria colocada directamente en código fuente -- meta datos -- al momento de compilar o ejecutar una clase Java.

El mecanismo de colocar información dentro del código fuente para fines distintos al de ejecución no es algo nuevo para Java, el proceso para generar documentación de clases emplea este mismo mecanismo y ha estado disponible desde las primeras versiones Java [ Observe el proceso para generación de documentación en Java], sin embargo, lo que resulta importante de esta funcionalidad en Java 5 es la capacidad de utilizar dicha información para fines de indicar instrucciones al compilador o JVM ("Java Virtual Machine").

En el JDK 5 se encuentran tres anotaciones que pueden ser utilizadas directamente :

Anotación Función

Override Utilizado para indicar que el método ha modificado su comportamiento ("override") sobre su superclase.

Deprecated Utilizado para indicar que el uso de determinado elemento no es recomendable o ha dejado de ser actualizado.

SuppressWarnings Permite suprimir mensajes del compilador relacionados con advertencias/avisos.

Debido a la misma gama de anotaciones que pueden ser requeridas en distintas circunstancias, el JDK ofrece los mecanismos para crear anotaciones de cualquier tipo e integrarles lógica personalizada, inclusive ya han empezado a surgir librerías de anotaciones producidas por terceros para uso en proyectos con requerimientos particulares. Sin embargo, debido a que la creación de anotaciones personalizadas implica un proceso

extenso e involucrado aquí no será explorada su creación, en lugar, será ilustrado como utilizar las anotaciones base del JDK.

import java.util.*;

class Instrumento {

public void tocar() {

System.out.println("Instrumento.tocar()");

}

public String tipo() {

return "Instrumento";

}

/**

* Metodo suplantado ("deprecated") en favor de afinarDeFabrica

* @deprecated Utilizar afinarDeFabrica()

*/

@Deprecated public void afinar() {}

public void afinarDeFabrica() {}

}

class Guitarra extends Instrumento {

@Override

public void tocar() {

System.out.println("Guitarra.tocar()");

}

@Override

public String tipo() { return "Guitarra"; }

@Override

public void afinar() {}

}

El grupo de clases anteriores demuestra como la anotación @Override nos ofrece una salvaguarda para detectar si la clase derivada efectivamente ha reimplementado ("override") correctamente los métodos de su superclase. Al colocar esta anotación en los métodos de la clase derivada, el compilador verifica que dichos métodos existan en la superclase, de esta manera, si se escribe incorrectamente el método en la clase derivada o se tiene una jerarquía de herencia compleja, este tipo de error que puede ser difícil de encontrar una vez en ejecución el programa, es detectado al momento de compilación.

La anotación @Deprecated permite marcar un método como obsoleto o antiguo y que el usuario de dicha superclase sea notificado de esta circunstancia al momento de compilación . En versiones anteriores a Java 5, la única manera en que un usuario podía percatarse de utilizar una librería o método marcado como "deprecated" era mediante la documentación

de dicha librería, ahora a través de la anotación @Deprecatedes posible detectar este hecho al momento de compilar.

Uso de Enumeraciones.Una enumeración es una estructura de datos diseñada para agrupar información estática y facilitar su acceso de otras clases. Esta funcionalidad ya presente en una serie de lenguajes de programación, comoC++ y Perl, esta siendo debutada en el mundo Java hasta esta versión 5.

Como sería normal esperar, ante la falta de apoyo directo en el JDK para enumeraciones, muchos desarrolladores familiarizados con esta técnica han creado sus propias maneras de simular el comportamiento. Ahora veamos si usted es uno de éstos, y de ser así, como incorporar la estructura formal de una enumeración a un programa Java, o de lo contrario, en que casos resulta apropiado utilizarla.

La característica común de los datos contenidos en las enumeraciones anteriores -- y cualquier otra desde luego -- es que son grupos de datos relacionados entre sí que no son modificados a lo largo de la ejecución de un programa.

Si ha trabajado ampliamente con Java, una enumeración es el equivalente grupal a definir un campo como :public static final, esto es, un dato con acceso publico, con una sola instancia (static) y que no cambia su valor al ejecutarse el programa (final).

Vale mencionar que una enumeración posee el mismo comportamiento que cualquier otra estructura Java de primer nivel como lo sería una clase o interfase : puede ser declarada en un archivo por si sóla y tiene la capacidad de pertenecer a una librería ("package") de manera independiente.

A pesar de la aparente similitud que tiene una enumeración con un arreglo o colección, difieren considerablemente, veamos un ejemplo practico donde sea utilizada una enumeración :

public class Piloto {

private Auto auto;

private Sexo sexo;

public Piloto(Auto auto, Sexo sexo) {

this.auto = auto;

this.sexo = sexo;

}

public static void main(String[] args) {

Piloto piloto1 = new Piloto(Auto.FORD,Sexo.M);

Piloto piloto2 = new Piloto(Auto.CHEVY,Sexo.M);

Piloto piloto3 = new Piloto(Auto.BMW,Sexo.F);

}

}

La clase anterior define dos campos : auto y sexo, basados en las enumeraciones Auto y Sexorespectivamente. Posteriormente, se define un constructor que asigna los valores de estos campos a cada instancia de la clase.

Dentro del método principal (main) de la clase , se lleva acabo la generación de instancias donde los respectivos campos son inicializados a partir de los valores en la enumeración. Note la sintaxis de asignación : Nombre_Enumeracion.Valor_Explicito.

Como puede observar, este tipo de asignación llevado acabo a través de enumeraciones, permite salvaguardar la asignación de datos estáticos en clases Java, de intentarse asignar un valor no encontrado dentro de la enumeración el compilador generaría un error. Además de este apoyo para detección de errores al momento de compilar, una enumeración también permite concentrar listas de datos que pueden ser reutilizadas en diversos programas, tales como los datos enunciados anteriormente, listas de ciudades, nombres, países u otro tipo información.

Capacidad para importar clases estáticas.Al hacer uso de librerías en programas Java, el mecanismo para tener acceso a ellas es a través del vocablo import, esto es, si deseamos hacer uso de las interfases o clases del "Collections Framework" debemos colocar la declaración : import java.util.* en la parte superior de la clase.

Aunque este proceso de acceso es estándar para la mayoría de clases Java, en versiones previas a Java 5 no es posible utilizarlo en un tipo de clase : Clases estáticas. El acceso a este tipo de clases en versiones del JDK 1.4 y anteriores requiere que éstas mismas sean declaradas explícitamente dentro del código central del programa. Observemos un ejemplo.

public class Raiz {

public static void main(String[] args) {

try {

Double numero = new Double(args[0]);

System.out.println("La raiz de " + numero + " es : " + Math.sqrt(numero));

} catch (Exception ex) {

System.err.println("Error: " + ex);

}

}

}

Note como la clase hace uso de tres métodos pertenecientes a clases estáticas, y en todas estas declaraciones es necesario indicar el nombre completo de la clase. Ahora observe la diferencia al tener la capacidad de importar clases estáticas :

import static java.lang.System.out;

import static java.lang.System.err;

import static java.lang.Math.*;

public class Raiz {

public static void main(String[] args) {

try {

Double numero = new Double(args[0]);

out.println("La raiz de " + numero + " es : " + sqrt(numero));

} catch (Exception ex) {

err.println("Error: " + ex);

}

}

}

Mediante las palabras import static seguido del nombre de la clase, o bien, utilizando * para especificar toda una librería, se obtiene acceso a los métodos y campos

pertenecientes a clases estáticas. Lo anterior trae consigo un ahorro considerable en la escritura de código al no tenerse que especificar la ruta completa de la clase en cada ocasión en que ésta sea utilizada.