Está en la página 1de 374

Kimeraweb Curso de Java

Curso de Java Gratuito orientado a emuladores de Lineage Java para la comunidad de administradores de Lineage2, con ejemplos, desde nivel bsico a nivel programador. Incluye ODBC y MySQL. Mi compromiso con el foro de adminsprol2.com

Kimeraweb
Estas clases gratuitas pretenden abarcar los aspectos mnimos que hay que conocer para poder programar en cualquier lenguaje usando como herramienta el Lenguaje Java. Como modelo usar el emulador L2J. El curso est dirigido a todos los niveles de usuarios, desde los que no tienen conocimiento de programacin a los programadores habituales.

Kimeraweb email: brujulato@yahoo.es msn: kimera@kimeraweb.es fb: kimera kimeraweb 09/10/2012

Curso gratuito de Java en emuladores L2 Java


El ndice se ir modificando conforme este manual se expanda. ndice Tema 1, Introduccin. 1.1 Conceptos de programacin orientada a objetos 1.2 Desarrollo de un objeto 1.3 El cuerpo de un objeto 1.4 Reglas para crear nombres (nomenclatura) Tema 2, las clases 2.1 Alcance de una clase 2.2 Reglas de declaracin de una clase 2.3 Creando una clase 2.4 La clase abstracta Tema 3, los mtodos 3.1 Funciones de un mtodo 3.2 Modificadores de acceso 3.3 Modificadores que no dan acceso 3.4 Argumentos de los mtodos 3.4.1 Pasando variables referenciando a objetos 3.4.2 Pasando variables primitivas 3.5 Constructores Tema 4, las variables 4.1 Variables primitivas 4.2 Variables de instancia 4.3 Variables locales 4.4 Variables de tipo final, transient y volatile 4.5 Variables estticas 4.6 Arrays 4.6.1 Inicializando los elementos en un bucle 4.6.2 Arrays annimos 4.6.3 Arrays primitivos 4.6.4 Array de referencias a objetos 4.6.5 Asignaciones para arrays de una sola dimensin 4.6.6 Asignaciones para arrays de varias dimensiones Tema 5, Enumeraciones 5.1 Declarando una enumeracin 5.2 Declarando mtodos y variables en una enumeracin Tema 6, usando las caractersticas de la POO 6.1 La encapsulacin 6.2 Polimorfismo email: brujulato@yahoo.es

2012

Pgina 1

Curso gratuito de Java en emuladores L2 Java


6.3 Casteo de variables de referencia 6.4 Implementando una interfaz 6.5 Tipos de retornos en constructores sobrecargados 6.5.1 Devolviendo un valor 6.6 Autoboxing Tema 7, los constructores 7.1 Construccin, reglas y sobrecargas de constructores 7.2 Encadenamiento de constructores Tema 8, modificador esttico 8.1 Mtodos y variables estticas 8.2 Mtodos estticos y variables Tema 9, recolector de basuras

2012

9.1 Funcionamiento del recolector de basuras 9.1.2 Cmo funciona el recolector de basuras? 9.1.3 Puede una aplicacin Java quedarse sin memoria? 9.2 Escribir cdigo que maneje explcitamente objetos elegibles para ser borrados 9.3 Reasignado una variable de referencia 9.4 Aislando una referencia 9.5 Forzando el recolector de basuras 9.6 Limpiar antes de ejecutar el GC - el mtodo finalize() Tema 10, operadores 10.1 Operadores de asignacin 10.2 Operadores de asignacin compuestos 10.3 Operadores relacionales 10.4 Operaciones de igualdad 10.5 Comparacin de primitivos 10.6 Igualdad para variables de referencia 10.7 Igualdad para enumeraciones 10.8 Comparacin con instanceof() 10.8.1 Errores de compilacin con instanceof() 10.9 Operadores aritmticos 10.9.1 El operador resto % 10.9.2 Operador de concatenacin de cadenas 10.10 Operadores de incremento y decremento 10.11 Operador condicional 10.12 Operadores lgicos 10.13 Operadores e Bitwise 10.14 Atajo para operadores lgicos 10.15 Operadores lgicos sin atajos 10.16 Operadores lgicos ^ y

email: brujulato@yahoo.es

Pgina 2

Curso gratuito de Java en emuladores L2 Java


Tema 11, Control de flujo 11.1 Estamento if 11.2 Estamento switch 11.2.1 Expresiones vlidas para switch y case 11.2.2 Interrupciones y flujo en los boques switch 11.2.3 El caso default 11.3 Estamento while 11.4 Estamento do while 11.5 Estamento for, o for-in (el bucle bsico) 11.5.1 El for mejorado (para arrays, for each) 11.6 El uso de break y continue 11.6.1 Estamentos sin etiquetas 11.6.2 Estamento con etiquetas Tema 12, excepciones y aserciones 12.1 El estamento try catch 12.1.1 El uso de finally 12.1.2 Programacin de excepciones no capturadas 12.1.3 Definiendo excepciones 12.1.4 Jerarqua de las excepciones 12.1.5 Manejando una clase completa de la jerarqua de excepciones 12.1.6 Declaracin de Excepciones e Interfaces Pblicas 12.1.7 Relanzando la misma excepcin 12.1.8 Definicin de conceptos, excepcin y error 12.1.9 Programando el arrojo de excepciones 12.2 Mecanismo de aserciones 12.2.1 Reglas de expresin de las aserciones 12.2.2 Aserciones, identificados o palabra clave 12.2.3 Ejecucin con aserciones 12.2.4 Selector para activar y desactivar los assert 12.2.5 El uso apropiado para las aserciones Tema 13, la clase String, StringBuilder y StringBuffer 13.1 La clase String 13.1.1 String y memoria 13.1.2 Mtodos importantes de la clase String 13.2 StringBuffer y StringBuilder 13.2.1 Mtodos importantes de las clases StringBuffer y StringBuilder Tema 14, paquete I/O, lectura y escritura de ficheros 14.1 Resumen de las clases I/O 14.2 Creando ficheros, el uso de la clase File 14.3 El uso de FileWriter y FileReader 14.4 Combinando clases I/O, BufferedWriter y BufferedReader 14.5 Ficheros y directorios

2012

email: brujulato@yahoo.es

Pgina 3

Curso gratuito de Java en emuladores L2 Java


Tema 15, paquete I/O, la serializacin 15.1 ObjectOutputStream y ObjectInputStream 15.2 Objetos grficos 15.3 El uso de writeObject y readObject 15.4 La herencia y la serializacin 15.5 Serializacin y clases estticas Tema 16, bases de datos en Java: JDBC y SQL 16.1 La tecnologa Java Database Conectivity 16.2 El driver JDBC 16.2.1 Estructura y funcionamiento 16.2.2 Tipos de driver JDBC 16.3 El lenguaje SQL 16.3.1 Consultas 16.3.2 Tipos de sentencias SQL 16.3.3 Sentencias para la manipulacin de Datos (DML) 16.4 El API JDBC 16.4.1 Utilizacin de JDBC para acceder a datos 16.4.2 Gestin con la base de datos 16.4.3 Ejecucin de consultas 16.5 Manipulacin de registros 16.5.1 Obtener un objeto ResultSet 16.5.2 Desplazamiento por el conjunto de registros 16.5.3 Acceso a los campos 16.5.4 Otros mtodos de la interfaz ResultSet 16.5.5 Cierre de un ResultSet 16.5.6 El objeto ResultSetMetaData Tema 17, fechas nmeros y moneda 17.1 Trabajando con fechas, nmeros y monedas 17.2 La clase Date 17.3 La clase Calendar 17.4 La clase DateFormat 17.5 La clase Locale 17.6 La clase NumberFormat Tema 18, parseado, tokens y formateado 18.1 Un tutorial de bsqueda 18.1.1 Bsquedas simples 18.1.2 Bsqueda usando metacaracteres 18.1.3 Bsqueda usando cuantificadores 18.1.3.1 El punto predeterminado 18.1.3.2 Cuantificadores codiciosos 18.1.4 Cuando los metacaracteres y las cadenas colisionan 18.1.5 Usando Scanner para hacer bsquedas 18.2 Tokenizando 18.2.1 Tokenizando con String.split() email: brujulato@yahoo.es

2012

Pgina 4

Curso gratuito de Java en emuladores L2 Java


18.2.2 Tokenizando con Scanner 18.2.3 Formateando con printf() y format() Tema 19, Colecciones 19.1 Mtodos de Object 19.1.1 El mtodo toString() 19.1.2 Sobrescritura de equals() 19.2 Colecciones La interface List La interface Set La Interface Map La interface Queue 19.2.1 ArrayList La interfaz Comparable Comparator 19.2.2 Arrays 19.3 Bsqueda en Arrays y Collections Resumen de mtodos para Arrays y Collections. Convirtiendo Arrays a lista de Arrays 19.4 Listas 19.5 Set 19.6 Map 19.7 Priority Queue 19.8 El uso de genricos 19.8.1 Las colecciones al estilo "Legacy" 19.9 Polimrficos y genricos Mtodos genricos Declaraciones de genricos Creando tus propias clases genricas Creando mtodos genricos Tema 20, Clases anidadas 20.1 Las clases internas Escribiendo un cdigo "regular" de una clase interna Instanciando y referenciando una clase interna Mtodos locales de las clases internas 20.2 Clases internas annimas 20.3 Clases estticas internas Tema 21, Trabajando con hilos 21.1 Creando un hilo Heredando java.lang.Thread Implementando java.lang.Runnable 21.2 Instanciando un hilo 21.3 Ejecutando un Thread Iniciando y ejecutando mltiples hilos El administrador de tareas Mtodos de java.lang.Thread y java.lang.Object 21.4 El estado de los hilos y transiciones email: brujulato@yahoo.es

2012

Pgina 5

Curso gratuito de Java en emuladores L2 Java


Sleeping El mtodo Yield() El mtodo join() 21.5 Sincronizando cdigo Deadlock 21.6 Interaccin sobre los hilos El mtodo notifyAll() El mtodo wait() Tema 22, Compilando y ejecutando nuestro cdigo 22.1 Compilando con javac Compilando con -d 22.2 Lanzando aplicaciones Manejando los argumentos en la lnea de comandos 22.3 Ficheros .jar 22.4 Importaciones estticas

2012

email: brujulato@yahoo.es

Pgina 6

Curso gratuito de Java en emuladores L2 Java

2012

S que queremos hacer cosas ya, pero este manual no va indicado para personas con inquietudes exprs, es decir, lo leo ya y lo quiero ya. Un buen programador, espero que cuando acabes el manual lo seas, ha de ser meticuloso y usar todas las reglas aceptadas universalmente por los programadores, muy importante cuando se trabaja en equipo o se retoma el trabajo de otra persona, como es nuestro modelo L2J. En este captulo sabremos que es un objeto, como funciona y reglas de programacin. No te preocupes, empezaremos a programar tan pronto tengas la base necesaria. Acurdate de que un gigante con pies de barro...

1.1 Conceptos de la programacin orientada a objetos En programacin moderna, se usa una estructura llamada programacin orientada a objetos. Esto no es ms complicado que decir para programar algo haremos uso de otros cdigos que ya existen. Entonces, el concepto de objeto: ahora mismo diremos que un objeto es un programa que podemos incorporar a nuestro cdigo. En este objeto existen funciones. Una funcin, tal como su nombre indica es algo que est destinado a hacer, por ejemplo, las funciones de un coche? Frenar, acelerar, girar... Ahora imaginemos algo que podramos crear en breve, un formulario. Funciones de un formulario? Recoger informacin, guardar informacin, recuperar informacin... Que podra necesitar el formulario para guardar la informacin al disco? Acceso al disco, obviamente y... tendremos que crearnos un programa para poder grabar al disco? NO!! Afortunadamente, todos los lenguajes tienen incluidos objetos bsicos (realmente no son tan bsicos, se podran escribir miles de pginas... pero de momento lo dejamos aqu) que usaremos. Os habis dado cuenta de una cosa? Un objeto que slo sirve para acceder al disco... y nada ms.

email: brujulato@yahoo.es

Pgina 7

Curso gratuito de Java en emuladores L2 Java


1.2 Desarrollo de un objeto Los objetos en cualquier lenguaje, y Java no es una excepcin, es que deben ser de bajo acoplamiento y alta cohesin. Bueno, y esto para qu sirve?

2012

Ojo, si no ejecutas esta premisa a la perfeccin, tu programa va a ser un churro (para que hablar con rodeos). Primero, entremos en detalles, que es bajo acoplamiento? Un programa de bajo acoplamiento significa, que si el programa se modifica, no afectar al objeto que lo usa. Por ejemplo, imaginemos que tenemos un objeto llamado SUMADOR que lo nico que hace es sumar dos nmeros. Este objeto se invoca por otro objeto llamado CALCULADORA. SUMADOR acepta tomar dos nmeros y devolver su suma. Por cualquier desdicha de la naturaleza, el cdigo de SUMADOR tiene que ser reescrito porque se ha descubierto un fallo en su programacin. El bajo acoplamiento significa que aun reescribiendo el cdigo de SUMADOR, CALCULADORA seguir usando a SUMADOR sin tener que editar su cdigo, resumiendo, SUMADOR es totalmente independiente de CALCULADORA. La alta cohesin significa que el programa har exactamente y nicamente eso para lo que fue diseado, y nada ms; de esta manera, sabemos que SUMADOR solo suma, y no har las funciones de RESTADOR (ms adelante explicaremos otras ventajas). Con lo explicado aqu, sera censurable que CALCULADORA fuese un objeto que hiciese otra tarea que no fuese calcular, y que su edicin forzase a que otro objeto que usase CALCULADORA tuviera que ser editado tambin.

email: brujulato@yahoo.es

Pgina 8

Curso gratuito de Java en emuladores L2 Java


1.3 El cuerpo de un objeto

2012

Un objeto comn, est compuesto de funciones y variables. Explicado brevemente, el objeto tiene como misin hacer algo, y sus funciones son las tareas que puede realizar. Hay tareas que necesitan guardar alguna informacin para poder ser realizada, para guardar esta informacin se usan variables (porque su valor varia al menos, una vez). Entonces, resumiendo porque esto no tiene ms: Los objetos contienen funciones y contienen variables. Las funciones contienen variables.

Al principio cit objeto comn porque ms adelante veremos otra clase de objetos.

email: brujulato@yahoo.es

Pgina 9

Curso gratuito de Java en emuladores L2 Java


1.4 Reglas para crear nombres, o nomenclatura.

2012

Como sabemos cundo leemos un cdigo, si lo que leemos es un objeto, una funcin o una variable? Es por su nomenclatura (no, no vamos a hacer qumica... todava). Las reglas para crear nombres son: Un objeto (tambin se le llama clase) siempre comienza su nombre por maysculas. Una funcin siempre comienza su nombre por minsculas. Una variable de clase ir antecedida con el smbolo de guin bajo _, ejemplo _xPos. Este tipo de variables suelen ser privadas. Las veremos ms adelante. Las dems variables sern en minsculas.

Aparte de la nomenclatura, los nombres de las clases tienen que ser descriptivas, es decir, su nombre debe intuir lo que va a hacer, de tal manera, que cualquier lector ajeno al proyecto, pueda saber, sin leer el cdigo, que es o hace la clase. Sus mtodos deben ser creados de forma verbal, por ejemplo, las funciones de un coche se llamarn frenar, acelerar y no freno o aceleracion. Cuando se quiere obtener o asignar valores, se usan get y set, seguidos del nombre, get significa obtener, y set, seleccionar. As que si queremos obtener la velocidad del coche, getVelocidad. Si lo que deseamos es saber si una operacin se est realizando, usaremos is, que significa en castellano, ser o estar. Est frenando? -> isFrenando. Debo decir, que en ingls, queda mejor -> isStopping. Si estas fijndote, cuando se unen dos palabras, la segunda se pone en maysculas, a esto se le llama camelCase, y tambin es otra regla que se aplica a todo. Adems de las clases y funciones, las variables si van a trabajar durante un periodo largo, tambin han de ser descriptivas, por ejemplo, fuerzaDeFrenado = 100. Para terminar, las constantes de clase en maysculas, sin usar camelCase, y su lugar, guion bajo (constante es una variable que una vez asignado su valor, ya no va a cambiarlo) ejemplo: ANCHO_DOCUMENTO.

email: brujulato@yahoo.es

Pgina 10

Curso gratuito de Java en emuladores L2 Java


2.1 Asignar el alcance de una clase Estos modificadores tambin se pueden aplicar a las funciones y variables.

2012

Cuando creas un objeto, puedes querer que ese objeto, pueda ser usado o no, dentro de su mbito. Podemos querer que nuestra clase CALCULADORA sea invisible para la clase INSTRUMENTO MUSICAL y que sea visible para la clase MATEMATICAS por razones evidentes.

Para poder hacer esto, al declarar una clase, podemos hacerlo de diferentes maneras: Public Al declarar una clase como pblica, ser visible desde cualquier otra clase situada en cualquier otra parte. Sin modificador o Default Por defecto, una clase es visible en el paquete que se crea nicamente.

Al igual que creamos directorios en Windows (u otro sistema operativo) para agrupar ficheros que son comunes entre s, o sea, que tienen alguna relacin entre ellos, los ficheros creados en Java tambin cumplen la misma lgica. En un paquete Musica podramos encontrar los paquetes Instrumento de Percusion, Instrumento de viento, Instrumento de cuerda, y dentro de cada uno, mas directorios. Cada directorio se denomina paquete.

email: brujulato@yahoo.es

Pgina 11

Curso gratuito de Java en emuladores L2 Java


2.2 Reglas de declaracin de una clase

2012

Antes de crear tu primera clase, an tienes que conocer unas reglas ms, si, lo siento, es pesado pero necesario, pero no te preocupes, slo con leerlas, te acordars de ellas, porque a medida que avancemos, vers que son normas evidentes. Slo puede existir una clase pblica por fichero fuente. El nombre de la clase siempre ser el mismo que el nombre del fichero que la contiene. Si la clase est dentro de un paquete, el estamento package ser lo primero que aparezca en el fichero de la clase conteniendo su ruta. Lo siguiente que aparece despus de un package, son los comandos import. Las instrucciones import y package se aplican a todas las clases del fichero, en otras palabras, no hay manera de declarar diferentes package para cada clase definida en el fichero. Un fichero puede tener ms de una clase no pblica. Las clases no pblicas del fichero pueden tener un nombre distinto al fichero que las contiene.

En un package se inserta la ruta de la clase. Los import se usan para importar otras clases.

email: brujulato@yahoo.es

Pgina 12

Curso gratuito de Java en emuladores L2 Java


2.3 Creando una clase

2012

Bien, ya hemos ledo todas las normas habidas y por haber para crear una clase. En estos momentos somos unos eruditos en lo que a normas se refiere pero ya vamos a empezar la accin. Nuestra primera clase va a hacer poco, ya que, como dije antes, la programacin orientada a objetos se basa en el uso de otras clases para hacer uso de ellas, y como no hemos visto ninguna, no podemos hacer nada. Est fue una gran leccin, verdad? Nunca la olvidars. Pero aun sin conocer ninguna clase, podemos crear un pseudo cdigo que tiene como misin introduciros en el mundo de la programacin orientada a objetos, POO. Mi primera ilustracin va a consistir en crear un coche y sus funcionalidades.
public class Coche { public void frenar() { } public void acelerar() { } public String getAceleracion() {return null; } public String getFrenada() {return null; } }

Que estamos viendo aqu? Primero, al escribir la clase, se indica su visibilidad. Recuerda que si no se indica, se obtiene un alcance por defecto, es decir, slo es visible en el paquete que se crea. Despus tenemos la instruccin class y el nombre de la clase abriendo y cerrando llaves al final del cdigo, encerrando sus mtodos. En sus mtodos podemos ver tres palabras que no habamos visto antes, se trata de void, String y return. Cuando un mtodo no devuelve nada, al nombre del mtodo le precede la instruccin void. Cuando un mtodo devuelve algo, le precede el nombre del tipo que devuelve y entre corchetes la instruccin return seguido de una variable (en este caso, nada, null). El tipo se refiere a la clase de objeto que va a ser devuelto. Clases de tipos son por ejemplo, cadenas (conjunto de letras), nmeros, nombres de clases como String, Integer, Object, etc...

email: brujulato@yahoo.es

Pgina 13

Curso gratuito de Java en emuladores L2 Java


2.4 La clase abstracta

2012

El nico propsito de una clase abstracta es la de crear un patrn para crear clases del mismo tipo y funcionalidad que la clase abstracta. No puede duplicarse.
abstract class Coche { private double precio; private String modelo; private String year; public abstract void goFast(); public abstract void goUpHill(); public abstract void impresionarVecinos(); }

Cuando creas un coche heredando la clase abstracta Coche, lo que haces es coger un patrn y asimilarlo a tu cdigo. Podras pensar que, qu utilidad tiene esto? Imagina que eres el programador de Coche, y has creado un programa que usa los mtodos creados en Coche. Otro programador, podra crear un Coche como el tuyo, que adems volase, pero para que el cdigo ya implementado por ti funcione correctamente en el coche volador, necesita implementar los mtodos que tu esperas que estn ah, como el precio, el modelo, el impresionarVecinos... aparte este nuevo programador, hara que su coche volase sin tener que programar el precio, el modelo, el goFast... que ya estn creados por ti. Extrapola esto a un NPC de Lineage, si tienes un NPCWalker que quisieras a la vez que vendiera como un GMShop, que necesitas? Un NPCWalker vendedor, cierto? Al implementar esto en tu nuevo coche, inmediatamente se incluirn los cdigos necesarios para que tu clase est correctamente implementada.

Si te fijas, en cuanto heredo CochePlantilla, me exige Add unimplemented methods, aadir mtodos no implementados. En cuanto pulses ah, se implementarn los mtodos, pero el cdigo de su interior, es cosa tuya.

email: brujulato@yahoo.es

Pgina 14

Curso gratuito de Java en emuladores L2 Java

2012

Los mtodos marcados como abstractos terminan siempre en punto y coma, en lugar de llaves. Si el mtodo estuviera en una clase normal, es decir, con cdigo, con tan slo un mtodo abstracto que tenga una clase, tiene que ser marcada como abstracta tambin. Cuando una clase abstracta, lo es al 100%, es decir, que no posee ningn cdigo, algo como esto:

se dice que es una interfaz. Mientras que en una clase abstracta puedes tener funciones con cdigo y funciones abstractas mezcladas, en una interfaz no. Tambin, los mtodos de una interfaz son por defecto pblicos y abstractos, por lo tanto no tienes que declarar que son pblicos y abstractos. La razn es simple, si el mtodo fuera privado, cuando heredases la clase abstracta, las subclases no tendran acceso a ellos.

En la interfaz, las variables que puedes crear tienen que ser pblicas (por lo explicado antes), estticas (que no cambian su valor) y finales (que no pueden duplicarse). Sin embargo, los mtodos no deben ser estticos, ya que, cuando se hereda la interfaz, los mtodos abstractos hay que editarlos. Si fueran estticos, no se podran modificar. Una interfaz puede heredar una o ms interfaces, adems, no puede heredar otra cosa que no sea una interfaz. No confundir heredar con implementar, ya que la interfaz no puede implementar nada. Cuando se declara una interface, debe ser indicado con el modificador interface. email: brujulato@yahoo.es Pgina 15

Curso gratuito de Java en emuladores L2 Java

2012

Si quieres que tu interfaz sea accesible desde cualquier parte del cdigo, has de aadir el modificador public, sino el acceso por defecto (de paquete, default) ser seleccionado.

Aunque sea redundante, puedes usar los modificadores public y abstract para declarar una interfaz.

En una interface, como dije antes, puedes declarar constantes. Al hacer esto, se garantiza el acceso a esa variable desde cualquier clase.

Los modificadores que tienes que usar son: public static final, es decir, que son pblicas, que no varan su valor, y que no se pueden duplicar. Al no poder duplicarse, te aseguras que ninguna clase tenga acceso a la variable y altere su valor.

Si en cualquier caso olvidases esto, no olvides que, aunque no declares la variable como tal, ser tomada como public static final irremediablemente:

por lo tanto, al querer modificar la variable:

email: brujulato@yahoo.es

Pgina 16

Curso gratuito de Java en emuladores L2 Java

2012

obtenemos un error que nos comunica que borremos el modificador final de VELOCIDAD_MAXIMA, y como veis en la captura anterior, no existe tal modificador, es decir, se hace evidente que JDK nos ha declarado por defecto que es public, static y final.

email: brujulato@yahoo.es

Pgina 17

Curso gratuito de Java en emuladores L2 Java


3.1 Funciones de los mtodos.

2012

Sin los mtodos, las clases no tendran mucho sentido. Los mtodos son los responsables de que la clase cobre vida, y sentido. Cuando invocamos una clase, actuamos sobre sus mtodos. Lo principal que definimos en un mtodo, son las funciones get y set, encargadas de devolver y actuar sobre las variables de la clase directamente. Es una buena prctica crear las variables de las clases de forma privada, y manipularlas a travs de sus mtodos para evitar fallos en su uso. Por ejemplo, podramos usar la clase Coche, y en su variable VELOCIDAD_MAXIMA, le passemos un valor de cadena en lugar de un valor numrico, tal como se esperaba. Sin duda, acabara en tragedia:

Para evitar situaciones parecidas, la buena prctica nos obliga a usar un mtodo para prevenir o evitar errores, porque, en lugar de una cadena de texto, podramos haber pasado como valor un nmero decimal, pero aun as, sera un valor incorrecto. Las funciones pueden ejecutar una serie de acciones y devolver un valor, o no devolver nada. Para indicar que no devuelve nada, despus del modificador, o antes del nombre de la funcin, indicamos con void nuestra intencin, que traducido significa vaco, es decir, devuelve un valor vaco. Cuando queremos devolver el resultado de una operacin, de cualquier clase, entonces el lugar de void lo ocupa el tipo de objeto que vamos a devolver, una cadena (String), un objeto (Object) u cualquier otra cosa que hayamos inventado (un Coche por ejemplo). Para usar el mtodo de una clase, se usa el nombre de la clase seguido por un punto, y luego el nombre del mtodo. Es como el ejemplo, solo que, una funcin lleva parntesis, y las variables no llevan.

email: brujulato@yahoo.es

Pgina 18

Curso gratuito de Java en emuladores L2 Java


3.2 Modificadores de acceso

2012

Los modificadores de acceso de los mtodos son cuatro, en lugar de dos como en las clases (Default y Public) y sus caractersticas son compartidas con las variables. Si no defines ningn modificador, el modificador por defecto es Default (te lo recuerdo nuevamente, con Default solo se logra visibilidad dentro del paquete donde se crea). Public Cuando un mtodo o una variable se declara pblica puede verse desde cualquier parte del programa. Private Cuando el mtodo o la variable se declara privada, solo es accesible donde es creada. Protected Igual que privada, slo que tambin ser accesible por sus subclases. Default Por defecto, no estas forzado a declarar una clase Default. Si se omite el modificador, Default es tomado por defecto.

email: brujulato@yahoo.es

Pgina 19

Curso gratuito de Java en emuladores L2 Java


3.3 Modificadores que no dan acceso.

2012

Estos modificadores se usan para limitar el uso de los mtodos, se les denominan Nonaccess y hay siete clases.

Final Se usa para evitar que el mtodo sea sobrescrito en una subclase. Si se pasa final como parmetro al mtodo, es decir, proporcionarle un valor final, el mtodo no podr cambiar el valor.

Abstract Un mtodo abstracto se declara pero no se implementa. En otras palabras, es un mtodo no funcional. Recuerda que un mtodo abstracto no lleva llaves, termina en punto y coma. Cuando usas este modificador, lo que buscas es que la subclase implemente estos mtodos, forzando a definir las acciones que debe hacer la clase, pero no la manera en que debe hacerlas. As por ejemplo, nuestro Coche puede obligar a sus subclases a implementar gastarCombustible, y dependiendo del coche, podra gastar gasolina, diesel, queroseno, agua, hidrogeno...

Ahora una pregunta de control antes de continuar, podras crear un mtodo abstracto y final? Piensa en ello, la respuesta ms adelante.

email: brujulato@yahoo.es

Pgina 20

Curso gratuito de Java en emuladores L2 Java


Synchronized

2012

Indica que el mtodo slo puede ser accedido una vez al tiempo, es decir, que hasta que la tarea con la primera clase que invoc este mtodo no haya acabado, pondr en espera a los dems hilos que estn esperando ejecutar este hilo. Esto es til para una tarea como guardar un fichero. Si se estn guardando datos, sera un error permitir que otro hilo insertase nuevos datos mientras se lleva a cabo la operacin de salvar datos. El resultado final es imprevisible. Las variables no pueden ser declaradas Synchronized.

Native Este modificador indica que el mtodo se ha implementado desde otra plataforma de cdigo diferente, normalmente en lenguaje C. Tambin, al igual que Synchronized, es exclusivo de los mtodos.

Transient Para entender este, hay que saber primero que es serializar. Cuando se serializa un objeto, se pretende guardar la informacin que contiene normalmente en disco. Por ejemplo, el objeto Agenda podra contener objetos Personas. Si queremos guardar el contenido de la agenda, primero, hay que serializar el objeto. Bien, pues Transient indica que ese campo no debe ser serializado por ejemplo, porque sean derivadas de otros campos.

Strictfp Se usa para asegurar que cualquiera que sea la plataforma donde se ejecute el programa, la precisin de un nmero decimal sea el mismo. Si se omite, la JVM usar la precisin que mejor considere.

Static El ms complejo de usar. Resumir brevemente aunque ser comentado en profundidad ms adelante. Una variable esttica es una variable que pertenece a la clase, no al objeto, quiero decir, una instancia de ese objeto (una instancia, un clon). Las variables estticas se inicializan una sola vez, al comienzo de la ejecucin del programa antes que cualquier otra variable. Un mtodo esttico slo puede acceder a otros mtodos estticos. email: brujulato@yahoo.es Pgina 21

Curso gratuito de Java en emuladores L2 Java


Un mtodo o variable NombreClase.NombreMetodo. esttica es accesible

2012
directamente,

Un mtodo esttico no puede usar this o super. Se puede marcar como static: Mtodos Variables Clases anidadas (no dentro de un mtodo, se ver ms adelante) Inicializacin de bloques

No se puede marcar como static: Constructores (ya que se usan para crear instancias) Clases (a menos que estn anidadas) Interfaces Mtodos locales dentro de las clases (lo veremos en detalle) Mtodos internos de clase y variables de instancia. Variables locales.

La palabra reservada this se usa para referirse a la variable de la clase, y super a la variable de la superclase, es decir, la clase de la que se deriva la clase actual.

La respuesta a la pregunta, se puede declarar un mtodo como abstracto y final es... falso. Ambas declaraciones son opuestas, un mtodo abstracto tiene que ser sobrescrito, recuerda que se declara sin cdigo, mientras que un mtodo final evita ser modificado.

email: brujulato@yahoo.es

Pgina 22

Curso gratuito de Java en emuladores L2 Java


3.4 Argumento de los mtodos

2012

Es comn en cualquier lenguaje, que los mtodos acepten parmetros, es decir, en un objeto, para que se cree una accin, a veces es necesario decirle alguna informacin. Por ejemplo, si el mtodo fuera, acelerar, una pregunta obvia sera, cunto? Recordarte antes que nada, que un mtodo se crea primero poniendo su modificador de acceso (que es opcional, sino se elige, la JVM toma Default), luego el tipo de retorno que poda ser cualquier cosa (si no quieres ningn tipo de retorno, se escribe void), seguido del nombre del mtodo y entre parntesis los parmetros. Pues en Java, la manera ms simple de crear un mtodo es hacer uno que no tome ningn parmetro:
public class ejemplos { public void digoHola() { System.out.print ("HOLA"); } }

Explico un poco lo que vemos, primero, la clase, que abre llaves y encierra a un mtodo que no toma parmetros y se cierran las llaves de la clase. En el interior de la clase, el mtodo se declara encerrando entre llaves las acciones. Cuando insertamos un parmetro en el mtodo, debemos decirle a la JVM el tipo de dato que va a ser insertado. Puede ser cualquier cosa que exista. En este ejemplo, ser de tipo String, que significa cadena de texto.
public class ejemplos { public void digoHola(String nombre) { System.out.print ("HOLA " + nombre); } }

Ahora podemos ver que al ejecutar nuestro ejemplo, nos pedir un nombre para incluir, y que en el mtodo usaremos ese nombre para incluir en el saludo.

email: brujulato@yahoo.es

Pgina 23

Curso gratuito de Java en emuladores L2 Java


Continuemos con el ejercicio y ahora tomaremos la edad como siguiente parmetro:
public class ejemplos {

2012

public void digoHola(String nombre, String edad) { System.out.print ("HOLA " + nombre + ", se que tienes " + edad + "aos."); } }

Ahora nuestro resultado sera algo como: Hola Maria, se que tienes 17 aos. El cdigo para obtener este resultado sera:
ejemplos.digoHola("Maria","17);

Puedes seguir sumando parmetros hasta que la RAM de tu ordenador se quede sin memoria. No hay problema. Si quisiramos hacer una operacin matemtica con la edad, usaramos el tipo int, que significa, entero, o mejor dicho, nmero entero:

La clase main, que sera la clase que siempre se ejecuta primero por defecto y porque la JVM busca un mtodo llamado main para ejecutar el programa. Sino existiese ningn mtodo main, nos lanzara un error.

email: brujulato@yahoo.es

Pgina 24

Curso gratuito de Java en emuladores L2 Java


La consola nos devuelve el resultado de la operacin.

2012

Como colofn de los parmetros hay un argumento denominado var-args, cuando declaramos como parmetro un var-args estamos diciendo que esperamos un nmero indeterminado de parmetros. Este tipo slo se acepta una sola vez en un mtodo y siempre tiene que ser declarado el ltimo:

Salida de la consola:

Esta forma var args se forma escribiendo tres puntos despus del tipo y en realidad lo que hace es convertir la variable en un array que se puede recorrer con un for.

email: brujulato@yahoo.es

Pgina 25

Curso gratuito de Java en emuladores L2 Java


Conceptos nuevos: Un array es una variable capaz de almacenar ms de un dato.

2012

Este for se denomina foreach porque toma un valor del array y lo introduce en una variable. Se trata de un bucle que recorre todos los valores almacenados en la variable, en cada vuelta introduce el valor del array en la variable hasta llegar al ltimo valor. Un bucle es un bloque de instrucciones que se repite un nmero de veces.

Todo esto se ver ms en detalle.

email: brujulato@yahoo.es

Pgina 26

Curso gratuito de Java en emuladores L2 Java


3.4.1 Pasando variables referenciando a objetos

2012

La memoria en Java se divide en 2 partes, la memoria permanente, Heap, donde viven los objetos, y la memoria Stack, donde viven las variables. Cuando pasas un objeto como argumento a un mtodo, debes tener en cuenta que ests pasando un objeto de referencia, y no el objeto en s mismo. Recuerda que una variable de referencia almacena los bits que representa (para la JVM) una manera de conseguir ese objeto en la memoria Heap. Ms importante, tienes que recordar que no ests pasando la variable de referencia actual, sino una copia. Una copia significa que consigues el patrn de bits de la variable de referencia, as que cuando pasas una variable de referencia, estas pasando la copia de bits del objeto que representa. En otras palabras, ambos, el invocador y el mtodo invocado tendrn ahora una copia idntica de la referencia, pero ambos se refieren exactamente al mismo objeto del Heap (no a la copia).

3.4.2 Pasando variables primitivas Cuando una variable primitiva se pasa por un mtodo tampoco cambia de valor, ya que se pasa una copia de la variable, al igual que ocurre con los objetos.

email: brujulato@yahoo.es

Pgina 27

Curso gratuito de Java en emuladores L2 Java


3.5 Constructores

2012

En un lenguaje orientado a objetos la principal caracterstica es que los objetos se instancian para obtener nuevos objetos con distinto comportamiento. Por ejemplo, si creamos un objeto Coche, las propiedades del objeto podran ser, marca, modelo, potencia y velocidad mxima por poner un ejemplo. Si a cada modelo Coche, le asignamos una marca distinta, estamos creando nuevos modelos de coches, al fin y al cabo son coches con distintas caractersticas. No creamos un modelo Ford, otro Ferrari y otro modelo Porche, sino que creamos estos modelos a partir de un objeto que rene todas las similitudes de un Coche. Habiendo aclarado que instanciando un objeto Coche podemos crear nuevos coches con distintos comportamiento, es hora de explicar la funcin de un constructor. Cada vez que instancias un objeto, por decirlo de otra manera, un nuevo clon de un objeto, ests ejecutando un constructor. Si no lo haces t, la JVM lo hace por ti. La primera cosa que llama la atencin es que un constructor se parece horrores a un mtodo, y para diferenciar un mtodo de un constructor nos fijamos que el constructor no lleva return ni lleva void.

Qu vemos aqu? Pues estamos viendo dos clases, la clase pblica Main creando un Ferrari, y la clase por defecto Coche. Recuerda que un fichero slo puede tener una clase pblica. Para instanciar un objeto se usa el comando new. Para convertir la variable Ferrari en una instancia de coche, primero se usa el nombre del objeto que se va a instanciar, en el ejemplo es Coche, seguido de la variable, y luego usando new (traducido significa nuevo, nuevo Coche) seguido del objeto que queremos instanciar (o clonar, como lo veas ms claro) y entre parntesis, las opciones del constructor. Si el constructor no existiese, la JVM implementara (aunque no aparecer en el cdigo): public Coche (){}

email: brujulato@yahoo.es

Pgina 28

Curso gratuito de Java en emuladores L2 Java


Ahora veamos el cdigo del Coche:

2012

La variable de la clase, tal como indicamos antes, por el convenio de Java, se escribe en maysculas y los espacios separados mediante guin bajo, MARCA_DEL_COCHE. Luego tenemos el constructor, el nombre es el mismo que el de la clase, Coche, y su funcin es la de asignar la variable de la clase la marca del coche, de esta manera, cada vez que instanciemos un Coche, tendremos una marca. Luego tenemos un mtodo coche, respetando el convenio de Java para los mtodos, el formato del nombre es camelCase. Pero aun respetando el convenio, es una mala prctica nombrar un mtodo con el mismo nombre que su clase, porque slo es til para crear confusin, como es este caso. La funcin de este mtodo es imprimir en pantalla la marca del coche pasada por parmetro. Hay una tcnica de programacin que se llama sobrecarga de mtodos, consiste en crear mtodos con el mismo nombre pero aceptando diferentes parmetros.

Por ejemplo, aqu declaro dos constructores, el primer constructor (lnea 13) construye un coche sin parmetros, en la lnea 14 construye un Coche con la MARCA_DEL_COCHE.

email: brujulato@yahoo.es

Pgina 29

Curso gratuito de Java en emuladores L2 Java


Ahora completemos lo que sera un Coche:

2012

Este cdigo es algo ms completo definiendo el Coche. En la clase Coche ahora hay cuatro propiedades en lugar de una. Tambin tenemos un constructor que toma dos parmetros de tipo String (cadena de texto) y dos ms de tipo int (nmeros enteros). Para completar, aadimos un mtodo para obtener la informacin del Coche, respetando siempre el convenio de Java, usamos get como parte del nombre (traducido significa obtener). Ahora, al instanciar un Coche, tendremos que introducir toda la informacin que el constructor de la clase est esperando, sino, nos dar error y no nos dejar compilar. As que la clase pblica Main instancia un Coche pasando cuatro parmetros en lugar de dos. Si te fijas, los parmetros que no son cadenas de texto, se pasan sin usar comillas. Slo las cadenas de texto se rodean con comillas.

email: brujulato@yahoo.es

Pgina 30

Curso gratuito de Java en emuladores L2 Java


Ahora hagamos dos coches y probemos el resultado.

2012

La consola arroja:

la especificacin de los dos coches. Como ves, el usar un objeto con diferentes propiedades da como resultado nuevos objetos con diferentes comportamientos. Esto es la programacin orientada a objetos.

email: brujulato@yahoo.es

Pgina 31

Curso gratuito de Java en emuladores L2 Java


4 Las variables

2012

Decamos antes que las variables son palabras que guardan un valor, y el nombre de la variable debe ser significativo, para relacionar de forma humana lo que guarda.

4.1 Variables primitivas Hay ocho tipos de datos primitivos en Java, son: char: es un carcter, su rango va desde el 0 al 65,535 boolean: es un 0 o un 1, se usa para indicar si una condicin es falsa o verdadera. byte: almacena un valor entre -128 al 127. short: almacena un valor entre -32,768 y 32,767. int: almacena un valor entre -2,147,483,648 y 2,147,483,647. long: almacena un valor entre -9,223,372,036,854,775,808 y 9,223,372,036,854,775,807 float: almacena un valor entre 1.40129846432481707e-45 y 3.40282346638528860e+38 double: almacena un 1.79769313486231570e+308d valor entre 4.94065645841246544e-324d y

Aunque para nuestros efectos, usaremos normalmente float. Esto slo es a ttulo informativo, no tendrs que aprendrtelo de memoria, aunque si tendrs que aprender bien boolean e int, que son los ms usados. Si prefieres aprendrtelo de otra manera, a m me gusta ms esta:

email: brujulato@yahoo.es

Pgina 32

Curso gratuito de Java en emuladores L2 Java


4.2 Variables de instancia

2012

Las variables de una instancia (no de una clase), se definen dentro de una clase pero fuera de cualquier mtodo, y se inicializan cuando se inicializa la clase. Qu diferencia hay entre una variable de clase y otra de instancia? La variable de clase no se instancia, es decir, no se puede clonar con new. Muy importante es recordar que las variables de instancia pueden usar cualquiera de los cuatro niveles de acceso (te acuerdas? public, private, default y protected) y que pueden ser marcadas como final y transient. No puedes declararlas como abstract, synchronized, stricfp, native y por supuesto, static. Para entender variable de clase y variable de instancia, he modificado el cdigo:

Fjate que en la variable de instancia, cada modelo de Coche tiene sus propias caractersticas, pero al poner como esttica una de las variables, automticamente se transforma en variable de clase, y al asignarle un valor al Ferrari, tambin se le asigna el valor al Toyota, de manera que cuando se ejecuta:

Obtenemos:

email: brujulato@yahoo.es

Pgina 33

Curso gratuito de Java en emuladores L2 Java


4.3 Variables locales

2012

Las variables locales se encuentran en los mtodos, viven y mueren dentro de el, y se destruyen cuando el mtodo ha terminado su tarea. Las variables locales slo pueden ser marcadas como final. Al contrario que las variables de clase, las variables locales para poder usarlas, es necesario inicializarlas primero, es decir, asignarle un valor. Que es declarar una variable? Decir que existe sin que contenga un valor, por ejemplo:

Que es inicializar una variable? Asignarle un valor (aunque sea un valor vaco).

Tambin puedes inicializar la variable cuando la declaras:

El resumen de los modificadores que puedes usar:

email: brujulato@yahoo.es

Pgina 34

Curso gratuito de Java en emuladores L2 Java


4.4 Variables de tipo final, transient y volatile

2012

El modificador final hace imposible reiniciar la variable una vez que se ha obtenido un valor explcito (que es distinto que un valor por defecto, como ocurre con las variables de clase). Acurdate que una clase final no puede heredarse, un mtodo final no puede sobrescribirse en una subclase y una variable final, no puede modificar su valor. El modificador transient sirve para decirle a la JVM que ignore la variable cuando se quiere serializar el objeto o deserializarlo. Cuando de/serializas un objeto lo que haces es recuperar/almacenar la informacin (del/en disco duro por ejemplo). Este recurso tan interesante ser visto ms adelante en profundidad. El modificador volatile le dice a la JVM que el hilo que accede a la variable debe obtener su propia copia. Esto se usa en procesos multihilos, donde cada hilo de ejecucin podr variar el valor de la variable. Profundizaremos en esto ms adelante, ahora solo tienes que saber que existe para ser usados en programacin multihilos.

email: brujulato@yahoo.es

Pgina 35

Curso gratuito de Java en emuladores L2 Java


4.5 Variables estticas.

2012

El modificador static se usa para crear variables estticas. Una variable esttica pertenece siempre a la clase donde se crea, no a sus instancias (o clones). Como vimos en el ejemplo anterior, al crear una variable esttica en la clase Coche, su valor cambiaba en todas las instancias creadas con Coche, en el ejemplo, al cambiar el valor en Ferrari, Toyota tambin fue afectado.

4.6 Arrays Los arrays son variables de tipo objeto porque son capaces de guardar mltiples valores o variables al mismo tiempo. Pueden almacenar datos primitivos o referencias a objetos. Esta clase de variables viven en el Heap. Los arrays se declaran comenzando por el tipo de elemento que el array va a almacenar, puede ser un objeto o datos primitivos seguidos de llaves cuadradas, luego le seguira el nombre de la variable. Aunque podramos poner los corchetes al final, resulta menos legible. Si colocamos ms corchetes, estamos creando dimensiones.

Podemos hacer cosas absurdas como esta, aunque tambin son vlidas:

Esto es lo mismo que poner dos corchetes juntos. En la declaracin nunca se pone el tamao al contrario que ocurre con otros lenguajes, que se puede iniciar el array declarando la cantidad de slots disponibles en el array. Quiero decir, esto, es ilegal:

email: brujulato@yahoo.es

Pgina 36

Curso gratuito de Java en emuladores L2 Java


La forma ms directa de construir un array es usando new seguido del tipo del array.

2012

En este ejemplo hemos creado 11 slots para guardar cadenas de texto en la variable nombre. Digo 11 porque se cuenta a partir del cero, si contamos el cero y el diez, tenemos once slots. Cuando se inicializa una variable, por defecto, en los slots encontraremos los valores por defecto del objeto. Para una cadena es nada, para un nmero es cero, y para objetos es null. Null no significa que nulo, no es un error, como en otros lenguajes, significa que no guarda ninguna referencia a ningn objeto. En los arrays dimensionales podramos decir que son arrays de arrays (en caso de arrays de dos dimensiones), es como imaginar el juego de hundir la flota. Si declaras una variable de 10 y 10, imagnate un eje X de 10 posiciones y un eje Y de 10 posiciones (10 x 10), son 100 posiciones de memoria reservadas. Los arrays de dos dimensiones pueden ser declarados de las siguientes maneras:

Es decir, puedes inicializar la cantidad de slots en cada dimensin del array.

email: brujulato@yahoo.es

Pgina 37

Curso gratuito de Java en emuladores L2 Java


4.6.1 Inicializando los elementos en un bucle Un bucle es un bloque de instrucciones que se repite mientras se cumple una condicin. Veremos las diferentes clases que hay en profundidad.

2012

Los objetos de un array tienen solo una variable pblica que devuelve el nmero de elementos del array. El ltimo valor del ndex es siempre uno menos que la longitud del array. Esto se debe a que la posicin cero del array se cuenta en la longitud. As que, array[3] es array.lenght = 4, suponiendo que haya un elemento en cada slot del array. La forma ms sencilla de recorrer un array es usar el for de tipo foreach (sera la forma que correspondera a otros lenguajes, en Java no hay distincin).

Lo que vemos aqu es que se ha declarado un array llamado numeros con 11 slots. El bucle es for y encierra entre llaves la repeticin de instrucciones. Dentro de for se declara a para que acepte cada elemento guardado en numeros. Inicializamos cada slot con el valor guardado en slot, que en cada repeticin del bucle suma uno (slot++). As que en cada bucle, se est eligiendo un slot para asignarle un valor (numeros[slot]=slot).

email: brujulato@yahoo.es

Pgina 38

Curso gratuito de Java en emuladores L2 Java


O sea, esto est haciendo: numeros[0] = 0 numeros[1] = 1 numeros[2] = 2 etc, hasta llegar al ltimo slot del array, el 9. Recuerda, del 0 al 9 son 10 slots. Otra manera es usando la antigua versin de for y la variable len del array.

2012

Lo que estamos viendo aqu es que en el for se inicializa la variable x con el valor de cero. En segundo lugar se especifica la condicin que mientras sea cierta, ejecutar el bloque de instrucciones, esta es que x sea menor que la longitud de numeros. Finalmente, le sumamos a la variable x un uno. Dentro del bucle se iguala la variable numeros con el ndice x al valor de x. Es exactamente el mismo proceso que el anterior, slo que aqu el final del array lo conocemos porque hacemos uso de la propiedad length del array. En for actualizado, esto se hace de forma automtica, el bucle se detiene porque conoce el ltimo elemento. Otra manera de declarar e inicializar un array es asignarle los valores en la misma lnea:

Al hacer eso, ya le hemos dicho al compilador que nuestro array tendr 10 elementos. Se pueden crear los elementos in situ:

email: brujulato@yahoo.es

Pgina 39

Curso gratuito de Java en emuladores L2 Java


4.6.2 Arrays annimos

2012

La otra manera de crear e inicializar un array se la conoce como array annimo. Esto sirve para crear un array justo en el momento de su uso, por ejemplo, para drselo a un mtodo que lo coja como parmetro. El array se crea sin variable de referencia.

4.6.3 Arrays primitivos Los arrays primitivos pueden aceptar cualquier valor que pueda ser promocionado implcitamente al tipo declarado en el array. Por ejemplo, un array de int, puede almacenar cualquier tipo de datos que pueda albergarse en 32bits. As que el siguiente cdigo es vlido:

4.6.4 Array de referencias a objetos Si el tipo de array es una clase, puedes poner objetos de cualquier subclase del tipo declarado en el array. Por ejemplo, si Subaro es subclase de Coche, puedes ponerlo tal como se muestra en el ejemplo:

Esto ayuda a recordar que los elementos en un Coche son solo variables de referencia a Coche. As que cualquier cosa que pueda ser asignada a Coche, puede ser asignada a un array de elementos de tipo Coche.

email: brujulato@yahoo.es

Pgina 40

Curso gratuito de Java en emuladores L2 Java

2012

Si el array es declarado como un tipo de interfaz, los elementos del array pueden referirse a cualquier instancia de cualquier clase que implemente la interfaz. El siguiente cdigo demuestra el uso de una interfaz en un array:

4.6.5 Asignaciones para arrays de una sola dimensin No estamos hablando sobre referencias en el array (en otras palabras, array de elementos), sino ms bien una referencia al array. Por ejemplo, si declaras un array int, la variable de referencia que declaras puede ser reasignada a cualquier array int (de cualquier tamao), pero no puede ser reasignado a cualquier que no sea un array int, incluyendo el valor int. Recuerda que todos los arrays son objetos, as que una referencia a un int no puede referirse a un nmero int. El siguiente cdigo muestra asignaciones vlidas y no vlidas para arrays primitivos:

email: brujulato@yahoo.es

Pgina 41

Curso gratuito de Java en emuladores L2 Java

2012

Es tentador suponer que porque una variable de tipo byte, short, o char puede ser explcitamente casteada y asignada a un int, un array de cualquiera de los tipos podra ser asignado a un array int. No puedes hacer eso en Java. Los arrays que almacenan referencias a objetos, lo opuesto a datos primitivos, no tienen restriccin. As como puedes poner un Honda en Coche (porque Honda hereda Coche), puedes asignar un array de tipo Honda a una variable de referencia tal como sigue:

Aplica el test ES-UN para distinguir lo que se puede de lo que no se puede hacer. Honda ES-UN Coche, as que el array de Honda puede ser asignado al array Car. Beer ES-UN Coche, no es cierto, no hereda Coche, as que no es vlido. Las reglas de los arrays se aplican por igual a las interfaces como a las subclases. Un array de interfaces, puede referenciar un array del tipo que haya implementado la interfaz. Recuerda que cualquier objeto de una clase que implementa una interfaz particular pasara el test de ESUN. Por ejemplo, si Box implementa Foldable, lo siguiente, es vlido:

4.6.6 Asignaciones para arrays de varias dimensiones Cuando asignas un array a un array ya declarado, el array que asignas debe ser de las mismas dimensiones que las referencias que estas asignando. Por ejemplo, un array de dos dimensiones de int no se le puede asignar a un int normal, tal como sigue:

email: brujulato@yahoo.es

Pgina 42

Curso gratuito de Java en emuladores L2 Java

2012

Presta una atencin en especial a la asignacin de los arrays usando dimensiones diferentes. Podras por ejemplo, confundirte al asignar un array int al primero elemento de un array de int, como sigue:

Puedes inicializar los bloques que quieras en una clase. Lo importante a tener en cuenta, es que, al contrario que los mtodos o constructores, el orden en el que los bloques aparecen en la clase, si importa. Cuando es la hora en que tienen que ejecutarse los bloques, si la clase tiene ms de uno, empezaran a ejecutarse en el orden en que aparecen la clase... en otras palabras, desde arriba hacia abajo. Acurdate de estas reglas: Los constructores se ejecutan en el orden en que aparecen. Los bloques estticos solo se ejecutan una vez, cuando la primera clase se haya cargado. Las instancias de los constructores se ejecutan cada vez que la clase se instancia. Las instancia de los constructores se ejecutan despus de la llamada a super().

Este cdigo:

tendr esta salida:

email: brujulato@yahoo.es

Pgina 43

Curso gratuito de Java en emuladores L2 Java

2012

Como puedes ver, las instancias de los bloques init se han ejecutado dos veces. Instanciar los bloques init son a menudo usadas en lugar de poner todo el cdigo en los constructores en la clase que deberan compartir. De esa manera, el cdigo no tiene que estar duplicado a travs de los constructores. Finalmente, si haces un error en tu bloque esttico, la JVM puede arrojar un ExceptionInInitalizationError. Veamos un ejemplo:

producira un error como:

Los bloques estticos se ejecutan antes, sin importar si estn despus:

Este cdigo dar como resultado:

email: brujulato@yahoo.es

Pgina 44

Curso gratuito de Java en emuladores L2 Java


5.1 Declaracin de una enumeracin

2012

Una enumeracin se usa para minimizar an ms los riesgos de equivocacin en un grupo de trabajo. Esta es una buena prctica, adems que facilita la lectura del cdigo. En Java se puede restringir una variable para tener unos valores predefinidos, a esto se le llama lista enumerada o enumeraciones. Digamos por ejemplo que tenemos varios tipos de coches que podemos tener, deportivos, turismo y comerciales. Para evitar que otro programador intente obtener un coche que no existe, digamos 4x4, se restringe los valores de la enumeracin a los que tengas creados. Declarar una enumeracin es tan fcil como esto:

Respetando el convenio de Java, se presentan en maysculas. El uso es igual de sencillo, adems, cuando declaras una enumeracin, el editor (yo uso Eclipse) te ayuda mostrando las opciones disponibles:

email: brujulato@yahoo.es

Pgina 45

Curso gratuito de Java en emuladores L2 Java

2012

De tal manera que la nica forma de conseguir coches ahora es usar las opciones que nos deja disponible la enumeracin:

Los componentes bsicos de una enumeracin son sus constantes. Pueden ser declaradas como clase propia (como en este ejemplo), miembro de una clase, pero no pueden ser declaradas dentro de un mtodo. As se declara como miembro de una clase:

Acurdate que slo puede existir una clase pblica por fichero. Esto sera miembro de una clase:

Es posible que encuentres la declaracin de una enumeracin terminando en ; esto es opcional.

email: brujulato@yahoo.es

Pgina 46

Curso gratuito de Java en emuladores L2 Java


Bueno, y una vez que tengo una enumeracin, que hago?

2012

Antes hay que explicar un poco ms. Una enumeracin no es un tipo String o int. Cada miembro de una enumeracin es una instancia de la misma (o sea, un nuevo clon). As que DEPORTIVO no es slo una constante de la enumeracin, es un tipo de TiposDeCoches. Es decir, al igual que un tipo es un String o un int, pues acabamos de crear tres tipos nuevos de TiposDeCoches. Una enumeracin es como una clase, la diferencia es que a cada valor le corresponde una instancia de la enumeracin, donde la instancia es esttica y final, es decir, no se puede editar su valor, ni se puede hacer una subclase de ella (usar la palabra new) as que realmente se tratan como de una constante. A cada valor de la enumeracin le corresponde un orden de indexado, es decir, la enumeracin sabe en que orden estn los tipos de coche. Ms adelante profundizaremos en este tema, de momento tienes que saber que existe y cmo funciona bsicamente.

email: brujulato@yahoo.es

Pgina 47

Curso gratuito de Java en emuladores L2 Java


5.2 Mtodos y variables en una enumeracin

2012

Una enumeracin adems de declarar tipos estticos y finales, puede ejecutar acciones al igual que una clase, es decir, podemos usar mtodos y variables, y algo raramente conocido como cuerpo de clase constante especfica. Esto es til por ejemplo, si quieres aadir una propiedad a los tipos, como por ejemplo, el nmero de puertas (por decir algo):

Bien, no te asustes, es sencillo y te lo voy a explicar. A cada tipo le hemos asignado un nmero, presumiendo que representan el nmero de puertas de cada tipo de coche. Pero para poder usar estos nmeros es necesario crear un constructor, ese constructor, al igual que en las clases, tiene el mismo nombre que la clase donde se crea. En nuestro caso acepta un parmetro de tipo int que se recoge del valor guardado en el tipo, es decir, nuestro constructor acepta un nmero que representa la cantidad de puertas del tipo de coche. Al igual que las clases, he incluido un mtodo para saber cuntas puertas tiene ese vehculo, getPuertas. No puedes olvidar que en el constructor no se puede invocar directamente. Se convoca automticamente con los argumentos que defines en las constantes. Por ejemplo DEPORTIVO(3) invoca al constructor y le pasa el parmetro 3. Esto lo habrs adivinado, y si no es as, es que necesitas practicar esto un poco. Coge Eclipse (u otro editor) y practica un poco. Al igual que en las clases, puedes sobrecargar el constructor, es decir, crear ms de uno. Tambin puedes hacer cosas extraas, como que parezca una clase annima interna (este tipo de clases la veremos ms adelante), se conoce como cuerpo de constante especfico y se usa cuando necesitas una constante para definir un mtodo definido en la enumeracin. Lo veremos ms claro con un ejemplo.

email: brujulato@yahoo.es

Pgina 48

Curso gratuito de Java en emuladores L2 Java

2012

Imaginemos que necesitamos devolver el tipo de combustible del coche, por defecto son todos gasolina pero el comercial sera diesel.

Puedes fijarte que a la enumeracin se incluye getCombustible, pero en COMERCIAL se han abierto unas llaves donde se define el mismo mtodo, es decir, lo est sobrescribiendo. Mientras que para el DEPORTIVO y el TURISMO el combustible devuelto ser gasolina, el COMERCIAL devolver diesel.

email: brujulato@yahoo.es

Pgina 49

Curso gratuito de Java en emuladores L2 Java


6.1 Encapsulacin

2012

La encapsulacin consiste en el acceso a las variables de la clase mediante un mtodo en lugar de hacerlo directamente. Bueno, y esto para qu sirve? Imagnate que estas creando un cdigo donde ests trabajando con un puado de personas, que no conocen las clases que has hecho, y la misma situacin es la tuya, tu no conoces su trabajo aunque estis participando en el mismo proyecto, te va sonando L2J? Si accedieses directamente a las variables podras pasarle tipos errneos. Que es un tipo? Un tipo es cada clase creada para trabajar con ellas, cada objeto que se crea o existe en Java, es un tipo. Otra ventaja es el mantenimiento del cdigo. Si en tu cdigo hubiera que corregir el cdigo y otro programador usase tu clase mediante los mtodos de la clase, este programador no necesitar saber siquiera los cambios que hagas en tu programa, no se enterar. Por ejemplo:

Aqu tenemos el hipottico caso en que un programador ha creado la clase Arma, y otro programador que la est usando. Como vemos, puede declarar un Arma con una potencia infinita. Si Coder crease un arma con potencia cien millones y resultase que esa arma descompensase el server (porque el jugador se hiciese invulnerable, por ejemplo), habra que arreglar este bug!

email: brujulato@yahoo.es

Pgina 50

Curso gratuito de Java en emuladores L2 Java

2012

Como arreglar este bug sin que Coder tenga que editar su cdigo? Bien, pues Magneto lo nico que tiene que hacer es limitar la potencia del arma:

Magneto rescribe su cdigo, si la potencia es mayor de 100, entonces la potencia es 100. De esta manera Coder no tiene que editar su cdigo, ni se ha enterado, l sigue haciendo lo suyo sin tener que mirar tu cdigo. Los beneficios que recibes pensando en POO es flexibilidad y mantenimiento. La habilidad de hacer cambios en tu cdigo sin romper el cdigo de otros programadores es la clave de la encapsulacin. Cuando creas una serie de mtodos accesibles desde otras clases (y manteniendo tus implementaciones ocultas para proteger tu clase), estas creando APIs. Para conseguir mantenimiento, flexibilidad y extensibilidad debes seguir estas premisas: Las variables de la instancias deben estar protegidas, el modificador de acceso normalmente, privado. Crea mtodos de acceso pblico para forzar a otros programadores a acceder directamente a los mtodos en lugar de a las variables. Usa la convencin de JavaBeans, setNombreMetodo, getNombreMetodo.

email: brujulato@yahoo.es

Pgina 51

Curso gratuito de Java en emuladores L2 Java


Veamos ejemplos:

2012

Como habamos dicho antes, las variables de instancia han de ser privadas, de tal manera que el programador no pueda acceder directamente a ella. Ahora la nica manera de seleccionar la potencia del arma es mediante el mtodo setPotencia.

email: brujulato@yahoo.es

Pgina 52

Curso gratuito de Java en emuladores L2 Java

2012

Encapsulada la variable de clase y protegiendo el exceso de potencia mediante nuestro cdigo, an tenemos un posible error que controlar, os pongo el cdigo:

El error es que Coder an podra crear un arma con potencia -100, pero te diste cuenta demasiado tarde, ya estn usando el programa. Vas a decirle a Coder que ha ingresado un valor negativo para el arma y que la gente se est quejando porque su arma no mata sino que cuenta chistes?

email: brujulato@yahoo.es

Pgina 53

Curso gratuito de Java en emuladores L2 Java


NO! Magneto rescribe su cdigo gracias a que ha hecho uso de la encapsulacin.

2012

y Coder ni se entera.

email: brujulato@yahoo.es

Pgina 54

Curso gratuito de Java en emuladores L2 Java


6.2 Polimorfismo

2012

El polimorfismo es la posibilidad de que una clase hereda las caractersticas de otra clase para crear una nueva. La clave para usar de buena forma el polimorfismo es preguntarte ES UN? Ejemplo, creamos la clase Coche, y luego creamos Ferrari heredando las caractersticas de Coche. Como saber si lo estamos haciendo bien? Plantate la pregunta, Es un Ferrari un Coche? La respuesta es SI. Ahora creas la clase Mercedes heredando las caractersticas de Ferrari. Ambos son coches, parece estar bien, pero si te preguntas Un Mercedes es un Ferrari? La respuesta es NO, as que estas liando los objetos. Con este truco jams te equivocars. Recuerda, ES UN. Para usar esta caracterstica usamos la palabra clave extends. En que consiste la herencia? La herencia consiste en heredar todas las propiedades de una clase. La utilidad de esto es que aadiendo nuevas propiedades creas nuevos objetos. Ejemplo, la clase Coche lleva ruedas, puertas, frenos, motor. La clase Ferrari lleva adems de lo que lleva un Coche, un caballo como smbolo, el constructor es Minielli y el diseador es Montiveri (por decir cualquier cosa). La clase Spider que hereda a Ferrari, adems de lo que lleva Ferrari, y de lo que lleva un Coche, mide 5 metros y alcanza los 295Km/h. Podramos sacar una nueva clase de Ferrari heredando Ferrari, o podramos sacar un Coche nuevo como Mercedes heredando Coche, o incluso podramos sacar un CocheVoladorSubmarino heredando Coche y aadiendo las caractersticas de un coche que vuele y se sumerja.

email: brujulato@yahoo.es

Pgina 55

Curso gratuito de Java en emuladores L2 Java

2012

En este ejemplo vemos cmo se crea Coche, CocheVolador y CocheVoladorSubmarino, donde CocheVolador aparte de tener alas y flaps propias de un avin, hereda rueda, frenos, puertas y chasis. Ms complejo aun es el CocheVoladorSubmarino, que aparte de llevar timn, vela y mastil propias de un barco, lleva alas y flaps del CocheVolador, y como el CocheVolador hereda Coche, CocheVoladorSubmarino tambien hereda ruedas, frenos, puertas y chasis. Aparte de sus variables de instancia, tambin heredarn sus mtodos, como podra ser frenar() (para el Coche), despegar() (para el coche volador) e inmersin() (para el coche volador submarino). Y si quisiera hacer un CocheTanque? Pues... heredara las propiedades de un Coche y le aado las caractersticas de un Tanque. La nica manera de acceder a un objeto es a travs de una variable de referencia, es decir, la variable que usas con new, en el ejemplo podemos ver que mi referencia a un coche volador submarino es DelfinConAlas. Tienes que saber que la variable de referencia slo puede ser de un slo tipo, es decir, aunque DelfinConAlas es el resultado de la herencia de un Coche y de un CocheVolador, el tipo es CocheVoladorSubmarino y nada ms.

email: brujulato@yahoo.es

Pgina 56

Curso gratuito de Java en emuladores L2 Java

2012

A la variable de referencia le puedes asignar otros objetos, a menos que sea declarada como final. Es decir:

Una variable de referencia tambin puede referirse a cualquier objeto del mismo tipo declarado en la referencia, incluso puede hacer referencia al subtipo declarado.

A esto se le llama casteo que lo veremos en profundidad. En el ejemplo vemos que declaro un CocheVoladorSubmarino donde la variable de referencia est referida a un CocheVolador. Como es del mismo subtipo, el compilador no lanza error y lo acepta como correcto. Una variable de referencia tambin puede ser declarada como un tipo de clase o un tipo de interfaz. Si se declara como un tipo de interfaz puede referenciar cualquier objeto de cualquier clase que implemente la interfaz.

email: brujulato@yahoo.es

Pgina 57

Curso gratuito de Java en emuladores L2 Java

2012

En el ejemplo la variable de referencia est declarada como interfaz Frenar pero se inicializa como CocheVoladorSubmarino, ya que CocheVoladorSubmarino implementa la interfaz Frenar. Al implementar una interfaz (lo habamos visto antes) se deben implementan los mtodos declarados, pero el cdigo de los mtodos abstractos tiene que ser escrito por ti. Fjate que la nica clase que implementa los mtodos es Coche, CocheVolador y CocheVoladorSubmarino no lo escriben. Eso es, como ya dije antes, que heredan sus mtodos, y no hace falta volver a escribirlos. Es ms, si se rescriben, estaras sobrescribiendo los mtodos de la clase que se hereda, o sea, creando unos nuevos. Esto est bien cuando el cdigo del mtodo no es suficiente para la clase que lo hereda.

email: brujulato@yahoo.es

Pgina 58

Curso gratuito de Java en emuladores L2 Java


6.3 Casteo de variables de referencia

2012

Como hemos visto en el tema anterior, un casteo consiste en cambiar de un tipo a otro tipo, siendo el otro tipo un subtipo, es decir, un casteo de Ferrari a Coche es posible, pero no uno de Ferrari a CocheVoladorSubmarino. Es un Ferrari un CocheVoladorSubmarino? NO! Acurdate siempre de este truco. En el casteo existen dos direcciones, subir en la escala de la herencia, que se denomina upcasting y el compilador funciona explcitamente (es decir, no tienes que indicarle que ests haciendo un casteo) y bajar en la escala de herencia, denominado downcasting, donde si tienes que indicar explcitamente el casteo. Ejemplo:

Aqu vemos que se declara c1 como Clase1 y se hace un casteo a la Clase4. Como la herencia es ascendente, no hace falta indicar expresamente el casteo. Pero al hacerlo al revs (downcasting):

el compilador se queja, indicar el casteo se hace obligatorio.

email: brujulato@yahoo.es

Pgina 59

Curso gratuito de Java en emuladores L2 Java


Pero, qu pasa si una vez casteado, queremos usar el mtodo de otro tipo?

2012

El casteo ahora est hecho correctamente, el compilador est feliz por la aclaracin, pero al ejecutar el programa, sorpresa...

Por qu ocurre esto? Por qu el compilador no nos ayuda y ver que Clase1 y Clase4 son del mismo subtipo? El compilador lo nico que puede hacer es confirmar que ambos tipos pertenecen al mismo rbol de la herencia. En un upcasting, el nmero de mtodos se restringe, as que no existe la posibilidad de que un mtodo de una clase se haya sobrescrito, al contrario que ocurre con un downcasting. El punto es que ninguna referencia de tipo Clase4 puede hacer llamadas seguras a una instancia de Clase1. Ojo, con esto no digo que el downcasting est prohibido. Si el compilador ve que hay posibilidad de que en tiempo de ejecucin funcione, compilar.

email: brujulato@yahoo.es

Pgina 60

Curso gratuito de Java en emuladores L2 Java


En este ejemplo vemos como el downcasting si est permitido. Explico el ejemplo. La clase Object es la clase de la que derivan todas las clases en Java, es la primera clase. La clase String es una clase derivada de Object que contiene cadenas de texto.

2012

Como la referencia o es una cadena de texto, cuando hacemos downcasting de Object a String, el compilador ve que la referencia es una cadena de texto, el mismo tipo que hace referencia o, y no da error. Esto compila sin problemas.

Aunque es raro usar esto de esta manera, nunca est de ms saber que existe y como funciona.

email: brujulato@yahoo.es

Pgina 61

Curso gratuito de Java en emuladores L2 Java


6.4 Implementando una interfaz

2012

Al crear interfaces te aseguras que otro programador seguir las reglas que tu creaste para el comportamiento de una clase. Por ejemplo, si creas una interfaz como Volador te aseguras que alguien que cree un objeto que vuele tenga los mtodos que necesitas para poder hacer que ese objeto vuele.

En el ejemplo vemos la implantacin forzosa de los mtodos que nos supone usar la interfaz Volador. Los comentarios los aade automticamente Eclipse al aceptar implementar los mtodos de la interfaz. @Override significa sobrescribir, y eso es lo que tenemos que hacer, crear cdigo para que esta clase pueda volar. Esos mtodos sern invocados desde otras clases que ya presumen que todos los objetos que vuelan podrn realizar las acciones que indica la interfaz.

email: brujulato@yahoo.es

Pgina 62

Curso gratuito de Java en emuladores L2 Java

2012

Aunque el compilador no se va a quejar de que no pongas nada en los mtodos que implementas, no quiere decir que ests haciendo una buena implementacin, por eso hay que seguir esta serie de reglas: Proveer de implementaciones no abstractas de todos los mtodos de la interfaz. Usar las reglas de la sobrescritura, es decir, debe devolver el mismo tipo. Declarar las excepciones no declaradas en los mtodos implantados que son declarados en el mtodo de la interfaz o subclase. Veremos cmo se hace esto ms adelante. Mantener el nombre del mtodo de la interfaz y el mismo tipo o subtipo.

Una implementacin tambin puede ser abstracta por si misma, es decir, una interfaz puede heredar otra interfaz, o ms de una, y no tiene que declarar los mtodos.

email: brujulato@yahoo.es

Pgina 63

Curso gratuito de Java en emuladores L2 Java

2012

Mientras que una clase slo puede heredar una clase, una interfaz puede heredar mltiples clases. Pero una interfaz no puede implementar nada aunque una clase puede implementar mltiples clases.

Las interfaces son siempre pblicas, aunque no lo declares explcitamente. Los mtodos implementados de la interfaz, han de ser pblicos, no puedes usar los modificadores private, final, protected, o default. Recuerda, que extends se usa para heredar otra clase, e implements se usa para clases abstractas o interfaces. Las clases abstractas tienen mtodos no implementados (sin cuerpo) y deben ser sobrescritos.

email: brujulato@yahoo.es

Pgina 64

Curso gratuito de Java en emuladores L2 Java


6.5 Tipos de retornos en constructores sobrecargados

2012

Sobrecargar un mtodo es crear otro mtodo con el mismo nombre pero aceptando diferentes parmetros.

Tambin se puede sobrecargar un mtodo cuando una clase hereda a otra (heredando sus mtodos) y rescribes el mtodo.

Cuando se crean mtodos que devuelven tipos distintos, segn los parmetros dados en el mtodo, el tipo de retorno es distinto.

Lo que no puedes hacer es rescribir el mtodo en lugar de sobrecargarlo, as sin ms:

email: brujulato@yahoo.es

Pgina 65

Curso gratuito de Java en emuladores L2 Java

2012

Si quieres rescribir el mtodo en tu subclase, es decir, sobrescritura, puedes cambiar el tipo de retorno si el tipo es un subtipo de la superclase.

En este ejemplo int y byte son distintos tipos, pero pueden ser casteados, es decir, si el valor de int no supera el valor de 255 que es el rango mximo de byte, puede funcionar sin errores en tiempo de ejecucin. Otro ejemplo:

Aqu Main hereda Uno, as que Main automticamente es subtipo de Uno, por eso el retorno del tipo es correcto.

6.5.1 Devolviendo un valor Hay cinco sencillas reglas para devolver un valor. 1. Puedes devolver null en un mtodo con un objeto de referencia de tipo return.

email: brujulato@yahoo.es

Pgina 66

Curso gratuito de Java en emuladores L2 Java

2012

2. Puedes devolver un array. Aunque no hemos visto como se construye un array, en el ejemplo se puede observar que se incluye unos corchetes [] al lado del tipo de devolucin, para indicar que se trata de un array, y dentro del mtodo, al inicializar la variable con los valores.

3. En un mtodo con tipo de retorno primitivo, puedes devolver cualquier valor o variable que pueda ser convertida implcitamente al tipo declarado. Esto es lo que vimos antes del casting.

4. Si el mtodo usa void (vaco) se presume que no devuelve nada, o sea, un valor vaco. Hacer que devuelva un valor es ilegal:

5. Un mtodo que use un objeto de referencia, puede devolver cualquier tipo que pueda ser casteado implcitamente al tipo de retorno.

email: brujulato@yahoo.es

Pgina 67

Curso gratuito de Java en emuladores L2 Java


6.6 Autoboxing Esta caracterstica consiste en convertir tipos automticamente, y es una caracterstica aadida en Java 5. Antes de existir esto, era ms complicado hacer una conversin evidente:

2012

y ahora:

Donde se puede hacer autoboxing? La regla general es que el boxing y unboxing funcionan cuando normalmente usas un dato primitivo o un envoltorio (o sea, el nombre del objeto como Integer).

email: brujulato@yahoo.es

Pgina 68

Curso gratuito de Java en emuladores L2 Java


7.1 Construccin, reglas y sobrecargas de constructores

2012

Cuando instancias un objeto con new se invoca el constructor de la clase siempre, pero adems de esto, se ejecuta el constructor de la superclase, es decir, se ejecutan todos los constructores de su superclase, y la ltima clase, de donde nacen todas las clases es Object. Un constructor por defecto es como esto:

Lleva el mismo nombre de la clase y entre llaves no hay nada. Este constructor no es necesario crearlo, el compilador lo hace por ti. Los constructores normalmente se usan para inicializar las variables de la instancia, es decir, los valores de las variables de la clase instanciada:

La palabra clave this se usa para hacer hincapi en que nos referirnos a la variable de instancia, no a las variables del mtodo que tienen el mismo nombre. Adems, Eclipse pinta de azul las variables de la instancia siempre que entienda que te refieres a ellas.

email: brujulato@yahoo.es

Pgina 69

Curso gratuito de Java en emuladores L2 Java

2012

Cuando creas un constructor, el constructor por defecto se omite, as que si quieres crear un Circulo sin pasar los parmetros que has establecido, dar error:

Las opciones que muestra Eclipse para arreglar el error es: Aadir los argumentos que encajen con Circulo(int, int, int) Cambiar el constructor Circulo(int, int, int): Quitar los parmetros int, int, int Crear el constructor Circulo() Como ves, si uno de los consejos es crear el constructor que debera estar por defecto, es que ya no est ah. Aunque sea deseable siempre tener un constructor sin argumentos para una clase, muchas veces, como en el ejemplo, no tiene sentido u ocasionara un error. Puedes crear tantos constructores como quieras dentro de una clase. Las reglas para crear un constructor: Los constructores pueden usar cualquier modificador de acceso. El nombre del constructor tiene que ser el mismo que el de la clase. Los constructores no devuelven nada (no tienen return). Es legal aunque estpido tener un mtodo con el mismo nombre que la clase. Esto no significa que sea un constructor. Si no se crea un constructor, el compilador crea uno por defecto, aunque no se vea en el cdigo. El constructor por defecto nunca lleva argumentos. Si has escrito un constructor y quieres otro sin argumentos, tendrs que hacerlo t mismo. Cada constructor invoca implcitamente a this o super. Si no incluyes this o super, el compilador lo har por ti, con o sin argumentos. No se puede invocar a ninguna parte de una instancia hasta que su constructor se haya ejecutado. Solo las variables estticas y mtodos pueden ser accedidas con super o this.

email: brujulato@yahoo.es

Pgina 70

Curso gratuito de Java en emuladores L2 Java


2012

Las clases abstractas tambin tienen constructores, son ejecutados cuando la subclase que los instancia son ejecutadas. Las interfaces no tienen constructores, como no son parte de ningn objeto, no forman parte de ningn rbol de herencia.

email: brujulato@yahoo.es

Pgina 71

Curso gratuito de Java en emuladores L2 Java


7.2 Encadenamiento de constructores

2012

Los constructores solo se ejecutan cuando usamos new en alguna clase, pero no slo se ejecuta este nico constructor. Al iniciar un constructor, tambin se inicia el de su superclase, hay un comando que se usa implcitamente, se ejecuta super(), y sirve para invocar a la superclase, y as sucesivamente hasta llegar a la superclase de todas las clases, Object. Para ver el efecto domin, miremos este ejemplo:

La clase Main crea una instancia de la Clase4, pero fjate que antes de ejecutarse el constructor de la clase 4, se ejecuta primero el de la superclase ms alta hasta llegar a la clase 4. Cuando se ejecuta un mtodo sin usar super, en este ejemplo, info, se ejecuta el de la clase que sobrescribe al de la superclase:

email: brujulato@yahoo.es

Pgina 72

Curso gratuito de Java en emuladores L2 Java

2012

Pero si quitamos el mtodo de la clase 4, se ejecuta el de la clase 1, lo mismo que si hubiramos usado super(), solo que el compilador ya lo hace por nosotros:

Aqu inserto super() en la clase 4:

En este ejemplo, se usa super para invocar al constructor de la superclase, pero si te fijas, al pasarle solo un argumento, se ejecuta el constructor de la clase2 sin arrojar error:

Al pasar 2 argumentos da error, porque no hay constructores con dos argumentos:

email: brujulato@yahoo.es

Pgina 73

Curso gratuito de Java en emuladores L2 Java


Finalmente, si instanciamos la clase 1, la clase 2 podr leer las variables de la superclase:

2012

email: brujulato@yahoo.es

Pgina 74

Curso gratuito de Java en emuladores L2 Java


8.1 Mtodos y variables estticas Hay que hacer una distincin entre las variables y variables marcadas como estticas.

2012

Las variables no estticas se resetean en cada nueva clase instanciada, mientras que la variable esttica pertenece a la clase, en lugar de a la instancia. Estudiemos este ejemplo:

Mientras que numero ha sido declarado sin el modificador static, es inicializado en cada instancia de la clase, mientras que conteo suma uno cada vez que se ejecuta el constructor, es decir, cada vez que se usa new. En ambas clases instanciadas, conteo tiene el mismo valor, ya que es esttico, no es una variable de la instancia, sino que es una variable esttica, que pertenece a la clase, no a sus copias. Esto tambin es aplicable a los mtodos estticos.

email: brujulato@yahoo.es

Pgina 75

Curso gratuito de Java en emuladores L2 Java


8.2 Accediendo a mtodos y variables estticas

2012

En el momento en que no necesitas una instancia para acceder a los mtodos o variables estticas usaremos el operador punto(.). Reglas que debes conocer:

Adems los mtodos estticos no pueden ser rescritos.

email: brujulato@yahoo.es

Pgina 76

Curso gratuito de Java en emuladores L2 Java


9.1 Funcionamiento del recolector de basuras

2012

Vamos a ver lo que queremos decir cuando hablamos del recogedor de basuras en la tierra de Java. A diez mil metros de altura, el recolector de basuras es la frase que se usa para describir el manejo de memoria en Java. Cuando un programa se ejecuta (en Java, C, C++, Lisp, Ruby...) usa la memoria de diferentes maneras. No vamos a meternos en la ciencia 101 de la computacin, pero lo tpico de la memoria es que se use para crear una pila en el Heap, que es la piscina donde Java pone sus mtodos y sus constantes. El Heap es la parte de la memoria donde los objetos de Java viven, y es una y nica parte de memoria que esta de cualquier modo envuelta en el proceso del recolector de basuras. As, que la directiva del recolector de basuras es liberar al Heap, para dejar libre el mayor espacio de memoria disponible. Lo que hace es borrar los objetos que no son alcanzables por el programa Java que est corriendo. Hablaremos ms cuando lleguemos al siguiente punto. Cuando el recolector de memoria se ejecuta, su propsito es encontrar y borrar objetos que no pueden ser alcanzados. Imagina un programa Java que es el comienzo de un bucle de creacin de objetos (que estn en el Heap), y que elimina los objetos cuando no se necesiten ms, crear nuevos objetos, descartarlos, etc... la pieza que falta es el recolector de basuras. Cuando se ejecuta, buscara esos objetos descartados y los borrara de la memoria descargndola para que pueda continuar. Ah, el gran circulo de la vida.

9.1.1 Cuando se ejecuta el recolector de basuras? El recolector de basuras funciona bajo el control de la JVM. La JVM decide cuando se ejecuta el recolector de basuras. Dentro de tu programa Java puedes pedir a la JVM que ejecute el recolector de basuras, pero no hay garantas, bajo ninguna circunstancia, de lo que la JVM va a hacer. La JVM ejecutar el recolector cuando la memoria alcance mnimos. La experiencia me indica que cuando le pides a la JVM que ejecute el recolector, la JVM garantiza que ser ejecutado en corto plazo y justo cuando crees que puedes contar con ello, JVM omite tu peticin

9.1.2 Cmo funciona el recolector de basuras? No puedes estar seguro. Habrs podido escuchar que el recolector de basuras funciona con un algoritmo de barrido, y para cualquier implementacin de Java puede ser cierto, pero la especificacin de Java no garantiza ninguna implementacin en particular. Podras haber odo que el recolector de basuras usa un conteo de referencia, una vez quizs si, quizs no. El concepto ms importante de es: Cuando el objeto es elegido para ser anulado por el recolector de basuras? Para contestar a esta pregunta, tenemos que dar un salto hacia delante y hablar de hilos. En un programa de Java, cada programa tiene desde uno a varios hilos. Cada hilo tiene su propia pequea pila de ejecucin. Normalmente, tu (el programador) crea al menos un hilo para

email: brujulato@yahoo.es

Pgina 77

Curso gratuito de Java en emuladores L2 Java

2012

ejecutar el programa, el primero con el mtodo main() en el fondo de la pila. Sin embargo, como aprenders en detalle ms adelante, hay muchas razones para lanzar hilos de ejecucin adicionales desde tu hilo principal. Aparte de tener su propia pila de ejecucin, cada hilo tiene su ciclo de vida. Por ahora, todo lo que necesitamos saber es que los hilos pueden estar vivos o muertos. Con esta informacin, podemos decir con contundente claridad que un objeto es elegido para el recolector de basuras cuando no hay hilos vivos accediendo a el. Basndonos en esta definicin, el recolector de basuras hace algo de magia, operaciones desconocidas, y cuando descubre un objeto que no puede ser alcanzado por un hilo vivo, considera que el objeto es elegible para ser eliminado, puede llegar incluso a borrarlo en el mismo momento. (Si, lo adivinaste, puede ocurrir que tampoco lo llegue a borrar nunca). Cuando hablamos sobre el alcance de un objeto, estamos hablando realmente sobre tener una variable de referencia alcanzable que se refiera al objeto en cuestin. Si nuestro programa Java tiene una variable de referencia que hace referencia al objeto, y esa variable de referencia est disponible en un hilo vivo, entonces ese objeto se le considera alcanzable. Hablaremos ms sobre como los objetos puede convertirse en inalcanzables en la siguiente seccin. 9.1.3 Puede una aplicacin Java quedarse sin memoria? S. El recolector de basuras intenta borrar todos los objetos de la memoria cuando no se usan. Sin embargo, si mantienes muchos objetos vivos a la vez (objetos referenciados por otros objetos) el sistema puede llegar a quedarse sin memoria. El recolector de basuras no puede asegurar que haya suficiente memoria, solo que la memoria disponible ser manejada de la forma ms eficiente posible.

9.2 Escribir cdigo que maneje explcitamente objetos elegibles para ser borrados En esta seccin vamos a mostrar cmo hacer objetos elegibles para el recolector de basuras usando el cdigo actual. Tambin vamos a discutir cmo forzar al recolector de basuras si es necesario, y como podemos ejecutar una limpieza adicional en objetos antes de que sean borrados de la memoria. Una referencia nula Como hemos visto antes, un objeto se convierte en apto para ser borrado cuando no hay referencias que lo apunten. Obviamente, sino hay referencias, no importa lo que le ocurra al objeto. Para nuestro propsito es solo algo flotando en el espacio, sin usar, inaccesible y que ha dejado de necesitarse. La primera manera de borrar una referencia a un objeto es seleccionar la variable de referencia que apunta al objeto y volverlo null. Examina el siguiente cdigo:

email: brujulato@yahoo.es

Pgina 78

Curso gratuito de Java en emuladores L2 Java

2012

El StringBuffer con el valor "hello" se le asigna la variable sb en la tercera linea. Para hacer este objeto apto para el GC (Garbage Collector, recolector de basuras), le asignamos a sb el valor de null, lo que elimina la nica referencia que exista al objeto StringBuffer. Una vez que la lnea 6 se ejecuta, nuestro pequeo "hello" est condenado para el GC.

9.3 Reasignado una variable de referencia Tambin podemos desemparejar una variable de referencia de un objeto asignndole a la referencia, otro objeto. Examina este cdigo:

Los objetos creados en un mtodo tambin necesitan ser considerados. Cuando un mtodo se invoca, ninguna variable local existe ms all de la duracin del mtodo. Una vez el mtodo ha terminado, el objeto creado en el mtodo es apto para el GC.

email: brujulato@yahoo.es

Pgina 79

Curso gratuito de Java en emuladores L2 Java

2012

Hay una excepcin obvia. Si se devuelve un objeto del mtodo, su referencia puede ser asignada a una variable de referencia en el mtodo que lo llam, as que, no ser apto para la coleccin. Examina este cdigo:

En el cdigo hemos creado un mtodo llamado getDate() que devuelve un objeto Date. Este mtodo crea dos objetos: un Date y un StringBuffer que contiene la informacin. En el momento que el mtodo devuelve el objeto Date, no ser apto para la GC aunque el mtodo se haya completado. El StringBuffer ser apto, aunque no lo hayamos hecho explcitamente, la variable ahora tiene asignado un null.

9.4 Aislando una referencia Hay otra manera en la que los objetos se conviertan en aptos para la GC, aunque tengan unas referencias vlidas. Nosotros llamamos a este escenario "isla de la soledad". Un ejemplo simple es que una clase que tiene una variable de instancia que es variable de referencia a otra instancia de la misma clase. Ahora imagnate que las dos instancias de la misma clase existen y que la una y la otra estn apuntndose. Si las otras referencias a estos objetos son eliminadas, entonces incluso aunque estas referencias sean vlidas, no hay hilo que acceda a estas variables de referencia. Cuando el GC se ejecuta, puede normalmente, encontrar estas islas y eliminarlas. Como te puedes imaginar, estas islas pueden llegar a ser bastante grandes, tericamente contiene cientos de objetos.

email: brujulato@yahoo.es

Pgina 80

Curso gratuito de Java en emuladores L2 Java


Examina el siguiente cdigo:

2012

Cuando el cdigo alcanza //, las tres islas previamente conocidas como i2, i3 e i4, tienen variables de instancias que apuntan las unas a las otras, pero sus enlaces hacia el mundo, ha sido anuladas. Estos tres objetos son aptos para la GC. Esto cubre todo lo que necesitaras saber sobre crear objetos aptos para la GC.

9.5 Forzando el recolector de basuras La primera cosa que debera de mencionarse aqu es eso, contrariamente al ttulo de esta seccin, el GC no puede ser forzado. Sin embargo, Java provee de mtodos que te permiten solicitar que la JVM efectu la ejecucin del GC. Por ejemplo, si estas a punto de ejecutar algunas operaciones sensibles, probablemente querrs minimizar el riesgo de lag causado por el recolector de basuras. Pero tienes que recordar que los mtodos que Java provee son peticiones, no demandas, la JVM har lo mejor respecto a lo que solicitas, pero no hay garantas de que efectuar la peticin. En realidad, solo es posible sugerir a la JVM que ejecute el GC. Sin embargo tampoco hay garantas de que elimine todos los objetos sin usar de la memoria (aunque el GC se ejecute). Es esencial que entiendas ese concepto para el examen. La rutina del GC que Java suministra son miembros de la clase Runtime. Esta es una clase especial que solo tiene un objeto (un Singleton) para cada programa. El objeto Runtime provee de mecanismos para comunicar directamente con la mquina virtual. Para conseguir una instancia de Runtime, puedes usar el mtodo Runtime.getRuntime(), que te devuelve el Singleton. Una vez que lo tengas, puedes invocar al recolector de basuras usando el mtodo gc(). Alternativamente, puedes invocar al mismo mtodo en la clase System, que es un mtodo esttico que puede funcionar obteniendo el Singleton para ti. La manera ms simple para pedirle al GC que se ejecute es:

email: brujulato@yahoo.es

Pgina 81

Curso gratuito de Java en emuladores L2 Java

2012

Tericamente, despus de llamarlo, tendrs mucha memoria libre. Decimos tericamente porque esta rutina no funciona siempre de esa manera. Primero, tu JVM no puede tener implementada esta rutina; la especificacin del lenguaje no permite hacer nada. Segundo, otro hilo puede ganar un montn de memoria en el momento que se ejecute el GC. Esto no es decir que System.gc() es un mtodo intil, es mucho mejor que nada. Simplemente no puedes confiar en System.gc() para liberar suficiente memoria, as que no tienes que preocuparte sobre quedarte sin memoria. Ahora que de alguna manera nos hemos familiarizado con cmo funciona, hagamos un pequeo experimento para ver si podemos ver los efectos del GC. El siguiente programa nos permitir saber cunta memoria tiene disponible la JVM y cuanta ha libreado. Esto va a crear 10.000 objetos Date. Despus, le dir cuanta memoria queda, llamar al GC (el cual decidir si se va a ejecutar, el programa se detendr hasta que todos los objetos sean eliminados). La memoria final debera indicarse cuando se haya ejecutado. Veamos el programa:

Ahora veamos el resultado:

Como puede verse, la JVM ha decidido recolectar la basura de los objetos aptos para ser eliminados. En el ejemplo, le sugerimos a la JVM que ejecutase el GC con 458Kb de memoria restante, y nos honr con su aparicin. Este programa solo tiene un hilo corriendo, as que no haba nada ms funcionando cuando fue ejecutado. Ten en mente que el comportamiento de gc() cuando es ejecutado puede ser diferente, as que no hay garanta que los objetos sin uso

email: brujulato@yahoo.es

Pgina 82

Curso gratuito de Java en emuladores L2 Java

2012

sean eliminados de la memoria. La nica cosa que se garantiza es que el GC funcionara antes de arrojar una excepcin OutOfMemoryException.

9.6 Limpiar antes de ejecutar el GC - el mtodo finalize() Java provee de un mecanismo para ejecutar un cdigo justo despus de que tu objeto sea borrado por el GC. Este cdigo est en un mtodo llamado finalize() que todas las clases heredan de Object. Sobre el tapete suena como una gran idea, quizs tu objeto consuma algo ms de recursos, y te gustara cerrarlo antes de que sea borrado. El problema es ese, que no puedes contar con el GC siempre para borrar un objeto. As, que cualquier cdigo que pongas dentro de tu clase, el mtodo finalize() tampoco te garantiza que se vaya a ejecutar, as que tampoco es recomendable que pongas cdigo esencial dentro de este mtodo. El caso, es que recomendamos que no sobrescribas el mtodo finalize(). Un pequeo truco para finalize() Hay un par de conceptos concernientes a este mtodo que necesitaras recordar: Para cualquier objeto dado, el mtodo ser llamado una vez por el GC. Llamar a este mtodo puede acabar en que el objeto sea salvado del borrado. Miraremos estos estamentos ms adelante. Lo primero de todo, recordar que el cdigo que pongas dentro de un mtodo normal, puedes ponerlo en finalize(). Por ejemplo, en el mtodo finalize() podras escribir el cdigo que pasa una referencia a un objeto en cuestin hacia otro objeto, efectivamente, el objeto acaba en el GC. Si en el mismo punto, ms tarde en este mismo objeto se convierte en apto para el GC, el GC puede pausar este proceso y borrarlo. El GC sin embargo recordar, que en este objeto, finalize() ya se estaba ejecutando, y no va a ser ejecutado otra vez.

email: brujulato@yahoo.es

Pgina 83

Curso gratuito de Java en emuladores L2 Java


10. Operadores

2012

Si tienes variables, tendrs que modificarlas. Necesitars incrementarlas, sumarlas, compararlas (hay como una docena de formas). En este captulo, aprenders como se hace todo eso en Java. Los operadores de Java producen nuevos valores desde uno o ms operandos (solo queremos dejarlo todo claro, recuerdo que los operadores son las cosas a la izquierda y derecha del operador). El resultado de la mayora de las operaciones son un valor booleano o numrico. Ya que sabes que Java no es C++, no vas a sorprenderte de que los operadores de Java no son sobrecargados. Sin embargo existen unas pocas excepciones donde los operadores pueden venir sobrecargados. El operador + puede ser usado para aadir dos nmeros o para hacer una concatenacin si el operando es una cadena. Los operadores &, |, y ^ pueden ser usados de dos formas distintas, aunque en esta versin del examen, sus capacidades de bit no sern testadas.

10.1 Operadores de asignacin Cuando asignamos un valor a un primitivo, el tamao importa. Pero asegrate de saber cundo hay un casteo implcito, cuando es el casteo explcito necesario y cuando puede ocurrir la truncacin (por ejemplo, acurdate de que un int puede entrar en un byte siempre que no supere el rango). Recuerda que una variable de referencia no es un objeto, es una manera de conseguir un objeto. (Sabemos que todos los programadores de C++ se mueren por decirnos que eso es un puntero, pero nosotros no.) Cuando asignamos un valor a una variable de referencia, el tipo importa. Recuerda las reglas para los supertipos, subtipos y arrays. Ahora cubriremos unos pocos de detalles de los operadores de y ms adelante, miraremos cmo funciona el asignador = con las cadenas (las cuales, son inmutables).

10.2 Operadores de asignacin compuestos Actualmente hay 11 asignadores, pero solo cuatro son los ms usados (+=, -=,*= y /=) en el examen (depende del objetivo). Los operadores de asignacin compuestos son la manera ms fcil de ahorrar unos pocos golpes de teclado. Aqu hay varios ejemplos de asignaciones, primero, sin usar el operador de compuesto,

email: brujulato@yahoo.es

Pgina 84

Curso gratuito de Java en emuladores L2 Java


Ahora con los operadores compuestos:

2012

Por supuesto, los resultados sern los mismos.

10.3 Operadores relacionales Hay seis operadores relacionales (<, <=, >, >=, == y !=). Los operadores relacionales siempre dan como resultado un booleano (true o false). Este valor booleano es usado a menudo como sigue:

pero el valor resultante puede ser tambin asignado directamente de booleano a primitivo:

Java tiene cuatro operadores relacionales que pueden ser usados para usarse en la comparacin de cualquier combinacin de enteros, decimales o caracteres:

> mayor que >= mayor o igual que < menor que <= menor o igual que

Observemos algunas comparaciones validas:

email: brujulato@yahoo.es

Pgina 85

Curso gratuito de Java en emuladores L2 Java

2012

En el cdigo anterior, usamos una comparacin entre caracteres. Tambin es legal comparar un carcter primitivo con un nmero (aunque no sea un gran estilo de programacin). Al ejecutar el cdigo, tendremos la siguiente salida:
The animal is a gray elephant

Hemos mencionado que los caracteres pueden ser usados en operadores de comparacin. Cuando comparamos un carcter con otro carcter, o un carcter con un nmero, Java usara el valor Unicode del valor del carcter como valor numrico para la comparacin.

10.4 Operaciones de igualdad Java tiene dos operadores de igualdad (tambin llamados operadores relacionales) que comparan dos cosas similares y devuelve un valor booleano que representa true o false de las cosas que se estn comparando. Estos operadores son:

== igual (mejor conocido como igual que) != no igual (mejor conocido como no igual que)

Cada comparador individual puede involucrar dos nmeros (incluyendo los caracteres), dos valores booleanos, o dos variables de referencia a objetos. En todo caso, no puedes comparar tipos incompatibles. Cual sera una respuesta si preguntamos si un booleano es igual que un carcter? o si un botn es lo mismo que una cadena de texto? (Exactamente, tonteras, porque no tienen sentido). Hay cuatro tipos diferentes de cosas que pueden ser comparadas:

nmeros caracteres booleanos primitivos variables de referencia a objetos

As que, que compara == (son dos signos de =)? El valor en la variable, en otras palabras, el patrn de bits.

10.5 Comparacin de primitivos La mayora de programadores estn familiarizados con la comparacin de valores primitivos. El siguiente cdigo muestra algunos test de igualdad sobre variables:

email: brujulato@yahoo.es

Pgina 86

Curso gratuito de Java en emuladores L2 Java

2012

El programa mostrara: character 'a' == 'a'? true character 'a' == 'b'? false 5 != 6? true 5.0 == 5L? true true == false? false Como puedes ver, normalmente si un numero decimal es comparado con un entero y el valor es el mismo, el operador == (dos signos de igualdad =) devuelve true, tal como se esperaba.

10.6 Igualdad para variables de referencia Tal como vimos antes, las dos variables de referencia pueden referirse al mismo objeto, tal como demuestra este cdigo:

Despus de ejecutar este cdigo, ambas variables estn apuntando al mismo objeto. Las variables de referencia pueden ser comprobadas para ver si son el mismo objeto usando el operador igual que(==). Recuerda que este operador examina los bits contenidos en la variable, as que para las variables de referencia significa que si ambas referencias contenidas son iguales, entonces se refieren al mismo objeto. Examina el siguiente cdigo:

Este objeto crea tres variables de referencia. Las primeras dos son dos objetos JButton distintos, mientras que a la tercera se le asigna la primera referencia. Cuando este programa se ejecuta, se muestra el siguiente resultado:
Is reference a == b? false Is reference a == c? true

Esto nos muestra que la primera y tercera referencia son la misma instancia de JButton. El operador "igual que" no har la comparacin si los objetos son significativamente

email: brujulato@yahoo.es

Pgina 87

Curso gratuito de Java en emuladores L2 Java

2012

equivalentes, un concepto que cubriremos con las colecciones, cuando veamos el mtodo equals() (lo opuesto a lo que estamos viendo aqu).

10.7 Igualdad para enumeraciones Una vez que has declarado una enumeracin, no se puede expandir. En tiempo de ejecucin no hay manera de aadir nuevas constantes. Naturalmente, puedes tener tantas variables como quieras, as que es importante el poder comparar dos variables de referencia de enumeracin para ver si son iguales, por ejemplo, para ver si se refieren a la misma constante de enumeracin. Tambin puedes usar el operador == o el mtodo equals() para determinar si las dos variables se estn refiriendo a la misma constante de enumeracin:

Sabemos que }} es bastante feo de ver, pero te estoy preparando. Esto da como salida:
== dot equals

10.8 Comparacin con instanceof() Este mtodo se usa solo para las variables de referencia, y puedes usarlo para saber si un objeto es de un tipo particular. Por tipo, queremos decir la clase o la interface, en otras palabras, si el objeto referido por la variable de la izquierda pasa el test de ES-UN para el tipo de clase o interface de la derecha

esto imprime:
s is a String

Incluso si el objeto que est siendo testeado no es una instancia del tipo de la clase del lado de la derecha del operador, instanceof devolver true si el objeto es compatible con el operando de la derecha.

email: brujulato@yahoo.es

Pgina 88

Curso gratuito de Java en emuladores L2 Java

2012

El siguiente ejemplo demuestra el uso normal de instanceof, verificar si un objeto es instancia de uno de sus subtipos antes de intentar hacer un downcasting:

El cdigo anterior compila y muestra:


'a' refers to a B

En el ejemplo, usamos instanceof para proteger al programa de un downcasting ilegal. Puedes testear un objeto de referencia con su propia clase, o cualquiera de sus subclases. Esto significa que cualquier objeto de referencia ser evaluado a true si usas instanceof con el tipo Object, tal como sigue:

lo cual imprime:
b is definitely an Object

Hay que aadir, que un valor null es tambin valido en una referencia. Esto siempre dar false:

Esto imprime: false false

email: brujulato@yahoo.es

Pgina 89

Curso gratuito de Java en emuladores L2 Java


10.8.1 Errores de compilacin con instanceof

2012

No puedes usar instanceof en dos tipos de jerarquas distintas. Por ejemplo, esto no compilara:

La compilacin falla, no hay manera de que d pueda referirse a Cat o a algn subtipo de Cat. El siguiente resumen muestra el uso de instanceof en el siguiente cdigo:

10.9 Operadores aritmticos

Estoy seguro de que estas familiarizado con los operadores aritmticos.


+ sumar - restar * multiplicacin / divisin

Se pueden usar de manera estndar:

email: brujulato@yahoo.es

Pgina 90

Curso gratuito de Java en emuladores L2 Java


10.9.1 El operador resto %

2012

Este operador puede que no te sea familiar. Lo que hace es obtener el resultado de dividir los operandos, tal como demuestra este cdigo:

Al ejecutar el cdigo, imprime:


The result of 15 % 4 is the remainder of 15 divided by 4. The remainder is 3

Recuerda: Las expresiones son evaluadas de izquierda a derecha por defecto, Puedes cambiar esta secuencia aadiendo parntesis. Tambin recuerda que *,/ y % preceden sobre + y -.

10.9.2 Operador de concatenacin de cadenas El signo ms puede ser usado para concatenar dos cadenas de caracteres.

La concatenacin se pone interesante cuando combinas cadenas y nmeros:

Que har el operador +? sumar las variables numricas o concatenara los caracteres? Ok, ya has tenido tiempo de pensarlo. Los valores int sern tratados como caracteres y puestos a la derecha de la cadena, dando de resultado:
String37

As que el cdigo se lee como "empieza con cadena, luego cadena "3" y cadena "7". Sin embargo, si usas los parntesis alrededor de las variables numricas tal como se muestra:

email: brujulato@yahoo.es

Pgina 91

Curso gratuito de Java en emuladores L2 Java


tendremos: String10

2012

Al usar los parntesis haces que se evale primero lo que est entre ellos, as que el operador ms a la derecha lo que hace es aadir el resultado del parntesis. Recuerda esta regla: Si el operador es una cadena, el operador ms se convierta en un concatenador de cadenas. Si ambos operadores son nmeros, el operador + hace una suma. Alguna vez encontraras problemas al decidir si el operador de la izquierda es cadena o no. En el examen no lo esperes tan obvio. Observa el siguiente cdigo:

No puedes saber que el operador + se est usando para sumar o concatenar hasta averiguar el tipo de devolucin de foo. Finalmente tienes que saber que es vlido usar el operador += con cadenas:

Como el operador += para hacer una suma, pues el resultado es 1234567.

10.10 Operadores de incremento y decremento Java tiene dos operadores que incrementan o decrementan una variable en 1. Estos operadores estn compuestos de dos signos mas o dos menos (++ --). El operador se sita despus del operando, o despus para cambiar su valor. Sin embargo, cuando el operador est a un lado u otro, el resultado cambia, veamos este ejemplo:

Observa la 4a lnea del programa donde el operador de incremento esta despus de la variable player. Esto significa que estamos incrementando el valor en 1 pero justamente antes de arrojar el valor en la pantalla. Cuando ejecutamos el programa, aparece esto:

email: brujulato@yahoo.es

Pgina 92

Curso gratuito de Java en emuladores L2 Java


%java MathTest players online: 0 The value of players is 1 The value of players is now 2

2012

Tienes que advertir que la variable escrita en la pantalla, primero dice el valor, y luego efecta la operacin de incremento si el operador esta despus de la variable. La lnea 5 no incrementa la variable, solo est haciendo una concatenacin. La lnea 6 incrementa el valor de la variable antes de ser mostrada en pantalla. Puedes esperar un remix de operadores tal como este ejemplo:

El cdigo imprime: x = 3 y = 4 Esto se lee como "Si 3 es igual a 2 O 3 < 4". La primera expresin compara x e y, y el resultado es false, porque el incremento de x no sucede hasta que el test == est hecho. Lo siguiente que ocurre es que se incrementa x, as que ahora x vale 3. Luego el segundo checkeo dice que x es menor que y, pero ya hemos incrementado y antes en la comparacin con x, as que la comparacin (3 < 4) es cierta y se muestra el resultado.

10.11 Operador condicional El operador condicional es un operador ternario (tiene tres operandos) y se usa para evaluar una expresin booleana, se parece mucho a if, solo que en lugar de ejecutar un bloque lo que hace es asignarle un valor a la variable. Su construccin es como sigue:

Puedes leer que el nmero de pets es 3. Los siguiente que hacemos es asignar el estatus a la variable. Si el nmero de pets es menor que 4, le asigna un mensaje, de otra manera, le asigna el otro. El operador condicional comienza por una operacin booleana seguida de dos posibles valores a la izquierda del operador =. El primero valor (el de la izquierda del :) se asigna si la condicin se cumple, y el segundo valor se asigna si la condicin no se cumple. Puedes incluso anidar operadores condicionales en un solo estamento:

email: brujulato@yahoo.es

Pgina 93

Curso gratuito de Java en emuladores L2 Java

2012

10.12 Operadores lgicos Hay seis operadores lgicos (&, |,^, !, &&, ||). En alguna documentacin de Sun se usa otra terminologa para estos operadores, pero nuestro propsito son los seis de arriba.

10.13 Operadores e Bitwise Okey, esto puede ser confuso. De los seis operadores lgicos listados arriba, tres de ellos (&, |,^) pueden ser usados tambin como operadores bitwise. Los operadores de bitwise fueron incluidos en versiones anteriores del examen, pero no estn en el examen de Java 5. Aqu hay varios estamentos que usan operadores de bitwise:

Los operadores de bit comparan dos variables bit a bit, y devuelve una variable cuyos bits han sido basados en cualquier de las dos variables y siendo comparados sus bits con AND (&), otro con OR(|) y otro con ON(^), la consola muestra:
0 15 1

10.14 Atajo para operadores lgicos Hay cinco operadores lgicos que son usados para evaluar estamentos que contienen ms de una expresin booleana. La mayora de los ms usados tienen dos atajos sintcticos, son AND, y OR. Se representan:

&& AND || OR

email: brujulato@yahoo.es

Pgina 94

Curso gratuito de Java en emuladores L2 Java

2012

Para que la condicin AND se cumpla, tiene que ser todo cierto, mientras que con OR es cierto si cualquiera de las comparaciones es verdadera. Estos atajos se evalan desde la izquierda a la derecha, si alguna condicin no se cumple, no se sigue evaluando el estamento. class Logical { public static void main(String [] args) { boolean b = true && false; System.out.println("boolean b = " + b); } } esto no muestra %java Logical boolean b = false

Presta atencin al siguiente ejemplo:

da como resultado % java TestOR i < 5 Result is true i >= 5 i >= 5

email: brujulato@yahoo.es

Pgina 95

Curso gratuito de Java en emuladores L2 Java


Aqu esta lo que ha pasado en el mtodo main():

2012

1. Cuando llegamos a la lnea 3, se evala el primer operando con || 2. El mtodo isItSmall(3) se invoca e imprime "i < 5" y devuelve true 3. El operando de la lnea 3 es cierto, el operador || no evala la siguiente comparacin as que nunca vemos "i >= 5" que se hubiera mostrado si se hubiera ejecutado el segundo estamento. 4. Se evala la lnea 6, comenzando con el primer operando || 5. El mtodo isItSmall(6) se invoca imprimiendo "i >= 5" y devolviendo false. 6. Como el primer operador || de la lnea 6 es false, el operador || no puede saltar el segundo operando, porque todava hay una expresin que puede ser verdad, si se evala el segundo operador dar true. 7. Se invoca isItSmall(9) que imprime "i >= 5". 8. El mtodo isItSmall(9) devuelve false as que la expresin de la lnea 6 es falsa, por lo que la lnea 7 nunca se ejecuta.

10.15 Operadores lgicos sin atajos Hay dos operadores lgicos, &, AND y | OR. Estos operadores son usados en expresiones lgicas al igual que && y ||, pero no son atajos, estos evalan ambos lados de la expresin siempre. Son ineficientes. Por ejemplo, si el primer operando con expresin & es falso, el segundo operando tambin se evaluara, aunque el resultado, evidentemente sea false. Y el OR tambin es ineficiente, si el primer operando es true, JVM todava evaluara el segundo operador aun sabiendo que el resultado dar true.

10.16 Operadores lgicos ^ y !

Los ltimos operadores lgicos son: ^ XOR y ! booleano invertido. El OR exclusivo (o XOR) slo evala valores booleanos. El operador ^ es tratado como un operador sin atajo y siempre evala ambos operandos en una expresin. Para el XOR, la expresin para que d true tiene que ser EXACTAMENTE que uno de los operandos sea true, por ejemplo:

resulta: xor false La expresin anterior devuelve false porque ambos operandos son verdaderos. El operador ! devuelve el valor contrario del booleano:

se puede leer como "si no es verdad que 7 == 5," y la salida produce email: brujulato@yahoo.es Pgina 96

Curso gratuito de Java en emuladores L2 Java


not equal

2012

Aqu hay otro ejemplo de booleano:

da como resultado:
! true false

En el cdigo anterior tienes que fijarte en que el test & es aprobado (imprimiendo true) y que el valor de la variable booleana f no cambi, as que imprimi false.

email: brujulato@yahoo.es

Pgina 97

Curso gratuito de Java en emuladores L2 Java


11 Control de flujo, excepciones y aserciones

2012

Como te imaginarias escribiendo un cdigo usando un lenguaje que no te permitiese ejecutar estamentos de forma condicional? El control de flujo es la clave de la mayora de los lenguajes de programacin, y Java ofrece varias maneras de hacerlo. Algo como if o el bucle for son estamentos comunes en la mayora de los lenguajes. Pero Java tambin arroja un par de controles que podras no haber usado antes, las excepciones y las aserciones. El estamento if y switch son de tipo condicin/decisin que permite a tu programa tener diferentes comportamientos dependiendo del resultado de un test lgico. Java provee de tres constructores de bucle, for, while y do, as que puedes ejecutar el mismo cdigo una y otra vez dependiendo de que se cumpla una condicin. Las excepciones dan una manera simple para organizar el cdigo de forma sencilla que se ocupa de problemas que pudieran surgir durante la ejecucin. Finalmente, la asercin, aadido en el lenguaje en el 1.4, te da una manera de comprobar y debuguear en condiciones que tu esperas puedan fallar mientras desarrollas, cuando no necesariamente necesites o quieras sobrecargar el manejo de excepciones. Con estas herramientas, puedes construir un programa robusto que pueda manejar cualquier situacin lgica con elegancia.

11.1 Estamento if El if y el switch se refieren normalmente a estamentos de decisiones. Cuando usas un estamento de decisin en tu programa, le estas pidiendo al programa que evale una expresin dada para determinar qu accin coger. Vamos a ver el estamento if. El formato bsico de un estamento if-else La expresin entre parntesis debe evaluarse a booleano. Normalmente, se usa para evaluar una expresin y ejecutar un bloque u otro segn si el resultado es verdadero o falso. El siguiente cdigo demuestra un estamento if-else valido:

El bloque else es opcional, as que tambin puedes usar lo siguiente:

email: brujulato@yahoo.es

Pgina 98

Curso gratuito de Java en emuladores L2 Java

2012

El cdigo asignara un 2 si la evaluacin es true. Las siguientes lneas fuera de las llaves se ejecutaran sin importar el resultado. Hasta las llaves son opcionales si solo quieres ejecutar un nico estamento (el inmediato justo despus del cierre del parntesis):

Ten cuidado con el cdigo de arriba, porque puedes pensar que se lee as: Si x es ms grande que 3, entonces poner y a 2, la z a z+8 y luego a =y + x. Pero en realidad las dos ltimas lneas se ejecutarn, no son parte del bloque de condicin. Podras tener la necesidad de anidar estamentos if-else (no es recomendado para la lectura). Puedes inicializar un if-else para evaluar mltiples condiciones. Los siguientes ejemplos usan dos condiciones, as que si el primer test falla, ejecutaremos un segundo test para decidir qu hacer:

Esto podra rescribirse tal como:

email: brujulato@yahoo.es

Pgina 99

Curso gratuito de Java en emuladores L2 Java


Hay un par de reglas para el uso de if-else-else if:

2012

Puedes tener desde cero else hasta uno para cada if, y deben ir despus de cada if. Una vez que else es ejecutado, ninguno de los dems else o if sern ejecutados.

11.2 Estamento switch Una manera de simular el uso de switch es usar mltiples if. Echa un vistazo al cdigo if-else y fjate como de confuso puede ser tener if anidados, incluso con pocos niveles:

y ahora lo mismo representado con switch:

email: brujulato@yahoo.es

Pgina 100

Curso gratuito de Java en emuladores L2 Java


Por si an no has pillado la estructura de un switch, te lo explico brevemente.

2012

Como cualquier bloque, un switch rodea entre llaves los cdigos. Entre parntesis, switch, evala la variable. En cada caso, case, ejecuta las instrucciones cuando el caso coincide, hasta llegar a break. Si no se pusiese un break, se continuaran ejecutando las dems instrucciones contenidas en los siguientes case. Es opcional. Con default, te aseguras de ejecutar alguna instruccin si no se cumple ningn caso. Es opcional.

11.2.1 Expresiones vlidas para switch y case El uso general de switch es:

Una expresin switch debe evaluar un carcter, byte, short, int o como en Java 5, una enumeracin. Eso significa que si no ests usando una enumeracin, solo puedes usar variables y valores que puedan ser automticamente ascendidos (en otras palabras, casteados) a int . No va a compilar si usas cualquier otra cosa, incluyendo nmeros long, float o double. El estamento case evala un valor constante del mismo tipo expresado en switch, con algo adicional, y gran, restriccin: la constante case debe ser una constante en tiempo de ejecucin! Lo que significa que solo puedes usar variables constantes o finales. No es suficiente con que sea una variable final, debe compilar como constante:

switch solo puede evaluar la igualdad. Quiere decir que los operadores como mayor que, menor que, etc.. no se pueden usar en case. Lo siguiente es una expresin vlida usando un mtodo de invocacin en el estamento switch. Fjate que en este cdigo, para que sea vlido, el mtodo est siendo invocado en la referencia del objeto, debe devolver un valor compatible con int.

email: brujulato@yahoo.es

Pgina 101

Curso gratuito de Java en emuladores L2 Java

2012

Qu pasa si usas una variable menor que un int en un switch? Observa el ejemplo:

El cdigo no compilar. Aunque el estamento es vlido, el byte es implcitamente casteado a int, el segundo case (128) es demasiado grande para un byte (hasta 127 positivos) y el compilador lo sabe! Intentar compilar la ltima instruccin da un error como este:

Tampoco no es vlido tener ms de un case usando el mismo valor. Por ejemplo el siguiente bloque no compilara porque usa 2 case con el valor 80:

email: brujulato@yahoo.es

Pgina 102

Curso gratuito de Java en emuladores L2 Java

2012

Es lcito aprovechar la potencia del boxing en una expresin switch. Por ejemplo, esto es vlido:

11.2.2 Interrupciones y flujo en los boques switch


Ya estamos preparados para discutir el estamento break, y mas detalles sobre control de flujo en un switch. Lo ms importante a recordar es que la ejecucin de un switch funciona as: Las constantes en case son evaluadas desde arriba, hacia abajo, y el primer valor evaluado en case que coincida con la expresin switch , ser el punto de entrada para ejecutar el bloque case. En otras palabras, una vez que un case sea ejecutado, la JVM ejecutar los dems bloques case siguientes, hasta encontrar un estamento break. El siguiente ejemplo usa una enumeracin en un estamento case:

En este ejemplo, el case green: encaja, as que la JVM ejecutar ese bloque y los subsiguientes produciendo la salida:
green blue done

De nuevo, cuando el programa encuentra la palabra break durante la ejecucin de un estamento switch, la ejecucin del bloque case finalizara y saldr del bloque switch. Si se omite break, el programa simplemente, ejecutar todos los bloques case hasta que termine el estamento switch. Observa el siguiente cdigo:

email: brujulato@yahoo.es

Pgina 103

Curso gratuito de Java en emuladores L2 Java

2012

mostrar:
x is one x is two x is three out of the switch

Esta combinacin sucede cuando el cdigo no encuentra el estamento break, la ejecucin se mantiene hasta el final. Esta clase de ejecucin se la conoce como "fall-through" porque la manera de ejecutarse cae desde el primer case ejecutado hasta el ltimo. Recuerda, que el punto de entrada de ejecucin es el primer case que coincida con la evaluacin de switch. En otras palabras, no puedes pensar "Encuentra la coincidencia, ejecuta el bloque y sal". Porque eso no funciona as. Si t quieres que funcione as, tienes que usar break en cada case como en el ejemplo:

Este cdigo mostrar:


x is one out of the switch

y ya est. Hemos entrado dentro del bloque switch en el case 1. Porque encaja con la evaluacin en switch, hemos conseguido ejecutar el estamento println, luego llega a break y salta al final de switch. Un ejemplo interesante del uso de este fall through se muestra en el siguiente cdigo:

email: brujulato@yahoo.es

Pgina 104

Curso gratuito de Java en emuladores L2 Java

2012

El estamento switch imprimir "x is an even number" o nada, dependiendo de si el numero esta entre uno y 10 y es par o impar. Por ejemplo, si x vale 4, la ejecucin comenzara por el case 4 hasta el final. Nota: Debido a que el fall-through es menos intuitivo, Sun recomienda que aadas un comentario // fall through cuando uses esta lgica.

11.2.3 El caso default Qu pasa si en el cdigo anterior, tu queras imprimir "x is an odd number" si ninguno de los casos (incluso los impares) encajaba? No podras ponerlo despus del estamento de switch, o incluso en el ltimo case, porque en ambas situaciones, podra imprimir siempre. Para conseguir este comportamiento necesitaras la palabra clave default. (Sin embargo, si t te has preguntado por qu hay una palabra clave default aunque no lo usemos como modificador de acceso, vers ahora que default se usa para un propsito completamente distinto). El nico cambio que necesitamos hacer es aadir un caso por defecto en el cdigo:

email: brujulato@yahoo.es

Pgina 105

Curso gratuito de Java en emuladores L2 Java

2012

Los bucles de Java vienen en tres sabores: while, do y for (y a partir de Java 5, for tiene dos variantes). Los tres te permiten repetir un bloque de cdigo tantas veces como se cumple la condicin, o especificas veces de iteraciones. Probablemente estas familiarizado con los bucles en otros lenguajes, as que si eres nuevo en Java, no ser ningn problema aprenderlos.

11.3 Estamento while Este bucle es bueno en escenarios donde no sabes la cantidad de veces que un bloque de estamentos tiene que repetirse, pero quieres continuar haciendo el bucle mientras la condicin se cumpla. Un estamento while es como esto:

En este caso, como en todos los bucles, la expresin debe evaluarse como un resultado booleano. El cuerpo del bucle se ejecutar mientras la expresin resulte true. Una vez dentro del bucle, el cuerpo se repetir hasta que la condicin sea evaluada a false. En el ejemplo anterior, el programa entrara en el bucle porque x es igual a 2. Sin embargo, x es incrementada en el bucle, as que cuando la condicin se comprueba otra vez, la evaluacin da false y sale del bucle. Cualquier variable usada en la expresin while debe ser declarada antes de que la expresin sea evaluada, en otras palabras, no puedes decir:

Nuevamente, por qu lo haras? En lugar de evaluar la variable, la declararas y la inicializaras, as que siempre tendra el mismo valor. No se parece mucho a una evaluacin! La clave est en recordar que el bucle while puede ni llegar a ejecutarse. Si la evaluacin devuelve false, el cuerpo de while se ignorara.

email: brujulato@yahoo.es

Pgina 106

Curso gratuito de Java en emuladores L2 Java

2012

El cdigo devuelve:
past the loop

Ya que la expresin (x > 8) se evala falsa, nada en el interior del cuerpo se ejecuta.

11.4 Estamento do while El uso de do while es similar a while excepto que la expresin no se evala hasta que el cdigo dentro del bucle se ejecuta, as que se garantiza que el cdigo del bloque se ejecutar al menos una vez. El siguiente ejemplo muestra un do-while en accin:

El System.out.println() imprimir una vez, aunque la expresin se evala falsa. Recuerda, el bucle siempre ejecutar el cdigo del cuerpo por lo menos, una vez. Asegrate de usar el punto y coma al final de la expresin while.

El uso de los bucles En Java 5, los bucles for tienen una segunda estructura. Nosotros llamaremos al viejo estilo, "bsico", y al nuevo estilo "bucle mejorado" aunque Sun se refieren a el como for-each. Dependiendo de la documentacin que uses (Sun incluida) vers los dos trminos paralelamente con for-in. Los trminos for-in y for-each se refieren al mismo for mejorado. El bucle bsico de for es ms flexible que su homnimo mejorado, pero el mejorado fue diseado para realizar iteraciones a travs de colecciones y arrays.

11.5 Estamento for, o for-in (el bucle bsico) Este estamento es til cuando sabes de antemano las veces que se ejecutar el cdigo dentro del bloque. Este estamento consta de tres partes adems del cuerpo del bucle:

email: brujulato@yahoo.es

Pgina 107

Curso gratuito de Java en emuladores L2 Java


2012

Declaracin e inicializacin de la variable La expresin booleana La iteracin

Las tres partes estn separadas por punto y coma. Los siguientes ejemplos demuestran un bucle for. El primer ejemplo muestra las partes de un bucle for en pseudo cdigo, y la segunda, un ejemplo tpico.

El for bsico: declaracin e inicializacin


La primera parte del estamento te permite declarar e inicializar cero, una o mltiples variables del mismo tipo dentro del parntesis despus de for. Si declaras ms de una variable del mismo tipo, entonces las tendrs que separar con ; (punto y coma) tal como sigue:

La declaracin e inicializacin ocurre antes que nada en el bucle. Y mientras las otras dos partes, la evaluacin booleana y la iteracin, se ejecutarn con cada vuelta de bucle, la declaracin slo suceder una vez al comienzo. Tambin tienes que saber que la vida de las variables declaradas en el bucle, terminan con el bucle. Este ejemplo lo demuestra:

Test.java:19: cannot resolve symbol symbol : variable x location: class Test System.out.println(x); ^

email: brujulato@yahoo.es

Pgina 108

Curso gratuito de Java en emuladores L2 Java


El for bsico: la expresin booleana

2012

La siguiente seccin que se ejecuta es la expresin de la condicin, la que (como en todos los test condicionales) debe evaluar un valor booleano. Solo puedes tener una expresin lgica, pero puede ser compleja. Observa el cdigo:

Este cdigo era vlido, pero el siguiente no lo es:

El compilador arrojar:
TestLong.java:20: ';' expected for (int x = 0; (x > 5), (y < 2); x++) { } ^

La regla para recordar esto: Solo puedes tener una expresin lgica. En otras palabras, no puedes usar mltiples evaluaciones separadas por comas, aunque las otras dos partes del estamento puedan tener mltiples partes.

El for bsico: el operador de iteracin Despus de cada ejecucin del cuerpo del bucle, se ejecuta la expresin de internacin. Esto es donde le dices lo que quieres que ocurra en cada iteracin del bucle. Recuerda que siempre ocurre despus de que el bucle se complete. Mira lo siguiente:

El cdigo precedente se ejecuta solo una vez. La primera vez, dentro del estamento x vale cero, luego se evala para ver si es menor que uno (lo que es cierto) para despus ejecutar el cuerpo del bucle. Despus de que el cuerpo se haya ejecutado, la iteracin se ejecuta incrementando x en 1. Lo siguiente que toca es evaluar x, pero el resultado ahora es falso, la ejecucin del bucle termina. Ten en cuenta que, salvo una salida forzada, la evaluacin de la iteracin y la evaluacin del booleano son las dos ltimas cosas que ocurre en un bucle for. El ejemplo de salida forzada pueden ser un break, un return, un System.exit() o una excepcin, que causar una salida repentina sin ejecutar la expresin de iteracin.

email: brujulato@yahoo.es

Pgina 109

Curso gratuito de Java en emuladores L2 Java


Observa este cdigo:

2012

Al ejecutar este cdigo, se arroja en pantalla:


in for loop

El estamento imprime una vez, porque un return causa que se abandone la ejecucin no solo la iteracin sino que tambin todo el mtodo. As que la iteracin de la expresin nunca se ejecuta en este caso. La siguiente tabla muestra el resultado de una salida forzada del bucle:

El for bsico: puntualizaciones


Ninguna de las tres secciones de la declaracin son obligatorias! El siguiente ejemplo es perfectamente vlido (aunque no es buena prctica):

En el ejemplo todas las partes estn vacas as que este bucle es infinito. Es importante saber que con la ausencia de la inicializacin y la seccin de incremento, el bucle actuar como bucle while. El siguiente ejemplo lo demuestra:

email: brujulato@yahoo.es

Pgina 110

Curso gratuito de Java en emuladores L2 Java

2012

El siguiente ejemplo demuestra que un bucle for con mltiples variables, y deben ser del mismo tipo. Recuerda que las variables declaradas en el estamento for son todas locales y no pueden ser usadas fuera del for.

La ultima cosa que aadir es que las tres secciones de for son independientes unas de otras. Las tres expresiones no necesitan operar la misma variable, aunque es lo tpico. Pero hasta el operador de iteracin, el cual es llamado errneamente el operador de incremento, no necesita incrementar o asignar nada, puedes poner un estamento virtual para lo que quieras que ocurra en cada iteracin del bucle. Mira lo siguiente:

Lo anterior imprime:
iterate iterate

11.5.1 El for mejorado (para arrays, for each) Fue introducido para Java 5 y est especializado en simplificar el trabajo de recorrer una coleccin o un array en un bucle. En este tema vamos a centrarnos en el uso de este nuevo for. Lo visitaremos nuevamente, cuando veamos las colecciones, que es donde esta nueva revisin se siente como en casa. En lugar de tener tres componentes, la versin actualizada tiene dos. Repasemos la vieja forma y luego usaremos la nueva:

Resultado:
12341234

email: brujulato@yahoo.es

Pgina 111

Curso gratuito de Java en emuladores L2 Java


Formalmente, vamos a describir cmo funciona:

2012

Las dos piezas del estamento son:

declaracin: es un bloque recin declarado de una variable, de tipo compatible con los elementos del array al que se est accediendo. Esta variable estar disponible dentro del bloque, y su valor ser el mismo que el elemento actual del array. expresin: debe evaluar el array que quieres recorrer. Podra ser un array, o un mtodo que devuelve un array. El array puede ser de cualquier tipo, objetos, primitivos o arrays de arrays.

Usando las definiciones anteriores, echemos un vistazo a algunas declaraciones vlidas y no vlidas:

El for actualizado asume que salvo en una salida forzosa del bucle, siempre recorrers todos los elementos del array. El siguiente apartado de break y continue se aplican a los dos tipos de for.

11.6 El uso de break y continue El uso de break y continue son usadas para parar todo el bucle o slo la iteracin actual (continue). Normalmente si usas break o continue, lo hars en una evaluacin if dentro del bucle, y si alguna condicin se convierte en true (o false dependiendo del programa) querrs salir inmediatamente. La diferencia entre ellos es si querrs continuar o no con la iteracin o saltar al primer estamento despus del bucle y continuar desde all

email: brujulato@yahoo.es

Pgina 112

Curso gratuito de Java en emuladores L2 Java

2012

El estamento break hace que el programa detenga la ejecucin de su bucle inmediato y comienza a procesar la siguiente lnea de cdigo. El estamento continue hace que slo la iteracin actual del bucle inmediato se detenga y comience por la siguiente evaluacin. Cuando usamos un continue en un bucle for, tienes que considerar los efectos que continue tiene en la operacin del bucle. Examina este ejemplo:

La pregunta es, esto es un bucle sin fin? La respuesta es no. Cuando llegamos al continue, la iteracin aun est ejecutndose. Se ejecuta a travs de la iteracin actual de manera natural. As que el ejemplo anterior, la variable i todava incrementar despus de que la condicin (i < 10) sea evaluada de nuevo. La mayora de las veces, un continue se usa dentro de un if tal como se muestra:

email: brujulato@yahoo.es

Pgina 113

Curso gratuito de Java en emuladores L2 Java

2012

Como nota personal, tengo que aadir que no vi claro el uso de continue, as que desarroll el siguiente cdigo:

y esto me produjo la siguiente salida:


Inside Wea: 0 Inside Wea: 1 Inside Wea: 2 Inside Wea: 3 Inside Wea: 4 Inside Inside Wea: 6 Inside Wea: 7 Inside Wea: 8 Inside Wea: 9 loop 0 loop 1 loop 2 loop 3 loop 4 loop 5 loop 6 loop 7 loop 8 loop 9

As pude observar, que cuando la condicin se cumple, el resto del bloque no se ejecuta (se ignora cuando i==5), volviendo el punto de ejecucin del estamento for, donde se continua la iteracin hasta el final.

email: brujulato@yahoo.es

Pgina 114

Curso gratuito de Java en emuladores L2 Java


11.6.1 Estamentos sin etiquetas

2012

Los estamentos break y continue pueden estar etiquetados o no. Quizs lo ms comn es que no estn etiquetados. Como dijimos antes, un estamento break (sin etiqueta) saldr del ms inmediato bucle y seguir con la siguiente lnea de cdigo despus del cuerpo. El siguiente ejemplo demuestra un estamento break:

En este ejemplo, el estamento break esta sin etiquetar. El siguiente ejemplo es un ejemplo de continue etiquetado:

En este ejemplo, se est leyendo el campo de un fichero. Cuando encuentra un error, el programa salta al siguiente campo de la fila y usa el estamento continue para volver al bucle (sino es el final de la fila) y mantiene la lectura de varios campos. Si el estamento break se hubiera puesto en su lugar, el cdigo habra parado de leer al encontrar el error y se hubiera terminado la lectura del fichero. El estamento continue es una manera de decir "Esta iteracin en particular necesita parar, pero no todo el conjunto. No quiera finalizar el resto de la iteracin".

11.6.2 Estamento con etiquetas Aunque muchos estamentos en Java pueden ser etiquetados, lo ms comn es usar las etiquetas con bucles como for o while, en conjuncin con break y continue. Una etiqueta debe ser puesta justo antes del estamento etiquetado, y consiste en un identificador valido que finaliza con dos puntos (:).

email: brujulato@yahoo.es

Pgina 115

Curso gratuito de Java en emuladores L2 Java

2012

Tienes que entender la diferencia entre etiquetado y sin etiquetar. La etiquetacin solo son necesarias en situaciones donde tienes un bucle anidado, y necesitas indicar cul de los bucles quieres romper, o a cul de ellos quieres continuar en la siguiente iteracin. Un estamento break saldr del bucle etiquetado, lo opuesto al bucle inmediato, si un break esta combinado con una etiqueta. Un ejemplo de como parece una etiqueta es lo siguiente:

La etiqueta debe de cumplir las mismas reglas que las variables en lo que se refiere al nombre. La sintaxis para el uso de la etiqueta en conjuncin con un estamento break es la palabra break , despus el nombre de la etiqueta, seguido de un punto y coma. Un ejemplo ms completo es el que sigue:

El cdigo produce la siguiente salida:


Hello Good-Bye

email: brujulato@yahoo.es

Pgina 116

Curso gratuito de Java en emuladores L2 Java

2012

En este ejemplo, la palabra Hello ser imprimida una vez. Luego la etiqueta de break ser ejecutada y el flujo saldr del bucle etiquetado outer. La siguiente lnea de cdigo imprimir Good-Bye. Veamos lo que pasa si el estamento continue se usa en lugar del break. El siguiente cdigo es similar al anterior, con la excepcin de la sustitucin de break por continue:

Al ejecutar el cdigo, se imprime:


Hello Hello Hello Hello Hello Good-Bye

En este ejemplo, Hello se imprimir cinco veces. Despus de que el estamento continue se haya ejecutado, el flujo continua con la siguiente iteracin del bucle identificado en al etiqueta. Finalmente, cuando la condicin en el bucle ms exterior se evala a false, este bucle terminara con la impresin de Good-Bye.

email: brujulato@yahoo.es

Pgina 117

Curso gratuito de Java en emuladores L2 Java


12. Excepciones

2012

Una vieja mxima del desarrollo de software dice que del 80% del trabajo se usa un 20%. El 80% se refiere al esfuerzo para evaluar y manejar errores. En muchos lenguajes, escribir programas que evalen y manejan errores es tedioso e hincha el cdigo fuente de la aplicacin para hacerlo tan confuso como un espagueti. Aun as, la deteccin y manejo de errores debe ser el ingrediente ms importante para la robustez de una aplicacin. Java provee a los desarrolladores con elegantes mecanismos de manejo de errores que produce un eficiente y organizado cdigo: manejo de excepciones (exception handling). Las excepciones permiten a los desarrolladores detectar errores fcilmente sin escribir cdigo especial para evaluar valores de retorno. Incluso mejor, te permite manejar la excepcin de manera separada y transparente del cdigo generado por la excepcin. Tambin te permite usar el mismo manejador de excepciones para hacer frente a una serie de posibles excepciones.

12.1 El estamento try catch Antes de empezar, introduzcamos algo de terminologa. El termino excepcin significa condicin excepcional, y es un suceso que altera el fluido normal del programa. Un puado de cosas pueden conducir a una excepcin, incluyen fallo de hardware, fuente de recursos agotadas, y unos buenos bugs. Cuando ocurre un evento excepcional en Java, se dice que se ha arrojado una excepcin. El cdigo responsable de hacer excepciones se llama "manejador de excepciones" en ingls "exception handler", y atrapa la excepcin arrojada. El manejador de excepciones funciona transfiriendo la excepcin del programa al manejador de excepciones apropiado cuando sucede el evento. Por ejemplo, si llamas a un mtodo que abre una fila y la fila no puede ser abierta, la ejecucin del mtodo se detendr, y el cdigo que escribiste para manejar la situacin se ejecutara. Para ello, necesitamos una manera de decirle a la JVM que el cuando suceda una cierta excepcin, ejecute el cdigo. Para hacer esto usamos las palabras claves try y catch. El try se usa para definir un bloque de cdigo donde puede ocurrir la excepcin. Este bloque se le llama regin guardada (lo que significa que el cdigo peligroso va aqu). Una o ms clausulas catch especifican la excepcin (o grupo de excepciones, ms tarde veremos ms) a un bloque de cdigo que lo maneje. Aqu se muestra un ejemplo en pseudocdigo:

email: brujulato@yahoo.es

Pgina 118

Curso gratuito de Java en emuladores L2 Java

2012

En este pseudocdigo, la lnea 2 hasta la 5 constituye la regin gobernada por try. La lnea 7 es el manejador de la excepcin de tipo MyFirstException. La lnea 12 es un manejador de excepciones de tipo MySecondException. Date cuenta de que el bloque catch sigue al try. Esto es obligatorio, si tienes uno o ms bloques catch, deben ir inmediatamente detrs de try. Adems, los bloques catch deben ir uno detrs del otro, sin estamentos o bloques de por medio. Tambin el orden de los catch importa, como veremos ms tarde. La ejecucin de la regin de seguridad empieza en la lnea 2. Si el programa se ejecuta sin excepciones, saltar a la lnea 15. Sin embargo, si la primera lnea 2 hasta la 5 arroja una excepcin de tipo MyFirstException, la ejecucin ser inmediatamente transferida a la lnea 7. Desde la lnea 8 a la 10 se ejecutarn todas las instrucciones del bloque, y luego la ejecucin continuara en la lnea 15 y continuar. Fjate que si una excepcin ocurre en la lnea 3 del bloque, el resto de lneas en el try no sern ejecutadas. Una vez que el control salte al bloque catch, no se volver a ejecutar el bloque try. Esto es exactamente lo que t quieres, creo... Imagina que tu cdigo se parece a esto en pseudo cdigo:

email: brujulato@yahoo.es

Pgina 119

Curso gratuito de Java en emuladores L2 Java

2012

El pseudocdigo demuestra cmo funcionan normalmente las excepciones. El cdigo que depende de un riesgo en la operacin (como llenar una tabla con los datos de un fichero en internet) esta agrupado dentro del bloque try de tal manera que si, por ejemplo, la primera operacin fracasa, no vas a intentar continuar ejecutando otro cdigo que seguramente va a fallar tambin. El pseudocdigo del ejemplo no te va a dejar leer un fichero si no hay internet en primer lugar. Uno de los beneficios de usar un manejador de excepciones es que el cdigo para manejar en cualquier excepcin en particular puede ocurrir en la regin gobernada, necesite ser reescrita solo una vez. Volviendo a nuestro cdigo anterior, pueden haber tres sitios en nuestro bloque try que pueda generar una MyFirstException, pero si esto ocurre, ser manejado en el mismo bloque catch (lnea 7). Hablaremos de ms beneficios de la excepcin casi al final de este tema.

12.1.1 El uso de finally Aunque try y catch proveen de un fabuloso mecanismo para atrapar y manejar excepciones, nos queda el problema de como limpiar lo que deja una excepcin. Dado que la ejecucin dentro del bloque try se interrumpe sbitamente si hay una excepcin, no podemos limpiar el cdigo del bloque try y esperar que sea ejecutado. Casi que es una mala idea poner nuestro cdigo de limpieza en cada bloque catch, veamos el motivo. El manejador de excepciones no son el lugar adecuado para limpiar el bloque try porque cada manejador requiere su propia copia para limpiar el cdigo. Por ejemplo, si algn puerto o fichero se queda abierto en la regin de seguridad, cada excepcin debera cerrar ese fichero o ese puerto. Sera ms fcil olvidar la limpieza y tambin un montn de cdigo redundante. Para solucionar este problema, Java provee del bloque finally. El bloque finally encierra un cdigo que siempre ser ejecutado despus del bloque try, sin importar si hay una excepcin o no. Aunque hubiera un return al final del bloque try, se ejecutara justo despus del return, y antes de que el return se ejecute! Este es el lugar correcto para meter tu cdigo de limpieza, cerrar tus puertos o cerrar tus ficheros, etc... Si el cdigo se ejecuta sin excepciones, el cdigo finally se ejecutar inmediatamente despus. Veamos otro ejemplo:

email: brujulato@yahoo.es

Pgina 120

Curso gratuito de Java en emuladores L2 Java

2012

Igual que antes, la ejecucin empieza en la primera lnea del bloque try, la lnea 2. Si no hay excepciones en el bloque try, la ejecucin prosigue por la lnea 11, la primera lnea del bloque finally. Por otra parte, si MySeconException arroja una excepcin mientras el bloque try se est ejecutando, la ejecucin prosigue en la primera lnea del manejador de excepciones, la lnea 8 de catch. Despus de que todo el cdigo en catch se haya ejecutado, el programa prosigue en la lnea 11, la primera lnea de finally. Repite despus de mi: el bloque finally siempre se ejecuta! OK, tenemos que afinar un poco, pero por ahora la idea de que finally siempre se ejecuta ha quedado clara. Tanto si se coge la excepcin, como si no, finally siempre se ejecuta. Ms adelante veremos unos pocos escenarios donde finally podra no completarse. Recuerda, la clusula finally no es obligatoria. Si no tienes, el cdigo, compilar perfectamente. De hecho, no todos los actos de un cdigo tienen que ser limpiados despus de que un bloque try se ejecute, as que la clusula finally no es obligatoria. Tambin, porque el compilador no exige tampoco la clusula catch, algunas veces querrs ejecutar el cdigo que tiene un try para terminar en el finally. Esta clase de programacin es til cuando la excepcin devuelve el flujo al mtodo que lo invoc, como explicaremos en la siguiente seccin. El uso de finally te permite limpiar el cdigo ejecutado aunque no haya clausula catch. Este cdigo muestra un try con finally pero sin catch:

Este cdigo muestra un try, catch y finally:

email: brujulato@yahoo.es

Pgina 121

Curso gratuito de Java en emuladores L2 Java

2012

El siguiente cdigo ilegal muestra un try sin catch o finally:

El siguiente cdigo ilegal muestra un ejemplo de cdigo fuera de lugar:

12.1.2 Programacin de excepciones no capturadas Por qu los catch no son obligatorios? Que ocurre tras una excepcin en un bloque try donde no hay catch? No hay requerimientos para que en tu cdigo haya un catch para cada excepcin que pueda ser arrojada al correspondiente bloque try. El hecho, es que es dudoso que puedas hacer tal hazaa! Si un mtodo no provee de una clausula catch en particular, a ese mtodo se le llama "ducking" y lo que hace es pasarle la pelota a otro mtodo. As que, qu ocurre? Cuando un mtodo, llama a otro, y esto a otro... la ejecucin comienza, obviamente, por el ultimo mtodo invocado. Cuando uno de estos mtodos invocados tiene una excepcin, esta excepcin se recorre hacia atrs, esperando a ser atrapada. Si esta excepcin llega al primer mtodo, y no es atrapada, estalla deteniendo la ejecucin del programa.

email: brujulato@yahoo.es

Pgina 122

Curso gratuito de Java en emuladores L2 Java

2012

Observa este ejemplo donde se crea una clase main que ejecuta el mtodo reverse sin proporcionarle argumento con el que trabajar (lnea 8). De la lnea 8 saltamos al mtodo reverse (lnea 23) que arroja una excepcin de tipo NadaQueDevolver. En la lnea 25, comprueba que la longitud de la cadena es menor que uno y arroja la excepcin. En la lnea 41 se crea la clase NadaQueDevolver que hereda la clase Exception. El constructor de la clase hace una llamada (super) pasndole como parmetro un texto. Volvemos a la lnea 10 donde se coge la excepcin y se imprime Excepcin arrojada: No hay texto que devolver. Finalmente se ejecuta el bloque finally.

email: brujulato@yahoo.es

Pgina 123

Curso gratuito de Java en emuladores L2 Java


12.1.3 Definiendo excepciones

2012

Hemos discutido las excepciones como concepto. Sabemos que se arrojan cuando surge algn tipo de problema, y sabemos el efecto que tienen en el flujo de un programa. En esta seccin vamos a desarrollar los conceptos ms all y el uso de excepciones en cdigo Java funcional. Antes dijimos que una excepcin es una ocurrencia que altera el flujo normal del programa. Pero desde que estamos usando Java, cualquier cosa que no es un primitivo, es... un objeto. Las excepciones no, bien, esta es la excepcin que confirma la regla. Cada excepcin es una instancia de la clase Exception en su propio rbol de jerarqua. En otras palabras, las excepciones son siempre una subclase de java.lang.Exception. Cuando se arroja una excepcin, un objeto del subtipo particular se instancia y maneja la excepcin como un argumento para la clusula catch. Un catch se parece a esto:

En este ejemplo, es una instancia de la clase ArrayIndexOutOfBoundsException. Como cualquier otro objeto, puedes llamar a sus mtodos.

12.1.4 Jerarqua de las excepciones Todas las clases de excepciones son subtipos de la clase Exception. Esta clase deriva de Throwable (la cual deriva de la clase Object). Esta imagen representa la jerarqua de las clases Exception:

email: brujulato@yahoo.es

Pgina 124

Curso gratuito de Java en emuladores L2 Java

2012

Como puedes observar, hay dos subclases que derivan de Throwable: Exception y Error. Las clases que derivan de Error representan situaciones inusuales que no son causadas por errores del programa, e indican cosas que normalmente no pasaran durante la ejecucin de un programa, como por ejemplo, que la JVM se quede sin memoria. Normalmente tu aplicacin no puede manejar este tipo de errores, as que no puede recuperarse. No se te pide que los manejes. Si tu cdigo no los maneja, (normalmente no) compilar sin problemas. Quizs hayas pensado en condiciones excepcionales, los errores tcnicamente no son excepciones porque no derivan de la clase Exception. En general, una excepcin representa algo que ocurre como resultado no deseado por un error de programacin, ya que no se considera alguna condicin que pueda estar presente. Por ejemplo, si en tu aplicacin se supone que va a comunicarse con otra aplicacin en otro ordenador, y este, no contesta, esta excepcin no la causa un bug. Estas excepciones son un caso especial porque algunas veces indican error del programa. Tambin pueden presentarse casos raros para manejar excepciones en condiciones excepcionales. En tiempo de ejecucin, las excepciones son discutidas con gran detalle, ms adelante en este tema. Java provee de muchas clases de excepciones, la mayora con nombres bastantes descriptivos. Hay dos maneras de conseguir la informacin de una excepcin. La primera es desde el tipo de excepcin en si misma. La siguiente es de la informacin que obtienes del objeto en excepcin. La clase Throwable (en lo alto de la herencia) provee a sus descendientes con algunos mtodos que son tiles para manejar las excepciones. Uno de ellos es printStackTrace(). Como imaginabas, puedes invocar los mtodos de una excepcin, como vimos en un ejemplo anterior, se imprimir el seguimiento de la excepcin. Ya vimos que una llamada a la pila empieza desde abajo para terminar en el primer mtodo que invoco a los dems. Te dars cuenta de que printStackTrace() imprime la mayora de los mtodos recientes y luego continua descendiendo, imprimiendo el nombre de cada mtodo de la pila.

12.1.5 Manejando una clase completa de la jerarqua de excepciones Ya hemos visto que la palabra clave catch te permite especificar un tipo en particular de excepcin. Puedes capturar ms de un tipo de excepciones en una nica clusula. Si la clase de excepcin especificada en catch no tiene subclases, entonces solo se atrapar esa excepcin en concreto. Sin embargo, si la clase especificada en catch tiene subclases, las subclases tambin sern atrapadas. Por ejemplo, la clase IndexOutOfBoundsException (se da cuando el ndice especificado de un array sale de su rango) tiene dos subclases, ArrayIndexOutOfBoundsException y StringIndexOutOfBoundsException. Podras desear escribir un cdigo que maneje la excepcin, pero no sabes que excepcin se est arrojando (cual subclase). En este caso, puedes escribir un catch como este:

email: brujulato@yahoo.es

Pgina 125

Curso gratuito de Java en emuladores L2 Java

2012

Si cualquier cdigo en el bloque try arroja: ArrayIndexOutOfBoundsException o StringIndexOutOfBoundsException, la excepcin ser atrapada y manejada. Esto puede ser conveniente, pero podra ser usado por separado. Al especificar una excepcin de la superclase en la clusula catch, ests descartando informacin valiosa de tu excepcin. Puedes, naturalmente, averiguar como ocurri esa excepcin en la clase, pero si vas a hacer eso, es mejor escribir una clausula catch para cada tipo de excepcin. Si tienes una jerarqua de excepciones compuesta de una superclase y un numero de subtipos, y ests interesado en manejar uno de los subtipos de una manera especial, necesitas escribir solo dos clausulas catch. Cuando se arroja una excepcin, Java intentar encontrar la excepcin del tipo que encaje con la clusula catch. Si no la hubiese, entonces ir a por su supertipo. Si tampoco existiese, entonces se propagar la excepcin hasta que pueda ser manejada llegando a interrumpir la ejecucin del programa sino se detuviese. Este proceso se le conoce como la excepcin que encaja con la bsqueda. Veamos un ejemplo:

email: brujulato@yahoo.es

Pgina 126

Curso gratuito de Java en emuladores L2 Java

2012

Este programa intenta abrir un fichero para lectura de algn dato. Abrir y leer ficheros puede generar muchas excepciones, la mayora de ellas son de algn tipo de IOException. Imagina que en este programa estamos interesados en saber slo cuando hay una excepcin en concreto: FileNotFoundException . De otra manera no sabemos de que problema se trata. FileNotFoundException es una subclase de IOException. As que podemos manejar en la clusula catch todos los subtipos de IOException, pero podramos evaluar la excepcin que determina si fue una FileNotFoundException . As que codificamos una excepcin exclusivamente para FileNotFoundException y separado de la excepcin que maneja los dems subtipos de excepcin. Este cdigo generado para FileNotFoundException ser manejado por catch comenzando en la lnea 10. Si genera otra clase de IOException, a lo mejor, EOFException, que es subclase de IOException, ser manejado por el catch de la lnea 15. Si cualquier otra excepcin es generada, tal como excepciones en tiempo de ejecucin, no ser capturada y se propagara por la pila. Date cuenta que la clusula para FileNotFoundException ha sido puesto antes que IOException. Esto es muy importante. Si no lo hiciramos, el programa no compilara. Los manejadores especficos de excepciones deben ser puestos delante siempre de los manejadores genricos. Lo siguiente no compilar:

email: brujulato@yahoo.es

Pgina 127

Curso gratuito de Java en emuladores L2 Java

2012

El compilador arrojara esto:


TestEx.java:15: exception java.io.FileNotFoundException has already been caught } catch (FileNotFoundException ex) { ^

12.1.6 Declaracin de Excepciones e Interfaces Pblicas Como sabemos que mtodos que arrojan una excepcin y que podemos capturar? Un mtodo debe especificar el tipo y la cantidad de argumentos que acepta y lo que devuelve, las excepciones que un mtodo puede arrojar tienen que ser declaradas (al menos las excepciones son subclases de RunTimeException). La lista de excepciones declaradas es una parte de la interfaz pblica del mtodo. La palabra clave throws se usa para enumerar la lista de excepciones que un mtodo puede arrojar:

Este mtodo tiene un tipo de retorno void, no acepta argumentos y declara que puede arrojar dos tipos de excepciones: MyException1 y MyException2. Solo porque el mtodo declare que arroja excepciones, no quiere decir que siempre las est arrojando. Solo dice que podra. Supn que tu mtodo no arroja directamente una excepcin, pero llama a un mtodo que lo hace. Puedes elegir manejar o no la excepcin. Si declaras la excepcin que tu mtodo puede conseguir de otro mtodo, y no provees de un try-catch, entonces el mtodo programar la excepcin. Cualquier mtodo que pueda arrojar una excepcin (que sea subclase de RunTimeException) debe declarar la excepcin. Eso incluye mtodos que no estn arrojando excepciones directamente, pero estn pasando la excepcin a otro mtodo. Si pasas la excepcin, ests arrojando RunTimeException. Como las subclases estn exentas de implementarlo, el compilador no va a mirar si lo has hecho. Todas las excepciones RunTimeException son consideradas "comprobadas", porque el compilador lo hace para estar seguro de que has reconocido que algo malo podra pasar aqu. Recuerda:

email: brujulato@yahoo.es

Pgina 128

Curso gratuito de Java en emuladores L2 Java

2012

Cada mtodo debe manejar todas las excepciones comprobadas suministrando un catch o listando mediante throws cada excepcin. Esta regla es requerida en Java. Se la conoce como "manejar o declarar", algunas veces se la llama tambin "atrapar o declarar". Nuevamente, algunas excepciones estn exentas de esta regla. Un objeto de tipo RunTimeException puede ser arrojado desde cualquier mtodo sin que se especifique como parte del mtodo o de la interfaz pblica (y no se necesita ningn try-catch). Incluso si un mtodo no declara RunTimeException , el mtodo que invoca tampoco tiene obligacin de declararlo o manejarlo. RunTimeException , Error, y todos sus subtipos de excepciones no comprobadas y esta clase de excepciones no tienen que ser manejadas. Por ejemplo:

Fjate en myMethod1(). Ya que EOException es subclase de IOException y este es de Exception, debe ser declarada como una excepcin que pueda arrojar este mtodo. Pero de donde viene la excepcin? La interfaz publica para el mtodo myMethod2() invocada aqu, declara que una excepcin de este tipo puede ser arrojada. Sin embargo ese mtodo actualmente arroja la excepcin por si misma o invoca otro mtodo que la arroja que no es importante para nosotros, lo nico que sabemos es que tenemos que atrapar la excepcin o declarar que se puede arrojar. El mtodo myMethod1() no atrapa la excepcin, as que la declara. Ahora veamos otro ejemplo valido:

De acuerdo con lo comentado, este mtodo puede arrojar un NullPointerException. Ya que RunTimeException es superclase de NullPointerException, y esto es una excepcin no comprobada, no necesita ser declarada. Podemos ver que myMethod3() no declara ninguna excepcin. Las excepciones en tiempo de ejecucin son excepciones no comprobadas. Todas las dems excepciones son comprobadas, y no derivan de java.lang.RuntimeException. Una excepcin debe ser atrapada en alguna parte del cdigo. Si invocas un mtodo que arroja una excepcin pero no la atrapas, el cdigo no compilara. Eso es porque lo llamamos excepciones comprobadas, el compilador se asegura que son manejadas o declaradas. Hay un gran nmero de mtodos en las libreras de Java 2 que arrojan excepciones, as que a menudo escribirs manejadores de excepciones para hacer frente a estas, generados por mtodos que ni tan siquiera escribiste.

email: brujulato@yahoo.es

Pgina 129

Curso gratuito de Java en emuladores L2 Java

2012

Tambin puedes arrojar excepciones t mismo, y la excepcin tambin puede ya existir en el API de Java, o uno creada por ti. Para crear tus propias excepciones, simplemente heredas Exception (o una de sus subclases) tal como sigue:

Este cdigo mosquear al compilador:


TestEx.java:6: unreported exception MyException; must be caught or declared to be thrown throw new MyException(); ^

Necesitas saber cmo se compara un Error con excepciones comprobadas y sin comprobar. Los objetos de tipo Error no son objetos Exception, aunque ellos presentan unas condiciones especiales. Exception y Error comparten la misma superclase, Throwable y ambas pueden ser arrojadas usando throws. Cuando un Error o una subclase de Error es lanzada, no se comprueba. No se le requiere atrapar objetos Error o subtipos. Tambin puedes lanzar errores t mismo (aunque aparte de una AssertionError no creo que quieras usar otra, quizs un OutOfMemoryError?) y puedes capturarla. Lo siguiente compila perfectamente:

Si estuviramos lanzando una excepcin comprobada en lugar de un Error, entonces el mtodo doStuff() necesitara declarar la excepcin. Pero recuerda, los errores no son subtipos de Exception, no necesitan ser declarados. Si quieres declralos, pero al compilador le dar lo mismo quien o de donde sale un error.

email: brujulato@yahoo.es

Pgina 130

Curso gratuito de Java en emuladores L2 Java


12.1.7 Relanzando la misma excepcin

2012

Igual que puedes lanzar una nueva excepcin desde un catch, tambin puedes arrojar la misma excepcin que atrapas. Aqu la clusula catch lo hace:

Las dems clusulas catch asociadas con el mismo try sern ignoradas, si existiese un finally, se ejecutara y la excepcin volvera a ser arrojada desde el mtodo que lo invoc. Si lanzas una excepcin comprobada desde un catch, debes tambin declarar la excepcin! En otras palabras, debes manejar y declarar, lo opuesto a declarar o manejar. Lo siguiente es ilegal:

El mtodo doStuff() es quien claramente el que atrapa la excepcin IOException, pero el compilador va a decir, "si, muy bien, veo que atraparas la excepcin, pero no es suficiente. Si quieres relanzar la excepcin, tendrs que declararla".

email: brujulato@yahoo.es

Pgina 131

Curso gratuito de Java en emuladores L2 Java

2012

En este programa se le pasa mediante parmetros la comida, y si no es del agrado, lanzar una excepcin.

email: brujulato@yahoo.es

Pgina 132

Curso gratuito de Java en emuladores L2 Java


A continuacin expongo como lanzar una aplicacin con Eclipse adjuntando parmetros:

2012

email: brujulato@yahoo.es

Pgina 133

Curso gratuito de Java en emuladores L2 Java

2012

y el ejemplo arroja como resultado:


No esta mal tu eleccion. Fin de la ejecucion.

Si ponemos otros manjares, como "piedras palos pelo" el programa arroja:


Esa comida no nos gusta! Fin de la ejecucion.

12.1.8 Definicin de conceptos, excepcin y error Comencemos con la excepciones ms comunes, NullPointerException. Tal como vimos, esta excepcin ocurre cuando intentas acceder a un objeto usando una variable de referencia con el valor de null. No hay manera de que el compilador tenga la esperanza de encontrar este problema antes de ejecutar el programa (o sea, en tiempo de ejecucin). Veamos lo siguiente:

email: brujulato@yahoo.es

Pgina 134

Curso gratuito de Java en emuladores L2 Java

2012

Seguramente el compilador encontrara el problema en este programita! No, es cosa tuya. El cdigo compilara perfectamente y la JVM arrojara un NullPointerException cuando intente invocar el mtodo length(). Antes discutimos la llamada a la pila. Como recordars, usamos la convencin de que main() estara al final de la pila, y que como main() invoca otro mtodo, y ese mtodo invoca otro, etc... la pila crece. Naturalmente la pila reside en la memoria, e incluso si tu sistema operativo te da un 1GB de RAM para tu programa, la cantidad de memoria aun es finita. Es posible crecer la pila hasta ocupar toda la RAM de tu SO para guardar la pila. Cuando esto pasa, lo que obtienes es un (espera para verlo...), StackOverflowError. La manera ms comn para que esto ocurra es crear un mtodo recursivo. Un mtodo recursivo es aquel que se invoca as mismo en el cuerpo del mtodo. Mientras que eso puede sonar a locura, es una tcnica muy extendida para buscar y ordenar algoritmos. Mira este cdigo:

Como puedes ver, si cometes el error de invocar al mtodo go(), el programa caer en un agujero negro, go() invocara a go() hasta, no importa cuanta memoria tengas, que consigas un StackOverflowError. Nuevamente, solo la JVM sabe cuando esto ocurre, y la JVM ser la fuente de este error.

12.1.9 Programando el arrojo de excepciones Fjate que digo "programando" como queriendo decir: "Creado por una aplicacin y/o API por un desarrollador" Por ejemplo, muchas clases en el API de Java tienen mtodos que toman cadenas como argumento, y convierten estas cadenas en nmeros primitivos. Un buen ejemplo de estas clases son llamadas clases de envoltorio. Algunos programadores escribieron la clase java.lang.Integer y crearon mtodos como parseInt() y valueOf(). El sabio programador decidi que si a uno de estos mtodos le pasaba una cadena que no poda ser convertida en nmero, el mtodo arrojara un NumberFormatException. El cdigo parcialmente implementado se parece a esto:

email: brujulato@yahoo.es

Pgina 135

Curso gratuito de Java en emuladores L2 Java

2012

Otros ejemplos de excepciones programadas incluye una AssertionError (vale, no es una excepcin, pero se arroja programando), y arroja IllegalArgumentsException. El hecho, nuestro mtico desarrollador del API podra usar un IllegalArgumentsException para su parseInt(), pero devuelve NumberFormatException que hereda IllegalArgumentsException, y es un poco ms preciso, as, en este caso, usando NumberFormatException da soporte a la idea discutida antes: que cuando tienes una excepcin en una jerarqua, deberas usar la excepcin ms precisa que puedas. Naturalmente como vimos antes, puedes hacer tus propias excepciones, y arrojarlas cuando quieras. Estas excepciones caseras tambin entran en la categora de excepciones programadas. Excepciones ms comunes: Excepcin ArrayIndexOutOfBoundsException Descripcin Arrojado cuando se intenta acceder a un array con un valor ndex equivocado (negativo o mayor que la longitud del array) Arrojado cuando se intenta castear una variable de referencia de un tipo que no pasa el test ES-UN Arrojado cuando un mtodo recibe un argumento formateado de diferente manera que lo que espera el mtodo. Arrojado cuando el elemento de entorno no es el mismo que la operacin que se intenta hacer, por ejemplo un Scanner que ha sido cerrado. Arrojado cuando se intenta acceder a un objeto con una variable de referencia null. Arrojado cuando un mtodo que convierte una cadena a un nmero, recibe una cadena que no puede ser convertida. Arrojado cuando el test booleano devuelve falso. Arrojado cuando se intenta inicializar una variable esttica o un bloque. Se arroja cuando un mtodo se recurre profundamente. Cada invocacin se aade a la pila. Arrojado cuando la JVM no encuentra la clase que necesita por un error en la lnea de comando, un problema de classpath o falta una .class. Arrojado por JVM

ClassCastException

JVM

IllegalArgumentException

Programado

IllegalStateException

Programado

NullPointerException

JVM

NumberFormatException

Programado

AssertionError ExceptionInInitializerError StackOverflowError

JVM JVM JVM

NoClassDefFoundError

JVM

email: brujulato@yahoo.es

Pgina 136

Curso gratuito de Java en emuladores L2 Java


12.2 Mecanismo de aserciones

2012

Sabes que no debes hacer suposiciones, pero no puedes evitarlo cuando estas escribiendo cdigo. Lo haces ponindolos en comentarios:

Escribes estamentos con ellos:

Aadido al lenguaje Java 1.4, las aserciones te permiten comprobar supuestos durante el desarrollo, sin el gasto (tu tiempo) de escribir manejadores de excepciones que asumes que nunca pasaran una vez el programa este desarrollado. Se supone que asumes que un nmero pasado como parmetro a un mtodo nunca ser negativo. Mientras compruebas y limpias de errores el cdigo, quieres validar tu suposicin, pero no quieres tener que empezar a escribir excepciones o cdigo evaluando cuando ya has hecho el programa. Pero dejar esto sin comprobar es un riesgo. Aserciones al recate! Mira este cdigo:

Ya que supones que ests en lo cierto, no quieres emplear ms tiempo para escribir excepciones. Y en tiempo de ejecucin no quieres incluir ms if-else porque alcanzar el else significa que tu lgica (lo que fuera que estaba ejecutndose antes de que este mtodo fuera invocado) ha fallado.

email: brujulato@yahoo.es

Pgina 137

Curso gratuito de Java en emuladores L2 Java

2012

Las aserciones te permiten comprobar tus suposiciones durante el desarrollo, pero el cdigo de la asercin bsicamente se evapora cuando el programa est finalizado, dejando atrs el cdigo debugger (depurador) y borrndolo. Escribamos un mtodo para evaluar que el argumento no daba negativo:

No slo el hacer aserciones te permite dejar tu cdigo ms legible, ya que las aserciones estn inactivas a menos que las actives, entonces, el cdigo de arriba cuando lo compiles, quedara como:

El funcionamiento de las aserciones es bastante simple. La asercin dar siempre true, si no, problema! El cdigo continua ejecutndose. Pero si tu asercin no est desactivada y da falso, entonces una excepcin tipo "que se pare el mundo" AssertionError se lanza (algo que jams podrs manejar) entonces, puedes arreglar la lgica que te condujo a este problema. Las aserciones vienen en dos sabores: realmente simple, y simple, como por ejemplo:

La diferencia entre los dos es que la versin simple aade una segunda expresin, separado del primero (expresin booleana) por dos puntos, esta expresin de cadena se aade al Stack trace (es el sistema que cuando salta un error, te muestra el recorrido del error). Ambas versiones arrojan inmediatamente AssertionError, pero la versin simple te da una ayuda para depurar el cdigo, mientras que la otra versin, solo te dice que tu lgica ha fallado.

email: brujulato@yahoo.es

Pgina 138

Curso gratuito de Java en emuladores L2 Java


12.2.1 Reglas de expresin de las aserciones

2012

Las aserciones pueden tener una o dos expresiones, dependiendo de si usas el modo simple, o el realmente simple. La primera expresin debe dar siempre un valor booleano! Sigue las mismas reglas que para el if. Todo lo que hace una asercin es una evaluacin, lo que significa que el resultado slo puede ser verdadero o falso. Si es verdadero, no hay problema. Sino, entonces tu suposicin era errnea y consigues un AssertionError. La segunda expresin, solo se usa con el modo simple, que puede ser cualquier cosa que resulte en un valor. Recuerda, la segunda expresin se usa para generar un mensaje de texto que muestre en el Stack trace algo ms de informacin para depurar el cdigo. Funciona como System.out.println() en el que puedes pasarle un primitivo o un objeto, y lo convertir a cadena de texto. Debe devolver un valor! Lo siguiente muestra una lista de expresiones vlidas y errneas para ambas clases de aserciones, recuerda que la expresin 2 se usa slo con el modo simple, donde la segunda expresin solo te da un poco ms de informacin sobre el detalle de la asercin:

email: brujulato@yahoo.es

Pgina 139

Curso gratuito de Java en emuladores L2 Java


12.2.2 Aserciones, identificados o palabra clave

2012

Si quieres usar aserciones, tienes que pensar primero como compilar con aserciones tu cdigo, y entonces, pensar como ejecutar el programa con las aserciones activadas. Ambas requieren Java 1.4 o superior, que nos trae el primer punto, como compilar con aserciones en tu cdigo. Identificadores contra palabras claves Antes de la versin 1.4, tendras que haber escrito:

Fjate que en el cdigo, asercin se usa como un identificador. Esto ya no es un problema en el 1.4. Pero no puedes usar palabras claves o reservadas como identificadores, empezando con assert que es una palabra clave. El dato de esta cuestin es que puedes usar assert como palabra clave o como un identificador, pero no como ambas.

El uso de javac (Compilador de Java) con assert El compilador de Java usar siempre assert como palabra clave por defecto. A menos que le digas lo contrario, el compilador generar un error si encuentra la palabra assert usada como identificador. Sin embargo, puedes decirle al compilador que le estas dando una cdigo antiguo y que quisieras que lo compilase a la antigua usanza. Digamos que haces un arreglo a un viejo cdigo del 1.3 que usa assert como identificador. En la lnea de comandos podras escribir:

El compilador mostrara mensajes cuando descubra que la palabra assert se usa como identificador, pero el cdigo compilar y se ejecutara. Supn que le dices al compilador que tu cdigo es de la versin 1.4 o posterior, por ejemplo:

En este caso el compilador mostrar errores cuando descubra que estas usando assert como un identificador. Si quieres decirle al compilador que use las reglas de Java 5 puedes hacer una de estas tres cosas: Omitir la opcin -source la cual est por defecto, o aadir una de estas dos opciones a source:

Te das cuenta lo claro que es Sun sobre 1.5 y 5?

email: brujulato@yahoo.es

Pgina 140

Curso gratuito de Java en emuladores L2 Java

2012

Si quieres usar aserciones como identificadores en tu cdigo, debes compilar usando -source 1.3. La siguiente tabla resume como el compilador de Java 5 reacciona a assert como identificador o palabra clave:

12.2.3 Ejecucin con aserciones Aqu tenemos algo bueno. Una vez que has escrito tu cdigo con aserciones puedes elegir entre activarlas o no en tiempo de ejecucin! Recuerda que las aserciones estn desactivadas por defecto. Activas las aserciones en tiempo de ejecucin usando:

Enable significa activar, as que "enableassertions" es "activar aserciones". En eclipse, los parmetros se introducen en este men:

email: brujulato@yahoo.es

Pgina 141

Curso gratuito de Java en emuladores L2 Java

2012

Tambin tienes que conocer la lnea de comandos para desactivar las aserciones:

12.2.4 Selector para activar y desactivar los assert La lnea de comando para aserciones puede ser usada de diferentes maneras:

Sin argumentos (como en el ejemplo), que activa o desactiva las aserciones en todas las clases, excepto para las clases del sistema. Con el nombre de un paquete, de esta manera se especifica el paquete donde quieres que acte (o no) la asercin. Con el nombre de la clase, al igual que con el nombre del paquete.

email: brujulato@yahoo.es

Pgina 142

Curso gratuito de Java en emuladores L2 Java

2012

Puedes combinar los interruptores para decirle que active las aserciones en una clase y que no se activen en las dems, tal como:

Esta lnea de comando le dice a la JVM que active las aserciones para todo el programa excepto para la clase en com.geeksanonymous.Foo. Puedes seleccionar todo el paquete haciendo:

Esta lnea le dice a la JVM que active las aserciones, pero que las desactive en el paquete com.geeksanonymous y todos sus subpaquetes. Puede que el trmino de subpaquetes no te sea familiar, ya que no hemos hablado de ellos. Un subpaquete es un paquete en un directorio del paquete nombrado. Por ejemplo:

Este rbol lista:


com geeksanonymous twelvesteps

y tres clases:
com.geeksanonymous.Foo com.geeksanonymous.twelvesteps.StepOne com.geeksanonymous.twelvesteps.StepTwo

El subpaquete de com.geeksanonymous es el paquete twelvesteps. Recuerda que en Java el com.geeksanonymous.twelvesteps se trata como un paquete distinto y que no tiene relacin con los paquetes que hay por encima de el, tan slo estn compartiendo directorio. La siguiente tabla muestra ejemplos para activar o desactivar aserciones en la lnea de comandos:

email: brujulato@yahoo.es

Pgina 143

Curso gratuito de Java en emuladores L2 Java


12.2.5 El uso apropiado para las aserciones No todos los usos vlidos de las aserciones se consideran de uso apropiado.

2012

Como ocurre con gran parte de Java, se puede abusar del uso de las aserciones a pesar de los esfuerzos de los ingenieros para disuadir de hacer esto. Por ejemplo, nunca te harn manejar una asercin que falle. Esto quiere decir que no deberas atraparla con catch para intentar manejarla. Legalmente, sin embargo, AssertionError es subclase de Throwable, asi que puede ser atrapado. Pero no lo hagas!! Si intentas recuperar algo, debera ser una excepcin. Para disuadirte de que sustituyas una asercin de una excepcin, el AssertionError no te da acceso al objeto que lo gener. Todo lo que consigues en un mensaje de texto.

No use las aserciones para validar argumentos de un mtodo pblico Esto es un ejemplo de uso inapropiado:

Un mtodo pblico puede ser invocado desde cdigo que no controlas (o desde un cdigo que jams has visto). Ya que los mtodos pblicos son parte de tu interface al mundo exterior, se supone que tienes que garantizar cualquier anomala en los argumentos que el mtodo sea forzado a tomar. Pero las aserciones no garantizan que se ejecuten (normalmente se desactivan en las aplicaciones), el cdigo no se ejecutara si las aserciones estn desactivadas. No quieres cdigo accesible que funcione solo condicionalmente, dependiendo de si las aserciones estn activadas. Si necesitas validar los argumentos de un mtodo pblico, probablemente arrojars excepciones, es decir IllegalArgumentException, si el valor pasado al mtodo pblico no es vlido.

Usar aserciones para validar argumentos en un mtodo privado Si escribes un mtodo privado, seguramente escribirs algn cdigo que lo ejecute. Cuando asumes que la lgica del cdigo es correcta, puedes evaluar la suposicin con una asercin de esta manera:

La nica diferencia importante entre en el ejemplo anterior y este, es el modificador de acceso. As, puedes forzar a cumplir las condiciones en los mtodos privados, pero no en los mtodos pblicos.

email: brujulato@yahoo.es

Pgina 144

Curso gratuito de Java en emuladores L2 Java

2012

No uses aserciones para validar una lnea de comando tomada como argumento
Esto es realmente un caso especial. Si tu programa requiere una lnea de comandos de argumentos, necesitars usar excepciones.

No uses aserciones en mtodos pblicos para evaluar casos que sabes que nunca podran suceder.
Esto incluye bloque de cdigo que nunca podra ser ledo, incluyendo el default de un bloque switch como este:

Si asumes que un cdigo en particular podra no ser ejecutado, como en el ejemplo donde hay una asercin, entonces podras obtener un falso error porque este cdigo nunca sera ejecutado.

No uses una asercin en expresiones que puedan causar efectos colaterales!


Esto podra ser muy mala idea:

la regla es, una asercin dejara el programa en el mismo estado que estaba antes de la expresin! Piensa en ello. Las aserciones no garantizan que se ejecuten siempre, as que si no quieres que tu cdigo tenga un comportamiento distinto dependiendo de si las aserciones estn activadas, las aserciones no deben causar algn efecto.

email: brujulato@yahoo.es

Pgina 145

Curso gratuito de Java en emuladores L2 Java


13.1 La clase String

2012

La clave del objeto String, es que una vez creado, no puede ser cambiado, as que, que es lo que realmente ocurre cuando un objeto String parece que ha cambiado?

Los objetos String inmutables Empezaremos con un poco de informacin de trasfondo sobre las cadenas. El manejo de caracteres es un aspecto muy fundamental en la mayora de los lenguajes de programacin. En Java, cada carcter en una cadena es un carcter Unicode de 16 bits. Ya que los caracteres Unicode son de 16 bits (no los escasos 7 u 8 bits que ASCII provee), el alfabeto internacional de caracteres se representa como Unicode. En Java, las cadenas son objetos. Al igual que cualquier objeto, puedes instanciar una cadena con new:

Esta lnea de cdigo crea un nuevo objeto de la clase String, y le asigna la variable de referencia s. Hasta ahora, los objetos String se parecen a cualquier otro objeto. Ahora asignemos una cadena como valor:

Tal como esperabas la clase String tiene un buen puado de constructores, as que puedes usar el atajo que ms te convenga:

y ya que usars cadenas todo el tiempo, incluso puedes hacer esto:

Hay algunas diferencias entre estas opciones, que veremos ms tarde, pero todas tienen en comn que crean un nuevo objeto String, con el valor abcdef y se le asigna la variable de referencia s. Ahora digamos que quieres una segunda referencia a un objeto String al que se refiere s:

Todo bien hasta ahora. Los objetos String parece que se comportan de la misma manera que los dems objetos, as que, cual es la pega?... La inmutabilidad! (Y qu diablos es la inmutabilidad?) Una vez que has asignado un valor a String, el valor nunca cambia, es inmutable, no cede. (Hablaremos de esto ms tarde.) Las buenas noticias es que mientras un objeto String es inmutable, su referencia no, as que continuamos con nuestro anterior ejemplo:

email: brujulato@yahoo.es

Pgina 146

Curso gratuito de Java en emuladores L2 Java

2012

Ahora espera un minuto, dijimos que era inmutable? Que es lo que pasa con "aadir un literal al final de la cadena?" Excelente pregunta, veamos que ha pasado realmente... La JVM tom el valor del objeto s y aadi el literal al final, dndonos el valor "abcdef mas cosas". Ya que las cadenas son inmutables, la VM no puede dar este nuevo valor a la variable de referencia as que lo que hace es reasignar la variable de referencia al nuevo valor. Llegados a este punto hay dos objetos de cadena creados, el antiguo y el nuevo. Tcnicamente son tres porque el literal para concatenar es otra cadena nueva. Pero slo tenemos referencias a "abcdef" y "abcdef mas cosas" (referenciado ahora por s). Que pasa sino tenamos previsto crear una segunda variable de referencia para "abcdef" antes de que concatenramos " mas cosas"? En este caso, la cadena original aun estar en memoria pero se la considera "perdida". Ningn cdigo de nuestro programa tiene alguna manera de referenciarla. Acurdate de que la cadena original, es inmutable, as que no ha cambiado, lo que si ha cambiado es la referencia de la variable s. Otro ejemplo:

ahora:

otro:

Como ves, el uso del mtodo sobre la referencia no implica que la antigua referencia se pierda, al contrario, si la nueva cadena no es referenciada, es la que se pierde. La discusin que sigue contiene las claves de la inmutabilidad de las cadenas de Java. Cubriremos ms detalle de la clase String, pero no te equivoques, lo que hemos cubierto est lejos de ser la parte ms importante para entender cmo funcionan los objetos String en Java. Terminaremos esta seccin representando un ejemplo de pregunta endiablada. Toma tu tiempo para escribir en una hoja el resultado de lo siguiente:

email: brujulato@yahoo.es

Pgina 147

Curso gratuito de Java en emuladores L2 Java

2012

Cul sera la salida? Cuantas variables de referencia hay? Cuantos objetos String fueron creados antes de la salida? Respuesta: El resultado es "spring winter spring summer". Hay dos variables de referencia, s1 y s2. Haba en total ocho objetos creados: "spring", "summer" (se pierde), "spring summer" (se pierde), "spring fall" (se pierde), "spring summer spring" (se pierde), "winter" (se pierde), "spring winter" (spring se pierde aqu). Solo dos de los ocho objetos String no se pierden en este proceso.

13.1.1 String y memoria En esta seccin vamos a ver como Java maneja los objetos String en memoria, y algunas de las razones que dan a lugar a este comportamiento. Una de las claves de cualquier buen programador es hacer un eficiente uso de la memoria. Ya que la aplicacin crece, es normal que las cadenas literales ocupen grandes cantidades de memoria y a menudo son redundantes en el universo de cadenas literales del programa. Para hacer ms eficiente el uso de la memoria, la JVM tiene un rea especial de memoria llamada "Piscina de constantes de String". Cuando el compilador encuentra un String literal, verifica que en la piscina exista otra idntica. Si existe, la referencia al nuevo literal es dirigida a la ya existente, no a la nueva creada. Ahora podemos empezar a ver por qu hacer a los objetos String inmutables es una buena idea. Si hay varias variables de referencia apuntando a la misma cadena aun sin saberlo, no sera buena idea cambiar el valor de la cadena. Ahora puedes pensar "Bien eso est bien, pero que pasa si alguien sobrescribe la clase String, no podra causar eso un error en la pool?" Esa es una de las principales razones por la que la clase String es marcada como final, nadie puede sobrescribir el comportamiento de ninguno de los mtodos de String, as que puedes estar tranquilo que los objetos inmutables, sern, inmutables. Veamos un par de ejemplos de cmo se puede crear un String, y asumamos que no existen ms objetos en la pool:

email: brujulato@yahoo.es

Pgina 148

Curso gratuito de Java en emuladores L2 Java


En este caso, "abc" ira a la pool y s lo referenciara.

2012

En este caso, ya que usamos new, Java crear una nueva cadena en la memoria normal (no en la pool), y s la referenciar. Adems, "abc" ira a la pool.

13.1.2 Mtodos importantes de la clase String Los siguientes mtodos son algunos de los ms comunes usados en la clase String:

charAt() Devuelve el carcter localizado en el ndice especificado. concat() Concatena una cadena con el final de la otra (lo mismo que "a"+"b"). equalIgnoreCase() Determina la igualdad de dos cadenas ignorando las maysculas. length() Devuelve el nmero de caracteres en una cadena. replace() Reemplaza las concurrencias de un carcter con otro carcter. substring() Devuelve una parte de la cadena. toLowerCase() Devuelve una cadena con los caracteres en minsculas. toString() Devuelve el valor en forma de cadena. toUpperCase() El opuesto a toLowerCase(). trim() Elimina los espacios de una cadena.

Veremos los metodos en detalle.

public char charAt(int index) Este mtodo devuelve el carcter localizado en el ndice especificado. Recuerda, el primer ndice es el cero.

public String concat(String s) Este mtodo devuelve una cadena con el valor del parmetro aadido al final de la cadena.

Los operadores + y += funcionan de forma similar:

email: brujulato@yahoo.es

Pgina 149

Curso gratuito de Java en emuladores L2 Java

2012

En el ejemplo "atlantic ocean", tienes que darte cuenta del valor de x, ha cambiado. Recuerda que la operacin += es de asignacin, as que realmente est creando la nueva cadena "Atlantic ocean" y asignndole la variable x, quedando "Atlantic" abandonada.

public boolean equalsIgnoreCase(String s) Este mtodo devuelve un valor booleano (true o false) dependiendo de si el valor de la cadena es igual al del mtodo. Este mtodo devolver true aunque los caracteres sean capitales. Ejemplo:

public int length() Este mtodo devuelve la longitud de la cadena usada para invocar el mtodo, ejemplo:

public String replace(char old, char new) Este mtodo devuelve una cadena cuyo valor es la cadena invocada en el mtodo actualizando la concurrencia. Donde el primer carcter ser reemplazado por el segundo:

email: brujulato@yahoo.es

Pgina 150

Curso gratuito de Java en emuladores L2 Java


public String substring(int begin) public String substring(int begin, int end)

2012

Este mtodo devuelve una parte de la cadena suministrada. El primer argumento representa la localizacin (la primera letra ocupa el ndice cero) de la subcadena. Si la llamada tiene solo un argumento, la subcadena se extraer hasta el final de la cadena original. Si tiene dos argumentos, la subcadena terminara en el carcter localizado en esa posicin. Ejemplo:

public String toLowerCase() Este mtodo devuelve una cadena cuyo valor es la cadena usada para invocar el mtodo, pero con todas las letras maysculas convertidas a minsculas. Por ejemplo:

public String toString() Este mtodo devuelve el valor de la cadena usada para invocar el mtodo. Qu? Para que iba yo a necesitar tal cosa? Un mtodo que no hace nada? Todos los objetos en Java tienen un mtodo toString(), que devuelve una cadena significativa que describe el objeto en cuestin. En caso de una cadena, que ms significativo podra ser que el mismo valor de la cadena? Para ms inri, un ejemplo:

email: brujulato@yahoo.es

Pgina 151

Curso gratuito de Java en emuladores L2 Java


public String toUpperCase()

2012

Este mtodo devuelve una cadena cuyo valor es la cadena usada para invocar al mtodo, pero todos los caracteres en minsculas los devuelve en maysculas. Por ejemplo:

public String trim() Este mtodo devuelve una cadena cuyo valor es la cadena usada para invocar el mtodo, pero los espacios en blanco son eliminados. Ejemplo:

13.2 StringBuffer y StringBuilder Las clases java.lang.StringBuffer y java.lang.StringBuilder se usan cuando tienes que hacer un montn de modificaciones a una cadena de caracteres. Tal como vimos antes, las cadenas son objetos inmutables, si eliges hacer un montn de manipulaciones con objetos de cadena, terminars con un montn de objetos perdidos en la pool de String. (Incluso en estos das de los gigabytes de RAM, no es buena idea desperdiciar la memoria en objetos descartados). Por otra parte, los objetos del tipo StringBuffer y StringBuilder pueden ser modificado tantas veces como quieras sin dejar por detrs ese rastro de objetos descartados. StringBuffer vs StringBuilder La clase StringBuilder fue aadida en Java 5. Tiene el mismo API que StringBuffer, excepto que StringBuilder no es un hilo seguro. En otras palabras, sus mtodos no estn sincronizados. Sun recomienda que uses StringBuilder en lugar de StringBuffer cuando sea posible porque StringBuilder se ejecuta ms rpido. As que aparte de la sincronizacin, cualquier cosa que digamos sobre los mtodos de StringBuilder valdr para StringBuffer y viceversa.

email: brujulato@yahoo.es

Pgina 152

Curso gratuito de Java en emuladores L2 Java


El uso de StringBuilder y StringBuffer

2012

Hemos visto que en el examen se te va a evaluar la compresin sobre la inmutabilidad de String en fragmentos como este:

Conseguimos una nueva cadena, pero en trasfondo, la cadena "abc" ha quedado perdida en la pool de String, malgastando memoria. Si hubiera usado un StringBuffer en lugar de String, seria:

Todos los mtodos de StringBuffer que discutiremos operan sobre el valor de StringBuffer invocado del mtodo. As que una llamada a sb.append("def"); est aadiendo "def" a si mismo. En efecto, estos mtodos pueden ser encadenados unos con otros:

Date cuenta que en los dos ejemplos anteriores, haba solo una llamada a new, y en cada ejemplo no se han creado objetos extras. Cada ejemplo necesitaba solo una lnea StringXxx para ejecutarse.

13.2.1 Mtodos importantes de las clases StringBuffer y StringBuilder Los siguientes mtodos devuelven un objeto StringXxx con el valor del argumento aadido al valor del objeto que invoca el mtodo.

email: brujulato@yahoo.es

Pgina 153

Curso gratuito de Java en emuladores L2 Java


public synchronized StringBuffer append(String s)

2012

Como vimos antes, este mtodo actualizar el valor del objeto que invoc el mtodo, dependiendo de si se le asigna una variable al retorno. Este mtodo toma muchos argumentos, incluyendo boolean, char, float, int, long y otros.

public StringBuilder delete(int start, int end) Este mtodo devuelve un objeto StringBuilder y actualiza el valor del objeto StringBuilder que invoc la llamada al mtodo. En ambos casos, se elimina una subcadena del objeto original. El ndice inicial (el primero es el cero) se define en el primer argumento y el ndice final (el primero tiene que ser al menos el 1!) es definido en el segundo argumento. Estudia detenidamente este ejemplo:

public StringBuilder insert(int offset, String s) Este mtodo devuelve un objeto StringBuilder y actualiza el valor del objeto que invoc la llamada del mtodo. En ambos casos, la cadena pasada en el segundo argumento se inserta dentro del original StringBuilder comenzando en la localizacin offset representado en el primer argumento (el primero es el cero). Tambin, como segundo argumento puede tomar cualquier tipo de dato, boolean, char, double, float, int, long...etc... pero es el tipo cadena lo que ms vas a ver:

email: brujulato@yahoo.es

Pgina 154

Curso gratuito de Java en emuladores L2 Java


public synchronized StringBuffer reverse()

2012

Este mtodo devuelve un objeto StringBuffer y actualiza el valor del objeto que invoc la llamada del mtodo. En ambos casos, la cadena en StringBuffer es invertida, el primer carcter se convierte en el ltimo:

public String toString() Este mtodo devuelve el valor del objeto StringBuffer que invoco al mtodo como una cadena:

Esto es todo para StringBuffer y StringBuilder. Recuerda que al contrario de los objetos String, StringBuffer y StringBuilder si mutan.

email: brujulato@yahoo.es

Pgina 155

Curso gratuito de Java en emuladores L2 Java


14.1 Resumen de las clases I/O

2012

File Este API dice que la clase File es una representacin abstracta de un fichero y el nombre de su ruta. La clase File no se usa para leer o escribir datos, se usa para trabajar a alto nivel, creando ficheros vacos, buscando ficheros, borrando, creando directorios y trabajando con rutas. FileReader Esta clase se usa para leer caracteres de un fichero. El mtodo read() es de bajo nivel, y te permite leer un solo carcter, de toda la cadena de caracteres, o un nmero fijo de caracteres. Los FileReader se usan para envolver objetos de alto nivel, como BufferedReader, que dan ms rendimiento y proveen de formas ms convenientes para trabajar con datos. BufferedReader Esta clase se usa para crear clases Reader de bajo nivel como FileReader, ms eficientes y fciles de usar. Comparado a los FileReader, BufferedReader es capaz de leer grandes cantidades de datos de una sola vez y ocuparlos en un buffer. Cuando te preguntes por el siguiente carcter o lnea de datos, se toma desde el buffer, que minimiza el nmero de veces de operaciones de lectura. Adems, BufferedReader te provee de ms eficientes mtodos tales como readLine(), que te permiten leer la siguiente lnea de caracteres de un fichero. FileWritter Esta clase se usa para escribir caracteres a un fichero. Su mtodo write() te permite escribir caracteres o cadenas a un fichero. Los FileWriter son, normalmente, envoltorios para objetos de alto nivel de tipo Writer, tales como BufferedWriter o PrintWriter, que proveen de mejor rendimiento y son de alto nivel, con mtodo ms flexibles para escribir datos. BufferedWriter Esta clase se usa para crear clases de bajo nivel como FileWriter, mas eficiente y fcil de usar. Comparado a FileWriter, BufferedWriter escriben grandes cantidades de datos en un fichero de una sola vez, minimizando el nmero de veces, que retrasa, las operaciones de escritura. Adems, la clase BufferedWriter provee del mtodo newLine() que hace ms fcil crear plataformas especficas que separan las lneas automticamente. PrintWriter Esta clase ha sido mejorada significativamente en Java 5. Ya que los nuevos mtodo y constructores creados (como PrintWriter con File o String), puedes encontrar que puedes usar PrintWriter en sitios donde antes necesitaras un Writer envuelto con FileWriter y/o un BufferedWriter. Nuevos mtodo como format(), printf() y append() hacen de PrintWriter una clase muy flexible y poderosa.

email: brujulato@yahoo.es

Pgina 156

Curso gratuito de Java en emuladores L2 Java


14.2 Creando ficheros, el uso de la clase File

2012

Los objetos del tipo File se usan para representar el fichero actual (pero no los datos que contiene) o directorios que existen en el disco fsico de la computadora. Comencemos con unos pocos ejemplos de creacin de ficheros, escribirlos y leerlos. Primero, crearemos un fichero y escribiremos unas lneas en el:

Si ejecutas este programa, cuando mires el contenido de tu directorio actual, descubrirs que no hay absolutamente ninguna indicacin de algn fichero llamado fileWrite1.txt. Cuando creas una instancia de File, no estas creando el fichero, slo el nombre. Una vez que tengas el objeto File, hay varias maneras para crear el fichero. Veamos lo que puede hacerse con el objeto File:

Esto da como salida:


false true true

Y tambin produce un fichero vaco en tu directorio. Si ejecutas el cdigo una segunda vez, se mostrar:
true false true

email: brujulato@yahoo.es

Pgina 157

Curso gratuito de Java en emuladores L2 Java


Examinemos las ejecuciones:

2012

Primera ejecucin: La primera llamada a exists() devuelve false, lo que esperbamos, recuerda, que new File() no crea un fichero en el disco! El mtodo createNewFile() crea el fichero, y devuelve true, indicando que ha sido creado un nuevo fichero, y que no exista antes. Finalmente, llamamos a exists() otra vez, y esta vez devuelve true, indicando que el fichero ya existe en el disco.

Segunda ejecucin:

La primera llamada a exists() devuelve true porque nosotros hemos creado el fichero en la primera ejecucin. Luego la llamada a createNewFile() devuelve false ya que el mtodo no cre el fichero esta vez. Naturalmente la ltima llamada a exists() devuelve true. Un par de cosas nuevas han pasado en este cdigo. Lo primero, fjate que tuvimos que poner nuestro fichero en un try-catch. Esto es as para todos los cdigos I/O que escribes. I/O es una de esas clases con riesgos inherentes. Seamos simples por ahora, e ignoremos las excepciones, pero aun necesitaremos seguir manejando o declarando la regla para la mayora de los mtodo I/O que declaren excepciones marcadas. Hablaremos ms sobre I/O. Usamos un par de mtodo de la clase File en este cdigo:

boolean exists(), este mtodo devuelve true si puede encontrar el fichero. boolean createNewFile(), este mtodo crea un nuevo fichero si no existe an.

14.3 El uso de FileWriter y FileReader En la prctica, lo ms probable es que no uses FileWriter y FileReader sin envoltorio (en breve se explica mas). Como dije antes, vayamos y hagamos un pequeo fichero I/O:

email: brujulato@yahoo.es

Pgina 158

Curso gratuito de Java en emuladores L2 Java

2012

lo que produce la salida:


12 howdy folks

Lo que ha pasado: FileWriter fw = new FileWriter(file) hizo estas tres cosas: 1. Se cre una variable de referencia fw de tipo FileWriter. 2. Se cre un objeto FileWriter y se asign a fw. 3. Se cre un fichero vaco en el disco (puedes comprobarlo). Escribimos 12 caracteres en el fichero con write(), y luego hizimos flush() y close(). Hicimos un objeto FileReader, que abri el fichero para lectura. El metodo read() ley todo el fichero, un carcter cada vez, y ponerlo dentro de char[]. Imprimimos el nmero de caracteres ledos, e hicimos un bucle en el array para imprimir cada carcter, luego, cerramos el fichero.

Antes de seguir, hablemos sobre flush() y close(). Cuando escribes un dato en una cadena, pueden ocurrir un montn de cosas en el bfer, y nunca sabrs exactamente cundo se envi el ltimo dato. Podras ejecutar muchas operaciones de escritura en una cadena antes de cerrarla. Al invocar el mtodo flush() antes de cerrar el fichero, te garantiza que el ltimo dato se ha escrito en el fichero.

email: brujulato@yahoo.es

Pgina 159

Curso gratuito de Java en emuladores L2 Java

2012

No importa cmo ests usando un fichero. Para lectura o escritura, tienes que invocar el mtodo close(). Cuando ests trabajando con ficheros I/O, ests usando una gran cantidad de recursos del sistema, as que cerrar el fichero liberar las tareas del sistema. Por mi cuenta he practicado un poco lo dicho aqu, porque no soy persona de estudiar de memoria, sino de comprender y asociar, y sin estudiar, continuar el temario es como leer y tener la cabeza ocupada en coches y motos, terminas el libro y no te has enterado de nada, as que all voy. Para empezar, acabo de acordarme, que sino inicializas el array, arroja un NullPointerException. Pensaba que se inicializara como lo hace PHP, automticamente dependiendo de la entrada. Pues no. Confirmado. Luego, qu ocurre si declaras un array de 50 bytes y solo ocupas una parte? Bien, este fue el cdigo que hice:

y este fue el resultado:

Podras haber querido experimentar y darte cuenta que al usar read() sin variable, el cdigo no lanza error, pero, sin una variable que haga referencia al contenido ledo, como lograras tener acceso a esta informacin, donde est, quien lo sabe?

email: brujulato@yahoo.es

Pgina 160

Curso gratuito de Java en emuladores L2 Java

2012

Ahora, volviendo a nuestro ltimo ejemplo del manual. Este programa en realidad, funciona, pero hay un par de cosas dolorosas: 1. Cuando ests escribiendo datos a un fichero, insertamos manualmente los operadores de salto de carro (en nuestro caso \n). 2. Cuando leemos los datos, los ponemos en un array. Al estar en un array, tuvimos que declarar su tamao por adelantado, as que tendramos un problema si no supiramos el tamao. Podimos leer los datos de carcter en carcter, buscando el final despus de cada read(), pero es bastante doloroso tambin. Por estas limitaciones, nosotros usamos clases de ms alto nivel, como BufferedWriter o BufferedReader en combinacin con FileWriter o FileReader.

14.4 Combinando clases I/O, BufferedWriter y BufferedReader Todo el sistema I/O de Java fue diseado con la idea de usar varias clases en combinacin. Combinar las clases I/O se le llama envolviendo clases y otras veces, encadenando clases. El paquete I/O de Java tiene ms o menos 50 clases,10 interfaces, y 10 excepciones. Cada clase en el paquete tiene un propsito especfico (creando una alta cohesin) y estn diseadas para ser combinadas unas con otras de incontables maneras para manejar una amplia variedad de situaciones. Cuando llega la hora de hacer algo con I/O en la vida real, sin duda te encontrars con la duda de que API usar o que clases vas a necesitar y como ponerlas juntas.

email: brujulato@yahoo.es

Pgina 161

Curso gratuito de Java en emuladores L2 Java

2012

Ahora, digamos que queremos encontrar una manera menos dolorosa de escribir datos en el fichero y leer el contenido del fichero a la memoria. Empezando por la tarea de escribir datos en un fichero, hay un proceso para determinar que clase necesitaremos, y como juntarlas: 1. Ya sabemos que vamos a usar un objeto File. As que si usamos una u otra clase, una de ellas tiene que llevar un constructor que tome un objeto de tipo File. 2. Encontrar un mtodo que suene como el ms poderoso, el ms fcil de usar para hacer la tarea. Cuando miramos la tabla, podemos ver que BufferedWriter tiene el mtodo newLine(). Suena algo mejor que tener que hacer un separador a mano en cada lnea, pero ms abajo vemos que PrintWriter tiene un mtodo llamado println(). Parece que esto es lo que ms se aproxima de todo lo que tenemos. Vamos con el. 3. Cuando miramos los constructores de PrintWriter, vemos que puede construir un objeto PrintWriter si tenemos un objeto de tipo File, as que todo lo que necesitamos hacer es crear un objeto PrintWriter, tal como se muestra:

Hora de un poco de historia. Antes de Java 5, PrintWriter no tena constructores que tomaran String o File. Si estuvieras escribiendo algo de I/O en Java 1.4, como haras para conseguir que PrintWriter escribiera datos en File? Puedes imaginrtelo estudiando la tabla. Aqu hay una manera de resolver este puzle. Primero, sabemos que crearemos un objeto File al final, y que queremos un objeto PrintWriter. Podemos ver en la tabla que PrintWriter puede tambin construirse usando un objeto Writer. Aunque Writer no es una clase tal como vemos en la tabla, vemos que varias clases heredan Writer, lo que se supone como bueno, cualquier clase que herede Writer es un buen candidato. Mirando ms adelante, vemos que FileWriter tiene dos atributos que estamos buscando: 1. En el constructor se usa un File 2. Hereda Writer Dada esta informacin, podemos poner junto el siguiente cdigo (recuerda que es un ejemplo de Java 4):

Hasta aqu se ve que es fcil encadenar las clases para leer los datos del fichero a la memoria. Nuevamente, vemos que en la tabla, vemos una tabla que se llama readLine() que suena mucho mejor para leer datos. Podemos hacer el mismo proceso con el siguiente cdigo:

email: brujulato@yahoo.es

Pgina 162

Curso gratuito de Java en emuladores L2 Java

2012

14.5 Ficheros y directorios Antes vimos que la clase File se usa para crear ficheros y directorios. Adems, los mtodo de File pueden ser usados para borrar ficheros, renombrar ficheros y determinar si un fichero existe, crear ficheros temporales, cambiar los atributos de un fichero y hacer diferencias entre fichero y directorio. Un punto en el que a menudo se confunde es que un objeto de tipo File se usa para representar un fichero o un directorio. Hablaremos de ambos en breve. Como vimos antes, el estamento:

siempre va a crear un objeto File, y luego pueden pasar una de estas cosas: 1. Si "foo" no existe, no se crea ningun fichero. 2. Si "foo" existe, el nuevo objeto File apuntara al fichero existente. Hay que fijarse que File file = new File("foo"); NUNCA crea un fichero. Hay dos maneras de crear un fichero: Invocar al mtodo createNewFile(), por ejemplo:

Crear un objeto Stream, Reader o Writer. Concretamente, un FileReader, un FileWriter, un PrintWriter, un FileInputStream o un FileOutputStream. Si creas una instancia de alguna de estas clases, estas creando un fichero, a menos que ya exista, por ejemplo:

email: brujulato@yahoo.es

Pgina 163

Curso gratuito de Java en emuladores L2 Java

2012

Crear un directorio es similar a crear un fichero. Al igual que un fichero, crear un directorio es un proceso de dos pasos, primero creamos el directorio (usando File), luego creamos un directorio usando el mtodo mkdir().

Una vez que tienes el directorio, pones ficheros dentro, y trabajas con esos ficheros:

Este cdigo crea un fichero en el directorio. Ya que le suministras el directorio al constructor, lo que queda es el objeto File. En este caso existe una manera de escribir datos a myFile:

Ten cuidado al crear nuevos directorios! Tal como hemos visto, un Reader o un Writer crearn tu fichero automticamente si no existe, pero no es el caso de un directorio:

Esto generar una excepcin:


java.io.IOException: No such file or directory

Puedes referirte a un objeto File existente, ya sea fichero o directorio. Por ejemplo, asumamos que ya tenemos un subdirectorio llamado existingDir en la cual existe existingDirFile.txt, que contiene varias lneas de texto. Cuando ejecutas este cdigo:

email: brujulato@yahoo.es

Pgina 164

Curso gratuito de Java en emuladores L2 Java


generar la siguiente salida:
true true existing sub-dir data line 2 of text line 3 of text

2012

Presta especial atencin a lo que devuelve el mtodo readLine(). Cuando no hay ms lneas, el mtodo devuelve null, que es nuestra seal para parar la lectura del fichero. Tambin, no se ha invocado el mtodo flush(). Cuando se lee un fichero, no se requiere, as que no vas a encontrar un flush() en una clase de lectura (Reader). Adems de crear ficheros, la clase File te permite renombrar y borrar ficheros. El siguiente cdigo muestra una de las cosas ms comunes como el borrar y renombrar archivos:

da como salida: delDir is false y te deja un directorio llamado newDir que contiene un fichero llamado newName.txt. Aqu hay algunas reglas que podemos deducir del resultado:

delete() Puedes borrar un directorio si est vaco. renameTo() Le tienes que pasar un objeto File vlido con el nuevo nombre que quieres (Si newName ha sido null, podra dar un NPE). Tambin puedes cambiar el nombre de un directorio, aunque no est vaco.

Hay un montn ms para aprender en el paquete IO de Java, pero nos vamos a detener en una cosa ms, y es como buscar un fichero. Se asume que tenemos un directorio llamado searchThis que queremos buscar, el cdigo usa File.list() para crear un array de tipo String de ficheros y directorios, luego usaremos el for mejorado para recorrer el array e imprimir:

email: brujulato@yahoo.es

Pgina 165

Curso gratuito de Java en emuladores L2 Java

2012

Y conseguimos la siguiente salida:


found found found found found dir1 dir2 dir3 file1.txt file2.txt

En esta seccin hemos araado la superficie de lo que hay disponible en el paquete IO de Java. Se podra escribir un libro entero hablando de este paquete, as que obviamente hemos cubierto una muy pequea parte (pero usada frecuentemente) del API.

email: brujulato@yahoo.es

Pgina 166

Curso gratuito de Java en emuladores L2 Java


15 Paquete I/O, la serializacin

2012

Imagina que quieres guardar el estado de uno o ms objetos. Si Java no tuviese serializacin (como vimos, en la 1.4 no lo tiene), tendras que usar una de las clases I/O para escribir el estado de las variables de instancia de todos los objetos que quieres guardar. La peor parte podra ser la reconstruccin de los objetos que fueron virtualmente creados, idnticos a los que intentas guardar. Necesitaras tu propio protocolo para crear la manera en la que habra que escribir y recuperar el estado de cada objeto, o terminaras inicializando las variables con valores equivocados. Por ejemplo, imagina que guardaste un objeto que tiene variables de instancia para la altura y el peso. A la vez que grabas el estado del objeto, podras escribir la altura y el peso como dos int en un fichero, pero el orden es crucial. Sera demasiado fcil recrear el objeto sin equivocar la posicin de los valores. La serializacin te permite decir "graba este objeto y todas sus variables de instancia". Es un poco ms interesante que eso, porque puedes aadir,... a menos que hayas marcado variables explcitamente como transient, lo que da a entender que no se incluir el valor de esa variable como parte del estado del objeto serializado.

15.1 ObjectOutputStream y ObjectInputStream La magia de la serializacin consiste en slo dos mtodos: uno para serializar objetos y escribirlos es un stream, y el otro para leer el stream y deserializar el objeto.

Estas clases son consideradas de alto nivel, y como vimos antes, significa que tendrs que envolverlas en clases de bajo nivel, al igual que las clases java.io.FileOutputStream y java.io.FileInputStream. Aqu se muestra un pequeo programa que crea un gato (Cat) que lo serializa, y deserializa.

email: brujulato@yahoo.es

Pgina 167

Curso gratuito de Java en emuladores L2 Java


Examinemos los puntos claves de este ejemplo:

2012

Declaramos que la clase Cat implementa la interfaz Serializable. Serializable es una interfaz marcada, no tiene mtodos que implementar (En las siguientes secciones cubriremos las reglas que hablan sobre declarar clases Serializable). Creamos un nuevo objeto Cat, que ya sabemos que es serializable.

Serializamos el objeto Cat c invocando el mtodo writeObject(). Esto tom cierto trabajo antes de que serializramos nuestro Cat. Primero, tuvimos que poner todo el cdigo entre try-catch. Luego habamos creado un objeto para escribir FileOutputStream. Despus envolvimos el FileOutputStream en un ObjectOutputStream, que es la clase que tiene el mtodo mgico de serializacin que necesitamos. Recuerda que la invocacin de writeObject() tiene dos tareas, serializa el objeto y luego lo escribe en un fichero. Luego deserializamos el objeto invocando readObject(). El mtodo devuelve un objeto, as que tenemos que castearlo para volverlo Cat. De nuevo, tuvimos que hacer las acciones tpicas que requiere el I/O cuando trabajamos con el.

Este ejemplo de serializacin ha sido a palo seco. Luego veremos casos ms complejos asociados a la serializacin. Por cuenta propia quera saber si era posible guardar ms de un objeto a la vez, intente hacer un array para guardar todos los objetos del mismo tipo pero no me sali. Sin embargo, este ejercicio me sirvi para saber tambin, que puedes guardar ms de un objeto a la vez y recuperarlos usando la operacin inversa. Es decir, guardo dos objetos Gato, y los leo de una sola vez, asignndole una variable de referencia a cada Gato guardado.

Miremos el ejercicio:

email: brujulato@yahoo.es

Pgina 168

Curso gratuito de Java en emuladores L2 Java

2012

La otra clase:

email: brujulato@yahoo.es

Pgina 169

Curso gratuito de Java en emuladores L2 Java

2012

Interesante, no es cierto? El resultado, por supuesto es correcto:


Gatos guardados :) Recuperando los gatos... Tenemos a Miguel y a Juan, gatos Blanco y Marron respectivamente.

email: brujulato@yahoo.es

Pgina 170

Curso gratuito de Java en emuladores L2 Java


15.2 Objetos grficos Que quiere decir realmente guardar un objeto? Si las variables de instancia son todas de tipo primitivo, es bastante directo. Pero qu pasa si las variables de instancia son en si mismas referencias a objetos? Que estamos grabando?

2012

Claramente, en Java no tendra sentido grabar los valores actuales de la variable de referencia porque el valor de la referencia tiene solo el contexto de la instancia de la JVM. En otras palabras, intentaras recuperar el objeto en otra instancia de la JVM, que aunque este corriendo en la misma maquina donde el objeto fue serializado, la referencia seria intil. Que le ocurre al objeto de la variable de referencia que tiene asignada? Miremos esta clase:

Ahora crearemos un perro con un collar, primero, el collar del perro con un tamao:

luego el perro, y le pasamos un collar y la talla de para el perro:

Ahora que pasa si guardas al perro? Si la finalidad es grabar y recuperar un perro, y el perro recuperado es una duplica exacta del perro que fue guardado, entonces el perro necesita un collar que sea idntico. Esto significa que el collar del perro debera haberse guardado tambin. Y qu ocurre si el collar en si mismo tambin hace referencia a otro objeto? Como el color? Esto se complica rpidamente. Si dependiera del programador saber cmo est estructurado cada objeto que se est refiriendo al perro, tendra que guardar el estado de todos los objetos... que faena. Podra ser una pesadilla con el ms simple de los objetos.

email: brujulato@yahoo.es

Pgina 171

Curso gratuito de Java en emuladores L2 Java

2012

Afortunadamente, el mecanismo de serializacin de Java toma esto en cuenta. Cuando serializas un objeto, la serializacin toma el objeto completo de forma grfica. Esto significa que hace una copia muy profunda de todos los objetos que son necesarios recuperar. Por ejemplo, si serializas un perro, el collar ser serializado automticamente. Y si el collar hiciera referencia a otro objeto, seria serializado tambin, y as hasta finalizar, siempre que no olvides aadir la interfaz serializable a las dems clases que incluyas en Dog. Y el nico objeto del que tienes que preocuparte es de guardar y leer al perro. Los otros objetos requeridos para reconstruir al perro son guardados y recuperados automticamente a travs de la serializacin. Recuerda que tienes que ser consciente de crear objetos serializable implementando la interfaz Serializable. Si queremos guardar al perro, tendremos que modificar la clase como sigue:

Si ahora guardamos al perro con el siguiente cdigo:

Cuando ejecutamos el cdigo, tenemos una excepcin como esta:


java.io.NotSerializableException: Collar

De que nos hemos olvidado? La clase collar tiene que ser Serializable tambin, si modificamos la clase collar y la hacemos serializable, no habr problema:

email: brujulato@yahoo.es

Pgina 172

Curso gratuito de Java en emuladores L2 Java


Aqu est la lista completa:

2012

Lo que produce:
before: collar size is 3 after: collar size is 3

Y que pasara si no tuviramos acceso al cdigo fuente del collar? En otras palabras, que pasa si hacer el collar serializable no es una opcin? Obviamente podramos crear una subclase collar serializable y luego usar la subclase en lugar del collar. Pero eso no es siempre una opcin por varias razones poderosas: 1. El collar podra ser una clase final 2. El collar podra referirse a otro objeto no serializable, y sin conocer la estructura interna, no podras hacer los arreglos necesarios. 3. Hacer subclases no es una opcin por razones de diseo. As que, qu pasa si queremos guardar al perro? Aqu es donde viene el modificador transient. Si marcas la variable de instancia con transient, la serializacin saltara el collar. email: brujulato@yahoo.es Pgina 173

Curso gratuito de Java en emuladores L2 Java

2012

Ahora tenemos un perro serializable, con un collar no serializable, pero el perro tiene el collar como transient, la salida es:
before: collar size is 3 Exception in thread "main" java.lang.NullPointerException

Y ahora qu hacemos??

15.3 El uso de writeObject y readObject


Considera el problema, tenemos un perro que queremos grabar. El perro tiene un collar y el collar debera de ser grabado como parte del perro, pero este collar no es serializable, as que tuvimos que marcarlo como transient. Esto significa que cuando el perro se deserialice, el collar viene null. Que podemos hacer para asegurarnos de que cuando el perro se deserialice, tenga un collar que encaje con el mismo collar que tena el perro cuando fue grabado? Java tiene un mecanismo especial para este caso, un serie de mtodos privados que puedes implementar en tu clase que, si estn presentes, sern invocados automticamente durante la serializacin y la serializacin. Es casi como si los mtodos fueran definidos en la interfaz Serializable, solo que no lo estn. Forman parte de un contrato con el sistema de serializacin que bsicamente dice "Si tu (el programador) tienes un par de mtodos que encajen con esta firma exacta (lo veremos en un momento) estos mtodos sern invocados durante la serializacin y la serializacin". Estos mtodos te permiten un paso intermedio en la serializacin y la serializacin. Son perfectos para el problema del collar del perro. Cuando el perro est siendo grabado, puedes entrar a mitad del proceso y decir "de cualquier manera, me gustara aadir el estado del collar (un int) al stream cuando el perro sea serializado". Manualmente has aadido el estado del collar del perro aunque el collar como tal, no se ha grabado. Naturalmente necesitaras recuperar el collar durante la deserializacin accediendo en ese paso intermedio y diciendo "Leer un int extra que guard en el stream del perro, y lo usar para crear un collar nuevo, y luego asignare el nuevo collar al perro que est siendo deserializado". Los dos mtodos especiales que defines deben ser firmas que son exactamente como esta:

email: brujulato@yahoo.es

Pgina 174

Curso gratuito de Java en emuladores L2 Java

2012

Si, vamos a escribir mtodos que tienen el mismo nombre como los del ejemplo. Donde se colocan estos mtodos? Hagamos un cambio en la clase Dog (perro):

Echemos un vistazo al cdigo. En nuestro escenario hemos concretado que, por alguna razn real, no podamos serializar el collar pero, queramos serializar el perro (Dog). Para hacer esto, vamos a implementar dos mtodos, writeObject() y readObject(). Al implementar estos mtodos, le estas diciendo al compilador "Si alguien invoca writeObject() o readObject() para el objeto Dog, este cdigo tiene que ser parte de la lectura y la escritura". 1. Como la mayora de mtodos I/O, writeObject() puede arrojar excepciones. Tienes que declararlas o manejarlas, aunque recomendamos que las manejes. 2. Cuando invocas defaultWriteObject() desde el mtodo writeObject(), le estas diciendo a la JVM que haga una serializacin normal para este objeto. Cuando implementas writeObject(), normalmente estas pidiendo un proceso de serializacin normal, y adems algunos procesos personalizados de escritura y lectura. 3. En este caso hemos decidido escribir un extra int (el tamao del collar) al stream que se est creando para serializar al perro. Puedes escribir tu cdigo extra antes y/o

email: brujulato@yahoo.es

Pgina 175

Curso gratuito de Java en emuladores L2 Java

2012

despus de invocar defaultWriteObject(). Pero... cuando lo lees para recuperarlo, tienes que leerlo en el mismo orden que lo grabaste. 4. De nuevo, elegimos manejar y declarar ms excepciones. 5. Cuando toca deserializar, defaultReadObject() maneja la deserializacin normal que conseguiras sino implementases readObject(). 6. Finalmente construimos un collar nuevo para el perro usando la talla que habamos serializado manualmente. (Tuvimos que invocar readInt() despus de defaultReadObject(), si no, los datos del stream saldran desincronizados!) Recuerda, la mayora de las razones para implementar writeObject() y readObject() son cuando tienes que grabar alguna parte de un objeto de forma manual. Si lo haces, cuando quieras hacer solo una parte de la serializacin/deserializacin t mismo, tienes que invocar defaultReadObject() y defaultWriteObject(). Lo que trae otra pregunta, por qu no se pueden serializar todas las clases de Java? Por qu no es Object serializable? Hay muchas cosas en Java que no pueden ser serializadas porque estn en un entorno de ejecucin especfico. Cosas como streams, threads, runtime, etc. y algunas clases GUI (las cuales estn conectadas al sistema operativo) no pueden serializarse. Lo que es y no es serializable en Java no tienes que dominarlo, pero necesitars tenerlo en cuenta si quieres serializar objetos complejos.

15.4 La herencia y la serializacin La serializacin es algo sensacional, pero para aplicarla efectivamente tienes que entender como la superclase afecta a la serializacin. Esto nos trae otro tema clave con la serializacin... qu pasa si una superclase no est marcada como Serializable pero su subclase lo est? Puede la subclase ser serializada aunque su superclase no lo sea? Imagnate lo siguiente:

Ahora t tienes la clase perro serializable, con una superclase no serializable. Esto funciona! Pero hay importantes implicaciones. Para entender estas implicaciones, miremos la diferencias entre un objeto que viene de una serializacin, y otro que se ha creado con new. Recuerda, cuando un objeto se construye con new (lo opuesto a uno de una serializacin), ocurren estos hechos en este orden: 1. Todas las variables de instancia son iniciadas con el valor por defecto. 2. El constructor es invocado, lo cual implica que se invoca el constructor de la superclase (u otro constructor sobrecargado, hasta que se invoque el constructor de la superclase.) 3. Todos los constructores de la superclase se completan.

email: brujulato@yahoo.es

Pgina 176

Curso gratuito de Java en emuladores L2 Java

2012

4. Las variables de instancia inicializadas como parte de su declaracin se les asigna su valor inicial (lo contrario a los valores por defecto, son valores dados a priori por los constructores de la superclase.) 5. El constructor finaliza. Pero nada de esto ocurre en un objeto deserializado. Cuando una instancia de una clase serializada se deserializa, el constructor no se ejecuta, y las variables de instancia no se les asigna ningn valor inicial! Piensa en ello, si el constructor fuese invocado, o las variables de instancia se le asignasen los valores iniciales, el objeto que intentas recuperar en el estado que lo grabaste, volvera totalmente alterado. Por ejemplo, imagina que tienes una clase que declara una variable de instancia y le asigna el valor int 3, y le incluyes un mtodo que le cambia el valor a 10:

Obviamente, si serializas Foo despus de que el mtodo changeNum() se ejecute, el valor de la variable nim seria 10. Cuando la instancia Foo se deserializa, t quieres que la variable mantenga su valor de 10! Obviamente no quieres que se inicialice (en este caso, con el 3). Piensa en el constructor y la asignacin a la variable de instancia como una parte completada de la inicializacin del objeto (y el hecho, es que ellos hacen la inicializacin en el bytecode). El punto es, que cuando un objeto se deserializa no queremos que se ejecute la inicializacin. No queremos que el constructor se ejecute, y no queremos explcitamente valores declarados. Slo queremos los valores con los que fue grabado. Naturalmente si tienes variables marcadas como transient, no sern recuperadas con su valor original (a menos que implementes defaultReadObject()), pero en su lugar, se le darn los valores por defecto para el tipo de dato. En otras palabras, aunque digas:

cuando la instancia de Bar se deserialice, la variable x tendr el valor cero. Los objetos de referencia marcados como transient siempre se resetearan a null, no importa si fueron inicializados en tiempo de declaracin de la clase. As, que esto es lo que ocurre cuando el objeto se deserializa, y la clase serializada directamente hereda Object, o tiene solo clases serializables en su rbol de herencia. Tiene algo de trampa cuando la clase serializable tiene una o ms clases no serializables en su superclase. Volviendo a nuestra clase Animal con un Dog serializable:

email: brujulato@yahoo.es

Pgina 177

Curso gratuito de Java en emuladores L2 Java

2012

Ya que Animal no es serializable, cualquier estado mantenido en Animal, incluso la variable que se hereda en Dog, no va a ser recuperada cuando se deserialice! La razn es, que la clase Animal (no serializable) que es parte de Dog va a ser reiniciada igual que si estuvieras haciendo un nuevo Dog. En otras palabras, las variables de instancia de Dog sern serializadas y deserializadas correctamente, pero las variables heredadas de Animal sern iniciadas con sus valores por defecto. Si tienes una clase serializable pero tu superclase no lo es, entonces cualquier variable de instancia que heredes de la superclase reseteara sus valores a los suyos por defecto. Esto es porque el constructor de la superclase se ejecutar! De hecho, cada constructor encima de la clase no serializable tambin se ejecutar, no importa, porque una vez que el primer superconstructor sea invocado, su curso invocar sus superconstructores hasta el principio del rbol de herencia. Necesitars tambin reconocer que variables sern recuperadas y cules no, as que asegrate de estudiar el siguiente ejemplo y la salida:

email: brujulato@yahoo.es

Pgina 178

Curso gratuito de Java en emuladores L2 Java


Da como resultado:
before: Fido 35 after: Fido 42

2012

La clave aqu es Animal, que no es serializable, cuando el Dog fue deserializado, el constructor de Animal se ejecut y reseteo la variable weight.

15.5 Serializacin y clases estticas Finalmente, te has podido dar cuenta que hemos hablado solo de variables de instancia, no de variables estticas. Las variables estticas deberan ser grabadas aparte del estado del objeto? No es importante que la variable esttica sea serializada con el objeto? Si y no. Puede ser importante pero no es una parte del estado de la instancia. Recuerda, que debes pensar que las variables estticas son puramente variables de clase. Ellas no tienen nada que ver con las instancias individuales. Pero la serializacin se aplica solo a los objetos. Y que sucede si deserializa tres instancias distintas de Dog, que fueron serializados a distinta vez, y que todos fueron grabados con una variable esttica en la clase con un valor distinto? Que instancia debera prevalecer? Qu valor esttico debera ser usado para reemplazar el actual en cada clase Dog que est cargado actualmente? Ves el problema? Las variables estticas nunca se graban como parte del estado del objeto, porque no pertenecen al objeto!

email: brujulato@yahoo.es

Pgina 179

Curso gratuito de Java en emuladores L2 Java


16 Acceso a Datos en Java: JDBC y MySQL

2012

En el captulo anterior estudiamos la forma en que una aplicacin Java puede almacenar y recuperar informacin en ficheros de disco. No obstante, la mayora de los programas Java que manipulan informacin del disco, lo hacen con datos almacenados en una base de datos. Para tratar con bases de datos existen unos estndares que facilitan a las aplicaciones informticas, manipular la informacin contenida en ellas. En Java disponemos de un API especial basado en estos estndares que permite desarrollar aplicaciones Java para acceder a bases de datos relacionales, se trata del API JDBC. Ahora analizaremos este API, sin ninguna duda uno de los ms importantes que incluye la plataforma J2SE.

16.1 La tecnologa Java Database Conectivity La mayora de las aplicaciones necesitan acceder a los datos existentes en un Enterprise Information System, para ello necesitan disponer de alguna tecnologa, implementada en una librera de clases, que posibilite el envo de instrucciones SQL a la base de datos y la manipulacin de resultados. JDBC proporciona a las aplicaciones Java un mecanismo uniforme para el acceso a datos. La tecnologa JDBC consiste en la utilizacin de un conjunto de clases (API JDBC) que disponen de una serie de mtodos para operar con la base de datos. Utilizando estos mtodos, la aplicacin dirige todas las peticiones hacia un software intermediario, conocido como Driver JDBC, cuya misin es traducir las llamadas a los mtodos a rdenes nativas del gestor de la BD (Base de Datos). La principal ventaja que ofrece este sistema es que la aplicacin se independiza del tipo de base de datos utilizado para almacenar la informacin. En otras palabras, no hay que escribir un programa para acceder a Oracle, otro para Sybase, etc... dado que el API JDBC utiliza una serie de clases e interfaces genricas que actan sobre el driver, no sobre la base de datos. Tan solo es necesario disponer de un driver especfico para el tipo de base de datos con el que se va a trabajar.

16.2 El driver JDBC


Como se desprende de lo que hemos comentado, el driver JDBC juega un papel fundamental en las aplicaciones Java de acceso a datos. Por ello, vamos a comentar algunos aspectos relevantes sobre este antes de abordar los aspectos propios de la programacin.

email: brujulato@yahoo.es

Pgina 180

Curso gratuito de Java en emuladores L2 Java


16.2.1 Estructura y funcionamiento

2012

Bsicamente, el driver JDBC es una clase Java que implementa toda la funcionalidad del API JDBC, proporcionando la comunicacin entre la aplicacin y la base de datos. Normalmente, son los fabricantes de bases de datos los que distribuyen los driver JDBC aunque tambin se pueden encontrar en productos terceros, como entornos de desarrollo (IDE) o servidores de aplicaciones. En un driver JDBC se distinguen dos capas o interfaces:

Capa de aplicacin. En la parte del driver que interacta con la aplicacin, todos los driver JDBC, independientemente del tipo de base de datos para la que hayan diseado, proporciona la misma interfaz de aplicacin.

Capa de base de datos. Esta capa interacta con la base de datos, por lo que es especfica para cada tipo de base de datos.

16.2.2 Tipos de driver JDBC Independientemente del tipo de base de datos para el que se haya diseado, un driver JDBC puede pertenecer a una de las siguientes categoras:

Tipo 1: Driver puente JDBC-ODBC. Fueron los primeros tipos de driver JDBC que se lanzaron al mercado. Utilizan como intermediario un driver ODBC. Dada la gran expansin que en aquellos momentos tena la tecnologa ODBC y el amplio nmero de drivers ODBC existentes, el driver puente JDBC-ODBC puede utilizarse para acceder a la mayora de los tipos de bases de datos que hay en el mercado. La principal desventaja que tiene este driver es que es necesario configurar un DSN (Data Source Name) utilizado por ODBC, en el equipo donde se va a ejecutar la aplicacin. El driver puente JDBC-ODBC se incluye con el conjunto de clases del J2SE, concretamente se trata de la clase sun.jdbc.odbc.JdbcOdbcDriver.

email: brujulato@yahoo.es

Pgina 181

Curso gratuito de Java en emuladores L2 Java

2012

Tipo 2: Driver nativo El driver nativo convierte las llamadas JDBC en llamadas al API nativo del gestor de la base de datos. Su principal inconveniente es la necesidad de instalar el API nativo de la base de datos en el equipo donde reside la aplicacin.

Tipo 3: Driver intermedio Este tipo de driver convierte las llamadas JDBC en un protocolo especfico del middleware, a su vez, el servidor del middleware se encarga de transformar estas llamadas en el API nativo de la base de datos, proporcionando conectividad para diferentes tipos de bases de datos. La principal caracterstica de este driver es que no requiere ningn tipo de instalacin en el equipo donde se instala la aplicacin.

Tipo 4: Driver puro-Java Convierte las llamadas JDBC en el protocolo de red utilizado directamente por el servidor de base de datos, permitiendo llamadas directas desde la maquina cliente (aplicacin) al servidor de base de datos. Este driver no requiere ningn tipo de configuracin especial en la mquina cliente.

16.3 El lenguaje SQL El lenguaje SQL surge ante la necesidad de disponer de un mecanismo para operar con la informacin almacenada en bases de datos relacionales de diferentes fabricantes. Este lenguaje es soportado por la mayora de gestores de bases de datos relacionales existentes en el mercado. Sus instrucciones, de estructura muy simple, permiten operar sobre un conjunto de datos en vez de tener que hacerlo individualmente.

16.3.1 Consultas Una consulta es cualquier expresin en SQL que defina una operacin a realizar sobre la base de datos. Una consulta est compuesta por los siguientes elementos:

Una accin o verbo que determina la operacin a realizar. Por ejemplo SELECT. Un objeto, combinacin de campos de las tablas de la base de datos. Una clusula que determina sobre que objetos acta el verbo. Ejemplo From tabla.

email: brujulato@yahoo.es

Pgina 182

Curso gratuito de Java en emuladores L2 Java

2012

Las consultas SQL se expresan mediante sentencias de texto (sentencias SQL), estas pueden ser incluidas dentro de un programa Java como parmetro de alguno de los mtodos de las clases/interfaces del API JDBC, tal y como veremos en el siguiente apartado.

16.3.2 Tipos de sentencias SQL


El juego de sentencias SQL se divide en tres grupos:

Sentencias DDL: En este grupo se incluyen aquellas sentencias que se encargan de la creacin, definicin y destruccin de objetos. Entre ellas destacan CREATE y DROP. Sentencias DCL: Permiten controlar aspectos varios como la confidencialidad de los datos. Entre estas cadenas desatacan GRANT y REVOKE. Sentencias DML: Estn incluidas en este grupo las sentencias utilizadas para manipulacin de datos, como son extraccin (SELECT), modificacin (UPDATE), insercin (INSERT) y borrado (DELETE).

16.3.3 Sentencias para la manipulacin de Datos (DML)


Estas son las sentencias que se utilizan en las aplicaciones que acceden a base de datos, por tanto, vamos a comentar ms detenidamente el funcionamiento de cada una de ellas. Sentencia SELECT La sentencia SELECT es la ms importante y la ms compleja de todas las sentencias que forman SQL. Se utilizan para extraer datos de una o varias tablas de la base de datos, su forma general operando sobre una nica tabla es:

Esta instruccin devuelve el conjunto de registros de la tabla indicada en FROM, que cumplen las condiciones establecidas en WHERE y que estn formados por los campos indicados en SELECT. Para tomar todos los campos de la tabla se escribir *, en lugar de los nombres de los campos. Los registros se devolvern ordenados segn el campo o campos indicados en ORDER BY. Las clusulas WHERE y ORDER BY son opcionales, por lo que en caso de que no se utilicen, se devolvern todos los registros de la tabla en el orden definido en el interior de la misma. email: brujulato@yahoo.es Pgina 183

Curso gratuito de Java en emuladores L2 Java

2012

La siguiente instruccin de ejemplo devolvera el campo "nombre" y "ciudad" de aquellos registros de la tabla "usuarios" cuya ciudad sea "Madrid". Adems, las filas se devolvern ordenadas por el "nombre" del usuario.

Tanto en este ejemplo como en los dems que irn apareciendo, cada clusula se escribe en una lnea diferente a efectos de una mayor claridad. Adems, SQL no es case sensitive por lo que las clusulas pueden escribirse indiferentemente en maysculas o en minsculas.

Condiciones de seleccin Las condiciones de seleccin de registros, llamadas tambin predicados, se establecen en la clusula WHERE mediante un criterio, que es una expresin cuyo resultado se evala como verdadero o falso. La forma general es:

A la hora de escribir la condicin hay que tener en cuenta que:

Si el valor no es numrico, deber escribirse entre comillas simples, aunque algunos gestores de BD como Access, obligan a que los valores tipo fecha se escriban delimitados por #. valor puede ser el resultado de otra instruccin SELECT. La clusula WHERE puede incluir varias expresiones de este tipo vinculadas con los operadores AND y OR.

Ordenacin de registros La clusula ORDER BY determina como se van a ordenar los registros segn la forma:

donde hay que tener en cuenta que:


Si dos o ms registros poseen el mismo valor de campo1, se ordenara segn campo2. El modo de ordenacin puede ser ascendente (ASC) o descendente (DESC). El modo de ordenacin predeterminado es ascendente.

email: brujulato@yahoo.es

Pgina 184

Curso gratuito de Java en emuladores L2 Java


Otro ejemplo:

2012

En este caso se estn solicitando todos los campos de aquellos registros de la tabla pedidos cuya fecha sea inferior al 1/1/04 y el campo pendiente sea false. Los registros se devolvern ordenados por coste descendente. Consultas SELECT sobre varias tablas Para obtener datos incluidos en distintas tablas, basta con indicar los nombres de estas en la clusula FROM, separadas por comas. Si alguno de los campos seleccionados se encuentra en ms de una tabla, hay que preceder a su nombre el de la tabla en la que se encuentra separado por un punto .. Cuando se realiza una consulta sobre varias tablas, se debe aadir una condicin en la clusula WHERE que ligue a los datos de ambas tablas, de lo contrario la consulta devolver el producto cartesiano de las filas de ambas tablas. Para poder ligar dos tablas, estas deben tener un campo comn, la unin entre ellas se lleva a cabo aadiendo la siguiente expresin en la clusula WHERE:

La siguiente instruccin obtiene el nombre de todos los alumnos apuntados al curso de SQL SERVER estando el campo nombre cuyo valor se quiere recuperar, y el campo curso, que contiene la condicin en tablas diferentes. Ambas tablas se encuentran relacionadas por un campo comn, id:

Instrucciones SELECT subordinadas Dentro de la clusula WHERE de una sentencia SELECT se pueden especificar otras instrucciones SELECT, a las que se considera subordinadas de la primera. La instruccin anterior tambin se puede escribir utilizando un SELECT subordinado de la siguiente manera:

email: brujulato@yahoo.es

Pgina 185

Curso gratuito de Java en emuladores L2 Java


Operadores

2012

Adems de los operadores simples ( < , > , = ,...) una clusula WHERE puede incluir otro tipo de operadores:

LIKE Se utiliza para buscar campos que contengan combinaciones de caracteres que cumplan ciertas condiciones: Campo LIKE constante_alfanum La constante alfanumrica puede contener caracteres cualquiera, e incluir comodines: o % cadena de longitud aleatoria o _ carcter no nulo o [x-y] carcter dentro del rango x- y BETWEEN Comprueba si el valor est comprendido entre los dos dados: o Exp1 [NOT] BETWEEN exp2 AND exp3 El resultado es verdadero si exp1 est comprendido entre exp2 y exp3:

IN Comprueba si un valor est incluido en una lista de valores o Expresin IN (cte1,cte2,cte3...) En lugar de una lista de constantes podra especificarse una sentencia SELECT subordinada, que no puede incluir la clusula ORDER BY y debe dar lugar a una tabla con una sola columna:

Sentencia INSERT Permite aadir una o ms filas a una tabla. Puede utilizarse tanto para aadir una nica fila como para copiar en una tabla un subconjunto de registros proveniente de otra. En el caso de aadir filas individuales se utiliza el formato:

Esta instruccin inserta en una tabla un registro cuyos valores se especifican en VALUES, cada valor ser asignado a un campo segn el orden especificado en la lista de nombres de campos. Si no se especifica la lista de campos, se asume que se suministraran valores en la lista de valores para todos los campos de la tabla.

email: brujulato@yahoo.es

Pgina 186

Curso gratuito de Java en emuladores L2 Java


La siguiente instruccin aade un nuevo registro en la tabla clientes:

2012

El formato para insertar varias filas en una tabla es:

La siguiente instruccin aade a la tabla alumnos todos los registros de la tabla alumnos2:

Sentencia DELETE Permite borrar una o varias filas de una tabla. Su formato es:

La instruccin elimina de la tabla a todos los alumnos que hayan cursados Access:

Sentencia UPDATE Su funcin es modificar los valores de ciertos campos en aquellos registros que cumplan una determinada condicin. El formato de esta instruccin es el siguiente:

Mediante la clusula SET se indican los valores que se van a asignar los campos. La siguiente consulta aplica un 5% de descuento a los cursos de Access:

email: brujulato@yahoo.es

Pgina 187

Curso gratuito de Java en emuladores L2 Java


16.4 El API JDBC

2012

Las clases e interfaces que forman parte de este API se encuentran en el paquete java.sql. La siguiente tabla contiene los elementos ms importantes de este paquete:

16.4.1 Utilizacin de JDBC para acceder a datos En toda aplicacin que utilice JDBC para acceder a datos, se distinguen cuatro fases o pasos a realizar: 1. 2. 3. 4. Conexin a la base de datos Ejecucin de consultas Manipulacin de registros Cierre de la conexin

A continuacin, describiremos cada uno de ellos, analizando las clases e interfaces implicadas en cada paso.

16.4.2 Gestin con la base de datos Para realizar cualquier operacin con la base de datos es necesario primeramente, establecer una conexin con la misma. Esta accin requiere la realizacin de dos operaciones:

Carga del driver

Mediante esta opcin, se prepara el driver JDBC para que pueda ser utilizado. Esto se realiza mediante el mtodo esttico forName() de la clasejava.lang.Class, cuyo formato es:

Este mtodo localiza, lee y enlaza dinmicamente con el driver, devolviendo un objeto Class asociado a la clase indicada. Se debe tener en cuenta que la llamada a forName() puede provocar la excepcin ClassNotFoundException, por lo que deber ser capturada en la aplicacin. La siguiente instruccin realizara la carga del driver puente JDBC-ODBC proporcionado por Sun:

email: brujulato@yahoo.es

Pgina 188

Curso gratuito de Java en emuladores L2 Java

2012

Creacin de la conexin Una vez cargado el driver se debe proceder a la conexin con la base de datos, operacin que se lleva a cabo con el mtodo esttico getConnection()de la clase DriveManager del API JDBC. EL formato del mtodo es:

La cadena url representa la direccin de la base de datos y su formato es:

donde subprotocol depende del tipo de driver utilizado y base_datos es el nombre de la base de datos. En L2J, el driver ms utilizado es de MySQL: com.mysql.jdbc.Driver Por otro lado, el mtodo getConnection() devuelve un objeto que implementa la interfaz Connection, la cual proporciona varios mtodos para manejar la conexin. La siguiente instruccin utiliza el driver JDBC-OCDB para establecer una conexin con una base de datos, cuyo nombre de fuente de datos es "empresa":

El mtodo getConnection() est sobrecargado, existiendo una versin que adems de la url permite suministrar el usuario y la contrasea para acceder a la base de datos. El siguiente ejemplo permitira establecer una conexin con una base de datos de Oracle llamada info, usando un driver nativo de Oracle:

Class.forName(oracle.jdbc.driver.oracleDriver); Connection cn = DriverManager.getConnection(jdbc:oracle:thin:@miservidor:1521:info,scot,tiger);

Debe tenerse en cuenta que, al igual que sucede con la mayora de los mtodos de API JDBC, getConnection puede provocar una excepcin de tipo SQLException que habr que capturar.

email: brujulato@yahoo.es

Pgina 189

Curso gratuito de Java en emuladores L2 Java


16.4.3 Ejecucin de consultas

2012

Una vez establecida la conexin con la base de datos, se emplear esta para enviar consultas SQL a la base de datos.

Creacin del objeto STATEMENT Las consultas SQL se manejan a travs de un objeto que implementa la interfaz Statement, cuya creacin se realiza mediante el mtodo createStatement() de la interfaz Connection:

Puede provocar una excepcin de tipo SQLException.

Ejecucin de la consulta SQL La interfaz Statement proporciona diversos mtodos para enviar una consulta SQL a travs de la conexin. Los ms importantes son:
o

boolean execute(String sql) Enva a la base de datos la consulta SQL proporcionada como parmetro. Si se trata de una consulta de accin (Insert, Delete o Update) el mtodo devuelve false indicando que no se generan resultados. En la consulta de seleccin (SELECT) devuelve true.

int executeUpdate(String sql) Enva una consulta de accin a la base de datos, devolviendo el nmero de registros afectados por la accin.

ResultSet ExecuteQuery(String sql) Enva una consulta de seleccin de registros a la base de datos, devolviendo un objeto ResultSet para su manipulacin.

En caso de producirse algn tipo de error en la ejecucin de la consulta, los tres mtodos generan una excepcin SQLException. Para enviar una consulta de actualizacin de la tabla empleados de la base de datos empresa, utilizaramos:

email: brujulato@yahoo.es

Pgina 190

Curso gratuito de Java en emuladores L2 Java


Cierre de la conexin

2012

Las consultas de accin no devuelven datos a la aplicacin para su tratamiento, por lo que una vez ejecutada la consulta/s, se debe proceder al cierre de la conexin. Esto permite liberar recursos de memoria y CPU en la mquina. El cierre de la conexin se realiza mediante el mtodo close() de la interfaz Connection:

Puede provocar una excepcin de tipo SQLException. La interfaz Statement tambin dispone de un mtodo close() para liberar el objeto Statement, esto debe realizarse antes de cerrar la conexin. Cuando se cierra una conexin, todos los objetos Statement que queden abiertos sern cerrados automticamente.

A fin de aclarar lo visto hasta ahora, el siguiente listado representa el mtodo main() de una clase en el que se reflejan todos los pasos para realizar la insercin de un registro en una base de datos. La informacin es solicitada a travs del teclado:

email: brujulato@yahoo.es

Pgina 191

Curso gratuito de Java en emuladores L2 Java

2012

Para la correcta ejecucin de este script, ha sido necesario configurar ECLIPSE, aadiendo el conector a la librera del programa. Dentro de la carpeta jre/libs/ext he colocado mysql-connector-java-5.1.6-bin.jar, descargado de: http://dev.mysql.com/downloads/connector/j/3.0.html

16.5 Manipulacin de registros El envo de una consulta de seleccin de registros a la base de datos devuelve como resultado un objeto que, adems de disponer de un cursor para desplazarse por el conjunto de registros seleccionados, permite acceder a los valores de los campos. Este objeto implementara la interfaz ResultSet. La interfaz ResultSet del API SQL proporciona mtodos para desplazarse por el conjunto de registros afectados por la consulta y manipular sus contenidos.

16.5.1 Obtener un objeto ResultSet Un objeto ResultSet se crea al invocar al mtodo executeQuery() del objeto Statement:

De forma predeterminada, este objeto posee la caracterstica de ser slo avance y lectura. Esto implica que el recorrido se deber hacer siempre desde el primero registro hacia delante y los contenidos de los campos no podrn ser modificados. Desde el punto de vista de la velocidad y el consumo de recursos, esta es la forma ms ptima en la que se puede presentar ResultSet, resultando adems esta funcionalidad suficiente en la mayora de los casos. No obstante, en apartados posteriores veremos cmo crear ResultSet desplazables de lectura/escritura.

email: brujulato@yahoo.es

Pgina 192

Curso gratuito de Java en emuladores L2 Java


16.5.2 Desplazamiento por el conjunto de registros

2012

Una vez obtenido el ResultSet, su cursor se encuentra situado en la posicin que est antes del primer registro. Para realizar el desplazamiento por los registros, la interfaz ResultSet proporciona el mtodo next(). La llamada a este mtodo desplaza el cursor al siguiente registro del conjunto, devolviendo como resultado un boolean que indica si la nueva posicin apuntada se corresponde con el registro (true), o si el cursor se ha salido del conjunto (false). Utilizando este mtodo y una instruccin while, es posible recorrer todos los registros desde el primero hasta el ltimo:

16.5.3 Acceso a los campos La interfaz ResultSet proporciona dos grupos de mtodos para acceder a los campos del registro actual, el que est siendo apuntado por el cursor (registro actual). Estos mtodos se ajustan a los formatos:

xxx getXxx(int posicion) xxx getXxx(String nombre_campo)

donde xxx puede ser el nombre de cualquiera de los tipos bsicos de Java ms Date, String y Object, debindose utilizar aquel mtodo que se corresponda con el tipo almacenado en el campo. El primer grupo de mtodos permite obtener el valor de un campo a partir de su posicin dentro del registro, siendo 1 la posicin del primer campo. Por su parte, el segundo grupo de mtodos obtiene el valor del campo a partir del nombre del mismo. El siguiente trozo de cdigo, muestra en pantalla el level de todos los PJs online:

El siguiente programa presenta otro ejemplo de utilizacin de JDBC para extraer la informacin de una base de datos. En este caso, se solicita a un usuario que introduzca su usuario y su contrasea por el teclado, utilizando esa informacin para comprobar si el usuario est o no registrado en la tabla clientes de una base de datos:

email: brujulato@yahoo.es

Pgina 193

Curso gratuito de Java en emuladores L2 Java

2012

email: brujulato@yahoo.es

Pgina 194

Curso gratuito de Java en emuladores L2 Java

2012

Ejemplo de una consulta con MySQL que muestra los personajes asociados a una cuenta en L2J.

16.5.4 Otros mtodos de la interfaz ResultSet Adems de los anteriores, la interfaz ResultSet proporciona los siguientes mtodos a travs de un cursor de slo avance y lectura:

boolean isFirst() Devuelve true si el cursor apunta al primer registro. boolean isBeforeFirst() Devuelve true si el cursor esta antes del primer registro. boolean isLast() Devuelve true si el cursor esta apuntando al ltimo registro. boolean isAfterLast() Devuelve true si el cursor esta despus del ltimo registro. int getRow() Devuelve la posicin del registro actual siendo 1 la posicin del primer registro.

Todos los mtodos de la interfaz ResultSet pueden lanzar una SQLException.

email: brujulato@yahoo.es

Pgina 195

Curso gratuito de Java en emuladores L2 Java


16.5.5 Cierre de un ResultSet

2012

El mtodo close() de la interfaz ResultSet permite cerrar el objeto y liberar los recursos utilizados por este, algo que resulta bastante adecuado para reducir el consumo de recursos y mejorar as el rendimiento de las aplicaciones. Si un mismo objeto Statement se utiliza para crear un segundo ResultSet, el primer ResultSet ser cerrado automticamente de forma implcita. Esto significa que si se desea tener dos ResultSet abiertos simultneamente, habr que crear dos Statement diferentes. De hecho, cualquier operacin que se realice con el objeto Statement despus de haber creado un ResultSet, provocar implcitamente el cierre inmediato de este.

16.5.6 El objeto ResultSetMetaData Informacin sobre los datos Adems de los datos en si, el API SQL proporciona una interfaz ResultSetMetaData, que permite obtener informacin sobre las caractersticas de los datos referenciados por un ResultSet. Obtener objeto ResultSetMetaData Primeramente, necesitamos obtener un objeto ResultSetMetaData para lo cual utilizaremos el mtodo getMetaData() de la interfaz ResultSet():

Acceso a la informacin La interfaz ResultSetMetaData proporciona los siguientes mtodos para obtener informacin sobre los datos:

int getColumnCount() Devuelve el nmero de columnas del conjunto de registros referidos por el ResultSet. String getColumnName(int posicin) Devuelve una constante entera que representa el tipo de dato SQL soportado por el campo. Las constantes de los tipos SQL se encuentran definidas en la clase java.sql.Types String getColumnTypeName(int posicin) Devuelve el nombre de tipo de dato soportado por el campo, segn est definido en el gestor de base de datos.

email: brujulato@yahoo.es

Pgina 196

Curso gratuito de Java en emuladores L2 Java

2012

Consultas preparadas Las consultas preparadas se basan en la utilizacin de consultas SQL precompiladas. La idea es precompilar una instruccin SQL, utilizando parmetros en vez de valores fijos, y sustituir estos por valores concretos en el momento de la aplicacin en aquellos casos en que vaya a utilizarse repetidas veces una determinada instruccin. Las consultas preparadas se gestionan mediante la interfaz PreparedStatement.

Creacin de un objeto PreparedStatement. Para la creacin de una consulta preparada utilizamos el mtodo preparedStatement de la interfaz Connection. Este mtodo recibe como parmetro la consulta SQL para su precompilacin:

Como se puede apreciar, los valores de los campos se indican mediante ? en la instruccin para su posterior sustitucin. Ntese que la ? hace referencia a un valor, independientemente de su tipo, por ello, no es necesario encerrar este smbolo entre comillas simples cuando se trate de valores de texto.

email: brujulato@yahoo.es

Pgina 197

Curso gratuito de Java en emuladores L2 Java


Asignaciones de parmetros

2012

Una vez creada la consulta preparada, ya queda para ser ejecutada tantas veces como se requiera. Pero antes, es necesario asignar valores a los parmetros definidos en la instruccin. Esta operacin se realiza con el siguiente grupo de mtodos existentes en la interfaz PreparedStatement:

pudiendo ser xxx el nombre de cualquier de los tipos bsicos de Java ms Date, Object y String. Por otro lado, indice_parametro es la posicin que ocupa el parmetro dentro de la instruccin, siendo 1 la posicin del primero. Para el ejemplo de la consola anterior, podramos hacer la siguiente asignacin de parmetros:

Ejecucin de la consulta Para proceder a la ejecucin de la consulta utilizaremos los mtodos execute() o executeQuery(), dependiendo de si es una consulta de accin o de seleccin de registros. En el ejemplo que estamos analizando:

email: brujulato@yahoo.es

Pgina 198

Curso gratuito de Java en emuladores L2 Java


ResultSet desplazable

2012

Como ya hemos visto, los ResultSet que hemos creado hasta el momento, nicamente permiten realizar desplazamientos hacia delante y el acceso de datos es solo lectura. Para disponer de un ResultSet con mayores prestaciones, tendramos que modificar la forma en que creamos los objetos Statement o PreparedStatement, utilizando las siguientes versiones de mtodos de creacin de consultas de la interfaz Connection:

Statement createStatement(int resultSetType, int resultSetConcurrency) PreparedStatement prepareStatement(String resultSetConcurrency) sql, int resultSetType,int

El parmetro entero resultSetType representa el tipo de ResultSet que pueden ser creados con el objeto. Los posibles valores que puede tomar este parmetro estn recogidos en las siguientes constantes definidas en la interfaz ResultSet:

ResultSet.TYPE_FOWARD_ONLY Los resultados creados son de tipo "solo avance", es el valor por defecto. ResultSet.TYPE_SCROLL_INSESITIVE Permite crear ResultSets que se desplacen en ambas direcciones, aunque no muestra los cambios que puedan realizar otros usuarios en la base de datos mientras el resultSet est abierto. ResultSet.TYPE_SCROLL_SENSITIVE Permite crear ResultSet desplazables en ambas direcciones y, adems, sensibles a los cambios que otros usuarios realicen sobre la base de datos.

En cuanto a resultSetConcurrency, indica si los datos son solo lectura o de lectura-escritura. Los posibles valores que pueden tomar son:

ResultSet.CONCUR_READ_ONLY Los campos son solo lectura, es el valor predeterminado. ResultSet.CONCUR_UPDATABLE Los campos son de lectura-escritura, pudindose utilizar el objeto ResultSet para realizar modificaciones sobre los mismos.

email: brujulato@yahoo.es

Pgina 199

Curso gratuito de Java en emuladores L2 Java

2012

Los ResultSet desplazables pueden utilizar, adems de los estudiados, los siguientes mtodos de la interfaz ResultSet:

void first() Desplaza el cursor hasta el primer registro. void beforeFirst() Desplaza el cursor a la posicin anterior al primer registro. void last() Desplaza el cursor hasta el ltimo registro. void afterLast() Desplaza el cursor a la posicin posterior al ltimo registro. void absolute(int pos) desplaza el cursor a la posicin indicada en pos.

email: brujulato@yahoo.es

Pgina 200

Curso gratuito de Java en emuladores L2 Java


17 Manejo de fechas y nmeros con clases

2012

El API de Java provee una larga (quizs un poco ms de la cuenta) lista de clases para ayudarte con las fechas, los nmeros y la moneda. Cuando hayas terminado esta seccin, deberas tener consolidados los fundamentos para tareas como crear una fecha, y usar objetos Date y DateFormat, convertirlo a cadena y volverlo a convertir en objetos, creando funciones de calendario, imprimir correctamente el valor formateado de la moneda y hacer todo esto para que funcione de manera correcta en cualquier pas del globo

17.1 Trabajando con fechas, nmeros y monedas Si queremos trabajar con fechas en todo el mundo (y quin no?) necesitars estar familiarizado con al menos cuatro clases de java.text y java.util. Aqu hay cuatro clases de fecha que necesitaras entender:

java.util.Date La mayora de los mtodos de esta clase se han quedado obsoletos, pero puedes usar esta clase como puente entre Calendar y DateFormat. Una instancia de Date representa una fecha y una hora en milisegundos. java.util.Calendar Esta clase tiene una gran variedad de mtodos que te ayudarn a convertir y manipular fechas y horas. Por ejemplo, si quieres aadir un mes a una fecha dada, o encontrar que da de la semana cae el 1 de Enero, los mtodos de Calendar pueden hacer casi de todo. java.text.DateFormat Esta clase se usa para formatear fechas, no slo estilos como "01/01/70" o "Enero, 1, 1970", sino que tambin formatea las fechas de forma local en todo el mundo. java.text.NumberFormat Esta clase se usa para formatear nmeros y moneda para todos los pases (Locale) del mundo. java.util.Locale Esta clase se usa conjuntamente con DateFormat y NumberFormat.

Manejando fechas y nmeros con las clases Cuando trabajas con fechas y nmeros, a menudo usars tambin clases. Es importante entender cmo funcionan las clases descritas arriba, como se usan unas con las otras. Por ejemplo, necesitars saber que si quieres hacer un formato especial para un Locale especifico, tendrs que crear un objeto Locale antes que el objeto DateFormat, porque necesitars el objeto Locale como argumento para DateFormat. La siguiente tabla es una vista rpida de los casos y soluciones usando estas clases. Esta tabla indudablemente muestra preguntas especficas sobre clases, y profundizaremos en ellas en el siguiente captulo. Una vez que hayamos visto la discusin entre los niveles de las clases, encontrars esta tabla como un resumen bastante bueno.

email: brujulato@yahoo.es

Pgina 201

Curso gratuito de Java en emuladores L2 Java

2012

17.2 La clase Date La clase Date tiene un pasado con altibajos. El diseo de su API no fue muy bueno en cuanto al manejo de internalizaciones. En su estado actual, la mayora de sus mtodos han quedado obsoletos, y para la mayora de los propsitos querrs usar Calendar en lugar de Date. La clase Date es interesante por varias razones: puedes encontrarlo en cdigo antiguo, es bastante fcil si quieres una manera rpida y sucia de obtener la fecha y la hora actual, est bien si quieres la hora universal, la cual no est afectada por zonas horarias, y finalmente, lo usars como puente temporal para formatear un objeto Calendar usando la clase DateFormat. Como hemos dicho antes, una instancia de la clase Date representa una fecha y hora. Internamente, se crea un long que guarda el tiempo transcurrido en milisegundos desde el 1 de Enero de 1970. Has intentando alguna vez imaginarte cmo es de grande el nmero? email: brujulato@yahoo.es Pgina 202

Curso gratuito de Java en emuladores L2 Java

2012

Usemos Date para averiguar la fecha de un trilln de segundos, comenzando desde el 1 de Enero de 1970:

a JVM arroja: 1st date Sat Sep 08 19:46:40 MDT 2001 Bien, para futuras referencias, recuerda que un trilln de milisegundos son 31 aos y dos tercios. Quizs la mayora de los mtodos de Date han quedado obsoletos, pero aun es aceptable el uso de getTime() y setTime(), aunque pronto veremos que es un poco doloroso. Vamos a aadir una hora a nuestra instancia d1 del ejemplo anterior:

lo que da: 1st date Sat Sep 08 19:46:40 MDT 2001 new time Sat Sep 08 20:46:40 MDT 2001 Date cuenta que setTime() y getTime() se usan en milisegundos, si quieres manipular fechas usando la clase Date, es la nica forma que hay. Mientras esto no fue demasiado doloroso, imagina lo divertido que puede ser aadir un ao a la fecha. Veremos ms tarde la clase Date, por ahora solo lo que necesitas saber es que si quieres crear una instancia de Date para crear la fecha actual, tienes que usar el constructor de Date sin argumentos:

(Nosotros estamos imaginando que si invocas ahora now.getTime(), conseguirs un numero entre uno y dos trillones.)

email: brujulato@yahoo.es

Pgina 203

Curso gratuito de Java en emuladores L2 Java


17.3 La clase Calendar

2012

Hemos visto que manipular fechas usando Date puede resultar engorroso. La clase Calendar fue diseada para manipular las fechas de manera ms sencilla. La clase Calendar tiene un milln de campos y mtodos, una vez que manejes unos pocos de ellos, el resto del trabajo funciona de forma similar. Cuando pruebas a usar Calendar, puedes darte cuenta que es una clase abstracta, no puedes decir:

Para crear una instancia de Calendar, tienes que usar el mtodo sobrecargado getInstance():

Cuando tienes una referencia como la de arriba, la variable se est refiriendo a una subclase de Calendar. No puedes saber con seguridad que subclase consigues (lo ms seguro que sea java.util.GregorianCalendar), pero eso a ti no te importa (quiero decir que no tienes que reparar cuidado en ello). Estars un API de Calendar. (Java continua distribuyndose por todo el mundo para mantener una cohesin, puedes encontrar subclases ms especficas para Locale). Bien, ahora vamos a obtener una instancia de Calendar, volvamos a nuestro ltimo ejemplo, y vamos a ver que da de la semana cae nuestro milisegundo nmero tres mil millones, y aadiremos un mes a esa fecha:

Esto nos da: 1st date Sat Sep 08 19:46:40 MDT 2001 Sunday is the first day of the week trillionth milli day of week is 7 new date Mon Oct 08 20:46:40 MDT 2001

Vamos a examinar el programa centrndonos en las 5 lneas resaltadas:

email: brujulato@yahoo.es

Pgina 204

Curso gratuito de Java en emuladores L2 Java

2012

1. Asignamos el Date d1 a la instancia Calendar c. 2. Usamos el campo SUNDAY para determinar si, nuestra JVM, considera SUNDAY como el primer da de la semana. (En algunos sitios, MONDAY es el primer da de la semana). La clase Calendar provee de campos similares para das de la semana, mes y das del mes, el da del ao, y as... etc. 3. Usamos DAY_OF_WEEK para averiguar que da de la semana cae el trillisegundo. 4. Hasta ahora hemos usado mtodos setter y getter que debera ser considerado ya algo intuitivo. Ahora vamos a usar el mtodo add() de Calendar. Es un mtodo muy poderoso que nos permite aadir y substraer unidades de tiempo apropiadas para el campo Calendar especifico. Por ejemplo:

5. Convierte el valor de c a una instancia Date. El otro mtodo de Calendar que deberas de conocer es roll(). Funciona igual que add() excepto que cuando una parte de la fecha se incrementa o decrementa, la parte ms grande de Date no ser incrementada o decrementada. Hmmm... por ejemplo:

La salida seria: new date Fri Jul 08 19:46:40 MDT 2001 Fjate en que el ao no cambi, aunque le aadimos 9 meses a Octubre. De forma similar, funciona roll() con HOUR, no cambiara la fecha, ni el mes o el ao. 17.4 La clase DateFormat Habiendo aprendido como crear fechas y manipularlas, vamos a empezar a formatearlas. Aqu se muestra un ejemplo de cmo puede formatearse una fecha de diferentes maneras:

email: brujulato@yahoo.es

Pgina 205

Curso gratuito de Java en emuladores L2 Java

2012

lo que produce:
9/8/01 7:46 PM Sep 8, 2001 9/8/01 Sep 8, 2001 September 8, 2001 Saturday, September 8, 2001

Viendo este cdigo nos damos cuenta de un par de cosas. Lo primero que salta es que DateFormat es otra clase abstracta, as que no podemos usar new para crear instancias de DateFormat. En este caso, usamos dos mtodos de la factora, getInstance() y getDateInstance(). Fjate que getDateInstance() esta sobrecargado, cuando hablemos de Locale, veremos la otra versin de getDateInstance() . Lo siguiente, es que hemos usados campos estticos de DateFormat para personalizar nuestras instancias de DateFormat. Cada una de las cuales representa un estilo de formato. En este caso parece como si getDateInstance() sin argumentos no diera el mismo estilo que la versin MEDIUM, pero eso no es una regla (veremos ms cuando hablemos sobre Locale). Finalmente usaremos el mtodo format() para crear la representacin de cadena de las versiones formateadas de Date cuando estemos trabajando con ella. El ultimo mtodo que te tendra que ser familiar es parse(). Este mtodo toma la cadena formateada en el estilo de la instancia de DateFormat, y convierte la cadena a un objeto Date. Tal como puedes imaginarte, esta operacin es arriesgada porque el mtodo parse() podra recibir la cadena mal formateada. Por esta razn, parse() arroja ParseException. El siguiente cdigo crea una instancia de Date, usa DateFormat.format() para convertirlo a cadena, y luego usa DateFormat.parse() para pasarlo de nuevo a Date:

email: brujulato@yahoo.es

Pgina 206

Curso gratuito de Java en emuladores L2 Java

2012

Lo que produce:
d1 = Sat Sep 08 19:46:40 MDT 2001 9/8/01 parsed = Sat Sep 08 00:00:00 MDT 2001

Tienes que darte cuenta de que estamos usando el estilo SHORT, hemos perdido algo de precisin cuando convertimos el Date a cadena. Esta prdida de precisin nos aparece cuando volvimos a poner la cadena como objeto Date, y entonces paso a ser de 7:46 a 00:00 midnight.

17.5 La clase Locale Antes vimos que una gran parte de este objetivo existe para evaluar tu habilidad con algunas tareas bsicas internacionales. Espera a que se acabe, Locale es tu ticket para la dominacin del mundo. Las clases DateFormat y NumberFormat (las veremos en breve) pueden usar una instancia de Locale para personalizar una salida formateada a un Locale especifico. Podras preguntarte como Java define un Locale? El API dice que un Locale es un sitio geogrfico especfico, poltico o regin cultural. Los dos constructores de Locale que necesitas entender son:

El argumento del lenguaje representa un cdigo del lenguaje ISO 639, as que por ejemplo, si quieres formatear fechas y nmeros en Wallon (un lenguaje usado algunas veces en Blgica), usaras "wa" en tu String de language. Existen sobre 500 ISO cdigos de lenguajes, incluyendo el Klingon ("thl"), aunque desafortunadamente Java no soporta el Klingon. Pensbamos decirte que memorizases todos los cdigos pero no queramos causar ms ataques cardiacos. As que no tienes que memorizar ningn ISO de los 240 que puedes encontrarte. Volvamos a cmo se pueden usar estos cdigos. Si quieres representar una aplicacin bsica italiana, todo lo que necesitas es el cdigo del lenguaje. Si, por otra parte, quieres representar el italiano usado en Suiza, tendrs que indicar que el pas es Suiza (si, el cdigo del pas es "CH"), y el lenguaje italiano tambin:

email: brujulato@yahoo.es

Pgina 207

Curso gratuito de Java en emuladores L2 Java

2012

El uso de estos dos Locale en una fecha nos dara una salida como esta: sabato 1 ottobre 2005 sabato, 1. ottobre 2005 Ahora pongmoslo todo junto en algn cdigo que cree un objeto Calendar, seleccionemos su fecha y convirtmoslo a Date. Despus tomaremos el objeto Date y lo imprimiremos usando locales de todo el mundo:

Esto nos da:


US 12/14/10 3:32 PM US full Sunday, December 14, 2010 Italy domenica 14 dicembre 2010 Portugal Domingo, 14 de Dezembro de 2010 Brazil Domingo, 14 de Dezembro de 2010 India ??????, ?? ??????, ???? Japan 2010?12?14?

email: brujulato@yahoo.es

Pgina 208

Curso gratuito de Java en emuladores L2 Java

2012

Ops... nuestra maquina no est configurada para soportar locales de la India o Japn, pero como puedes ver que un solo objeto Date puede ser formateado para trabajar con muchos locales. Hay un par de mtodos en Locale (getDispayCountry() y getDisplayLanguage()) que tendras que conocer. Estos mtodos te permiten crear cadenas que representan los Locale dados y el lenguaje en trminos de Locale por defecto y cualquier otro Locale:

da como resultado:
def loc def loc D>I Brazil Brasil Danish dansk danese

Dado que nuestra Locale de la JVM (por defecto la que es nuestra) es US, por defecto para Brasil es "Brazil", y por defecto para el dans es "Danish". En Brasil, el pas se llama "Brasil", y en Dinamarca la lengua se llama "dansk". Para terminar, solo por diversin, hemos descubierto que en Italia, la lengua danesa se la llama "danese".

Por cuenta propia he comprimido este apartado en un pequeo codigo, donde se usa a excepcion de Calendar, todo lo expuesto aqu:

email: brujulato@yahoo.es

Pgina 209

Curso gratuito de Java en emuladores L2 Java

2012

lo que arroja:
Date.getTime(): 1317143180202 Date.toString(): Tue Sep 27 19:06:20 CEST 2011 DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.LONG).format(d): martes 27 de septiembre de 2011 19:06:20 CEST Fecha tomada: Tue Sep 27 18:29:12 CEST 2011 La fecha en italiano: marted 27 settembre 2011 19.06.20 CEST

17.6 La clase NumberFormat Esta clase al igual que DateFormat, es abstracta, as que lo normal es que uses algunas de las ocho versiones de getInstance() o getCurrencyInstance() para crear un NumberFormat. No te sorprendas, el uso de esta clase es para dar formato a los nmeros o a los valores de moneda:

email: brujulato@yahoo.es

Pgina 210

Curso gratuito de Java en emuladores L2 Java

2012

Que da la salida:
123.457 123,457 $123.46 123,46 ?

Al igual que con DateFormat, NumberFormat tambin tiene su pequea prueba:

lo que arroja:
Numero en formato espaol: 123,456 Enseando el simbolo de la moneda: 123,46

No te preocupes si al igual que nosotros, no se te aparecen los smbolos de los francos, libras, rupias, baht o dragmas. No se espera que conozcas los smbolos usados para las monedas. Aqu va un pequeo cdigo que usa getMaximumFractionDigits(), setMaximumFractionDigits(), parse() y setParseIntegerOnly():

email: brujulato@yahoo.es

Pgina 211

Curso gratuito de Java en emuladores L2 Java

2012

que da como resultado:


3 123.457 123.45678 1234.567 1234

Fjate que en este caso, el numero inicial de los dgitos de la fraccin por defecto en NumberFormat es tres, y que format() redondea el valor de f1, no lo trunca. Despus nf cambia los dgitos de la fraccin, el valor es el de f1, el que se muestra. Luego, fjate que parse() debe ejecutarse en un try-catch y que el mtodo setParseIntegerOnly() toma un valor booleano y en este caso, causa llamadas subsecuentes a parse() para devolver nicamente la parte entera de la cadena formateada de nmeros de coma flotante. Tal como hemos visto, este objetivo cubre varias clases abstractas. Adems, para todas esas cases, la clave de la funcionalidad para cada instancia est establecida en el tiempo de creacin. La siguiente tabla resume los constructores o mtodos usados para crear las instancias de todas las clases que hemos visto en esta seccin:

email: brujulato@yahoo.es

Pgina 212

Curso gratuito de Java en emuladores L2 Java

2012

email: brujulato@yahoo.es

Pgina 213

Curso gratuito de Java en emuladores L2 Java


Tema 18 Parseado, tokens y formateado

2012

Vamos a empezar con otra descarga de responsabilidad: Esta pequea seccin no va a transformarte de novato a gur. En esta seccin vamos a ver tres ideas bsicas:

Buscando texto. Tienes cantidades enormes de texto, quizs leyendo un archivo o escrito en pantalla. En cualquier caso necesitas encontrar de forma fcil algn texto en el montn. Usaremos java.regex.Matcher y java.util.Scanner para encontrar texto. Buscando tokens. Tienes un fichero al que quieres sacarle los datos. Quieres transformar un pedazo de texto como "1500.00,343.77,123.4" en varios nmeros decimales. Te ensearemos a usar String.split() y java.util.Scanner para tokenizar tus datos. Formateando texto. Has conseguido un reporte y necesitas tomar en una variable el valor de 32500.000f y transformarlo a una cadena con el valor de "$32,500.00". Te presentaremos a la clase java.util.Formatter y los mtodos printf() y format().

18.1 Un tutorial de bsqueda. Tanto si ests buscando texto o tokens, hay un montn de conceptos iguales, as que vamos a empezar con algunos bsicos. No importa que lenguaje ests usando, ms tarde o ms temprano, probablemente tendrs que encarar la necesidad de buscar entre una ingente cantidad de texto usando algunas de las herramientas especficas. Las expresiones regulares son una clase en el lenguaje diseada para ayudar a los programadores en las tareas de bsqueda. Cada lenguaje provee de sus motores regex. Los motores regex buscan a travs del texto usando instrucciones que son codificadas en expresiones. Una expresin regex es como un programa muy corto o un script. Cuando invoques al motor regex, pasars un pedazo de texto que quieres procesar (en Java es normalmente una cadena o un stream) y una expresin que quieras usar para buscar a travs de los datos. Es divertido pensar que regex sea un lenguaje, y nos referiremos a el en toda la seccin. El lenguaje regex se usa para crear expresiones, y como vamos a ver en esta seccin, si hablamos de expresiones o sintaxis de expresiones, estamos hablando del lenguaje regex. Ah, un pliego de descargo ms, sabemos que pueden presentarse geniales expresiones de regex que las que vamos a presentar. Ten en cuenta que estamos creando estas expresiones solo usando una porcin del total de las expresiones que tiene regex. Gracias.

email: brujulato@yahoo.es

Pgina 214

Curso gratuito de Java en emuladores L2 Java


18.1.1 Bsquedas simples Para nuestro primer ejemplo, nos gustara buscar a travs de la siguiente fuente:

2012

todas las concurrencias de la expresin:

En todas estas discusiones asumiremos que nuestros datos empiezan con el index cero, asi que aplicando el index a nuestra cadena tenemos:

Podemos ver que tenemos dos concurrencias de la expresin ab, una empezando en la posicin cero y la otra en la posicin 4. Si enviamos el siguiente dato y la expresin a un motor regex, nos devolvera que ha encontrado concurrencias en las posiciones 0 y 4:

lo que da
0 4

No vamos a explicar el cdigo ahora mismo. En unas pocas pginas te ensearemos un montn de cdigo regex, pero primero queremos presentarte ms sintaxis sobre regex. Una vez que entiendas un poco ms de regex, los cdigos de ejemplo tendrn mas sentido. Aqu hay un ejemplo ms complicado:

Cuantas concurrencias encontramos en este caso? Bien, claramente en la posicin 0 hay una, y otra en la posicin 4. Pero y la posicin 2? En el mundo de regex, la cadena aba que empieza en la posicin 2 no ser considerada una concurrencia valida.

email: brujulato@yahoo.es

Pgina 215

Curso gratuito de Java en emuladores L2 Java


La primera regla de bsqueda de regex es que:

2012

una bsqueda comienza desde la izquierda a la derecha y una vez que encuentra un concurrencia, sus caracteres no pueden ser usados en otra concurrencia. As que en nuestro ejemplo anterior, la primera concurrencia usada en las posiciones 0, 1 y 2 no puede usarse nuevamente estas posiciones para otra concurrencia. As que la siguiente concurrencia valida se encuentra en el ndice 4. Esta es la manera normal en la que un motor regex funciona. Sin embargo, en pocas pginas veremos una excepcin a esta regla.

Este cdigo lo hice para buscar la palabra "admin" en el chat.log del servidor:

email: brujulato@yahoo.es

Pgina 216

Curso gratuito de Java en emuladores L2 Java


lo que arroja:

2012

Linea: 9435 Texto: [23 Mar 1:09:32] SHOUT [WiSe] con vosotros??? creo que les esta saliendo caro a los admins... Linea: 9475 Texto: [23 Mar 1:13:59] SHOUT [WiSe] lo de donar via sms se lo planteare a los admins ... ... ... El chat contiene 1.269.476 lineas. Tiempo empleado: 4.532 milisegundos.

Es posible que despues de leer el chat.log se os quiten las ganas de seguir pagando los costes del servidor xDDD
As que hemos encontrado un par de coincidencias, pero que pasa si queramos encontrar algo mas dinmico? Por ejemplo, que tal si queramos encontrar todas las concurrencias de nmeros hexadecimales o cdigos de distritos postales?

18.1.2 Bsqueda usando metacaracteres


Regex tiene un poderoso mecanismo para los casos descritos arriba. En el corazn de este mecanismo la idea es el uso de metacaracteres. Como un ejemplo simple, digamos que queremos buscar algunos datos en todas las concurrencias de nmeros. En regex, la siguiente expresin se usa para buscar nmeros:

Si cambiamos el programa anterior y le aplicamos la expresin \d a la siguiente cadena:

regex nos va a decir que ha encontrado dgitos en las posiciones 1, 2, 4, 6, 7 y 8. (Si quieres intentar esto en casa, necesitaras escapar el mtodo \d, o sea, dejarlo asi, \\d, veremos ms en breve). Regex provee de un rico surtido de metacaracteres que puedes encontrar descritos en la documentacin del API de java.util.regex.Pattern. No vamos a discutirlos todos ahora, slo los ms importantes:

\d un digito \s un espacio en blanco (carcter ASCII 32) \w un carcter de tipo palabra ( letras, nmeros y _ )

As que en ejemplo:

email: brujulato@yahoo.es

Pgina 217

Curso gratuito de Java en emuladores L2 Java

2012

regex devolver las posiciones 0, 2 ,4, 5, 7 y 8. El nico carcter en este listado que no encaja con la definicin de palabra son los espacios en blanco. Atencin, date cuenta en este ejemplo que hemos encerrado el listado entre comillas para indicar que no haba espacios en blanco al principio ni al final de la cadena. Tambin puedes especificar una serie de caracteres para buscar usando los corchetes y el rango de caracteres a buscar y un guin:

Adems, puedes buscar a travs de un rango de una sola vez. La siguiente expresin busca concurrencias en el rango a hasta la f, o A hasta la F, y no est combinando bsquedas fA:

As que por ejemplo:

devuelve las posiciones 0, 1, 4, 5, 6 Adems de lo descrito, tambin puedes aplicar los siguientes atributos para seleccionar y dar rango con los corchetes:

^ para negar el carcter especificado. corchetes anidados para crear una unin de selecciones. && para especificar la insercin de selecciones.

Por cuenta propia he ido a copiar unos ejemplos de dicho API:

Puedes ir a echar un vistazo clicando aqu. email: brujulato@yahoo.es Pgina 218

Curso gratuito de Java en emuladores L2 Java


18.1.3 Bsqueda usando cuantificadores

2012

Digamos que queremos crear un patrn regex para buscar hexadecimales literales. Como primer paso, tenemos que resolver el problema de los dgitos hexadecimales:

regex devolvera 6 y 11. Fjate que 0x y 0xg no son nmeros hexadecimales vlidos. Como segundo paso, pensemos en otro problema ms fcil. Qu pasa si queremos que regex encuentre concurrencias de enteros? Los enteros pueden tener uno o ms dgitos en una expresin. Hay una serie de constructores de regex llamados cuantificadores, que nos permite especificar conceptos como "uno o ms". En efecto, los cuantificadores que representan "uno o ms" es el carcter "+". Veremos el resto en breve. El otro problema que aparece es que cuando estamos buscando algo de una longitud variable, conseguir solo una posicin como valor devuelto tiene una utilidad limitada. As, adems de devolver los valores de ndice, otro poco de informacin que el motor de regex ofrece es el grupo de concurrencias completo de lo que encuentra. Vamos a cambiar la manera en la que hablamos sobre lo que devuelve regex especificando cada return o cada nueva lnea, recordando que ahora, en cada devolucin de regex vamos a volver a la posicin inicial despus del grupo concurrente:

Esta expresin se lee como "Encuentra uno o ms dgitos por columna". Lo que produce:
0 1 3 12 6 234

Puedes leer esto como "En la posicin 0 hay un entero con valor 1, luego en la posicin 3 hay un entero con valor 12, en la posicin 6 hay un entero con valor 234". Volviendo ahora con el problema hexadecimal, la ltima cosa que necesitamos saber es como especificar el uso de cuantificadores para una sola parte de la expresin. En este caso debemos tener exactamente una concurrencia de 0x 0X pero podemos encontrar ilimitadas concurrencias de dgitos hexadecimales. La siguiente expresin aade parntesis al lmite del cuantificador "+" solo para dgitos hexadecimales:

El parntesis y el mas de la expresin se lee como "Una vez que hayamos encontrado nuestro 0x o 0X, podemos encontrar una o ms concurrencias de dgitos hexadecimales." Date cuenta del cuantificador "+" al final de la expresin. Es til pensar que los cuantificadores siempre cuantifican la parte de la expresin que les precede.

email: brujulato@yahoo.es

Pgina 219

Curso gratuito de Java en emuladores L2 Java


Los otros cuantificadores que vamos a mirar son:

2012

* Ninguna o ms concurrencias ? Ninguna o una concurrencia

Digamos que tienes un texto que contiene un delimitador coma para una lista de nombres en un directorio que contiene unos proyectos muy importantes. (O por otra parte, estamos usando nuestros directorios ^^). Quieres crear una lista de todos los ficheros cuyo nombre comiencen con proj1. Puedes descubrir ficheros .txt, .java, .pdf... quin sabe? Qu clase de expresin regex podramos crear para encontrar estos ficheros? Primero echemos un vistazo a la parte del texto que podra aparecer:

Para resolver este problema vamos a usar el carcter ^ que hemos mencionado antes. El operador regex ^ nos ayudar a crear una solucin elegante y limpia a nuestro problema. El smbolo ^ indica negacin. Por ejemplo, si quieres encontrar cualquier cosa exceptuando aes, bes y ces en tus ficheros, podras poner:

As, que armados con el operador ^ y * (cero o ms) vamos a crear lo siguiente:

Si aplicamos esta expresion a la expresion listada arriba, regex devolveria:


10 proj1sched.pdf 25 proj1 37 proj1.java

La clave de esta expresin es "dame cero o ms caracteres que no usen una coma". El ultimo cuantificador del ejemplo es ? (cero o uno). Digamos que en nuestro trabajo esta vez toca buscar un fichero de texto y cualquier cosa que pueda ser local, un nmero de telfono de 7 dgitos. Digamos, arbitrariamente, que si encontramos siete dgitos en una columna o tres dgitos seguidos de un guion, o un espacio seguido de 4 dgitos, que tenemos un candidato. Aqu hay un ejemplo de nmeros de telfonos validos:

La clave para crear esta expresin es ver que necesitamos "cero o una instancia de un espacio o un guin" en medio de nuestros dgitos:

email: brujulato@yahoo.es

Pgina 220

Curso gratuito de Java en emuladores L2 Java


18.1.3.1 El punto predeterminado

2012

Adems de \s, \d y \w, tambin tenemos que conocer ".". Cuando veas este carcter en una expresin regex, quiere decir que "cualquier carcter puede servir aqu". Por ejemplo, el siguiente ejemplo:

dar como salida:


3 abc 7 a c

El punto hizo posible la concurrencia de que la b y el espacio estuvieran exentos de ser rechazados en el patrn "a lo que sea c".

18.1.3.2 Cuantificadores codiciosos


Cuando usas el *, + y ?, puedes afinarlos un poco para producir un comportamiento conocido como "codicioso", "renuente", o "posesivo". Aunque necesitars entender slo el cuantificador "codicioso", tambin vamos a explicar los cuantificadores "renuente" para servir como base de comparacin. Primero la sintaxis:

? es codicioso, ?? es renuente, para ninguna o una concurrencia. * es codicioso, *? es renuente, para ninguna o ms concurrencias. + es codicioso, +? es renuente, para una o ms concurrencias.

Qu pasa cuando tenemos el siguiente patrn?

Lo primero de todo, vamos a hacer algo un poco distinto aqu buscando caracteres con el prefijo esttico (xx) de la expresin. Nosotros pensamos que estamos diciendo algo como "Encuentra una serie de caracteres que terminen con xx". Despus te diremos que pasa, al menos queremos que consideres que hay dos posibles resultados, puedes encontrarlos? Recuerda que te dijimos antes que en general, el motor regex trabaja desde la izquierda a la derecha, y no cuenta los caracteres consumidos para la siguiente coincidencia. As, que trabajando de izquierda a derecha, podemos predecir que el motor debera de buscar los primeros 4 caracteres (0-3), encontrar xx al principio en la posicin 2, y tener su primera coincidencia. Despus debera encontrar la siguiente coincidencia en la posicin 6. Esto nos lleva a que el resultado sera:

email: brujulato@yahoo.es

Pgina 221

Curso gratuito de Java en emuladores L2 Java

2012

Un posible segundo argumento es que pedimos una serie de caracteres que terminen con xx, podramos obtener un resultado como este:
0 yyxxxyxx

A esto lo llamamos codicia. Para que la segunda respuesta sea correcta, el motor regex tendra que buscar en toda la fuente de datos para determinar el final. As, que el segundo resultado es correcto porque en el ejemplo usamos un cuantificador codicioso. El resultado que encuentra dos selecciones que pueden ser generadas usando el cuantificador renuente:

que da como resultado:


0 yyxxxyxx

Si cambiamos el patrn a:

estamos usando el cuantificador renuente *? y obtenemos:


0 yyxx 4 xyxx

Los cuantificadores codiciosos leen toda la fuente de datos, y luego comienzan el trabajo desde el final (de derecha a izquierda) hasta que encuentra la concurrencia ms a la derecha. En este punto, incluye todo desde el principio del dato fuente hasta el dato concurrente ms a la derecha.

18.1.4 Cuando los metacaracteres y las cadenas colisionan Hasta ahora hemos hablado de regex desde una perspectiva terica. Antes de ponernos a trabajar con regex tenemos que explicar un punto ms. Cuando vamos a implementar nuestro cdigo regex, es bastante comn que nuestro dato fuente o nuestras expresiones sean guardadas en cadenas. El problema es que los metacaracteres y las cadenas no se mezclan bien. Por ejemplo, digamos que queremos hacer un patrn sencillo que busque dgitos. Podramos intentar algo como esto:

Esta lnea de cdigo no compilar! El compilador ve \ y piensa que es una secuencia de escape, "quizs haya un salto de lnea". Pero no, lo que viene es una d y el compilador dice "Jams he odo de una secuencia de escape d". La manera de satisfacer al compilador es aadir otra barra antes de \d.

email: brujulato@yahoo.es

Pgina 222

Curso gratuito de Java en emuladores L2 Java

2012

La primera barra invertida le dice al compilador que lo que sea que venga luego deber ser tomado literalmente, no como una secuencia de escape. Y que pasa con el metacarcter . (punto)? Si queremos un punto como metacarcter en nuestra expresin, no hay problema, pero si ests leyendo algn dato que tenga puntos como delimitadores, se hara de la misma manera. Esto sera una serie de opciones posibles:

Un problema similar ocurre cuando un metacarcter se introduce a mano como argumento en la lnea de comandos. Si quieres pasar un \d en tu programa Java, nuestra JVM har las cosas bien si le decimos:

Pero tu JVM puede que no. Si tienes problemas para ejecutar los ejemplos, intenta probar aadiendo la segunda barra invertida en la lnea de comandos. El lenguaje Java define varias secuencias de escape, incluyendo:

\n retorno de carro \b espacio \t tabulacin

Llegados a este punto, hemos aprendido suficiente sobre el lenguaje regex para empezar a usarlo en nuestros programas Java. Comencemos mirando el uso de las expresiones regex para encontrar datos, y luego iremos a ver tpicos relacionados con el token.

18.1.5 Localizando datos por concurrencias en el patrn Una vez que sepas un poquito de regex, usar Pattern y Matcher es bastante fcil. La clase Pattern se usa para guardar la expresin regex, as que puede ser usada y reusada por instancias de la clase Matcher. La clase Matcher se usa para invocar al motor regex con la intencin de ejecutar las operaciones de concurrencias. El siguiente programa muestra a Pattern y Matcher en accin, y no es mala idea de que hagas tus propios experimentos con regex:

email: brujulato@yahoo.es

Pgina 223

Curso gratuito de Java en emuladores L2 Java

2012

Este programa usa la lnea de argumentos para representar la expresin que quieres usar en regex, y el segundo argumento es la fuente que quieres buscar. Aqu hay un ejemplo:

da la siguiente salida:
Pattern is \d\w 4 56 7 7a

Recuerda que si quieres usar esta expresin representada en una cadena, tienes que usar la doble barra (\\). Ya que a menudo tendrs caracteres especiales o espacios en blanco como parte de tus argumentes, probablemente vas a querer encerrar estos caracteres entre comillas. Echemos un vistazo a este cdigo en ms detalle. Primero de todo, fjate que no estamos usando new para crear un Pattern, si miras la API, veras que no hay constructores listados. Usars el mtodo esttico sobrecargado compile() (que toma expresiones de cadena) para crear instancias de Pattern. Necesitars saber cmo crear un Matcher, que se usa para tomar los datos fuente (Pattern.matcher()). Un mtodo importante en este programa es find(). Este es el mtodo que manipula el motor regex y ejecuta las bsquedas. El mtodo find() devuelve true si hay coincidencias, y acurdate de cul es la posicin de comienza en la bsqueda. Si find() devuelve true, puedes llamar a start() para obtener la posicin de la coincidencia, y puedes llamar a group() para obtener la cadena que representa el trozo de dato de la coincidencia. Una razn muy comn para usar regex es para hacer bsquedas y reemplazos. Deberas de saber que Matcher provee de mtodos para ejecutar reemplazos. Puedes mirar las API de appendReplacement(), appendTail(), y replaceAll() para ms detalle. La clase Matcher te permite buscar subsecuencias en el dato fuente usando un concepto llamado regin. En la vida real, las regiones pueden dotar de mayor rendimiento al programa.

email: brujulato@yahoo.es

Pgina 224

Curso gratuito de Java en emuladores L2 Java


18.1.5 Usando Scanner para hacer bsquedas

2012

Aunque la clase java.util.Scanner est pensada para tokenizar datos (lo veremos en seguida), tambin se puede usar para encontrar cosas, igual que Pattern y Matcher. Mientras que Scanner no provee de informacin sobre donde se encuentra lo que se busca, ni funciones de reemplazo, puedes usarlo para aplicar las expresiones regex al dato fuente para decirle cuantas instancias de una expresin existen en una pieza de dato dada. El siguiente programa usa la lnea de comandos como expresin regex, luego pide los datos usando System.in. Muestra un mensaje cada vez que encuentra una coincidencia:

La invocacin y el input:
java ScanIn "\d\d" input: 1b2c335f456

dan el siguiente resultado:


found 33 found 45 found null

18.2 Tokenizando Tokenizar es un proceso de coger grandes piezas de datos y separarlo en pequeas piezas, y guardar esas piececitas en variables. Probablemente lo ms comn es tokenizar un fichero para obtener el contenido del fichero y guardarlo en sitios tiles, como objetos, arrays o colecciones. Veremos las dos clases en el API que provee la tokenizacin: String (usando el mtodo split()) y Scanner, que tiene muchos mtodos muy tiles para tokenizar.

Tokens y delimitadores Cuando hablamos de tokenizar, estamos diciendo que los datos estn compuestos de dos cosas, de tokens y delimitadores. Los tokens son en efecto piezas de dato, y los delimitadores son expresiones que separan esos tokens unos de otros. Cuando la mayora de la gente piensa email: brujulato@yahoo.es Pgina 225

Curso gratuito de Java en emuladores L2 Java

2012

en delimitadores, creen que son caracteres simples, como las comas o las barras, o el espacio. De hecho, ciertamente esos son delimitadores muy comunes, pero estrictamente hablando, pueden ser mucho ms dinmicos. Como hemos puntualizando antes hace unas frases, los delimitadores pueden ser cualquier cosa que cualifique una expresin regex. Cojamos una pieza de los datos fuente y vamos a tokenizarla usando un par de delimitadores distintos: source: "ab,cd5b,6x,z4" Si decimos que nuestro delimitador es una coma, entonces nuestros cuatro tokens podran ser: ab cd5b 6x z4 Pero si en lugar de la coma, usamos \d, entonces obtendremos tres tokens: ab,cd b, x,z Recuerda que la \d significa digito. Por lo general, cuando tokenizamos un dato fuente, los delimitadores se descartan, y todo lo que nos queda son los tokens, as que en el segundo ejemplo, al definir dgitos como tokens, 5,6 y 4 no aparecen en los tokens.

18.2.1 Tokenizando con String.split() El mtodo de la clase String, split(), toma como expresin su argumento, y devuelve un array de String con los tokens. Se usa para tokenizar datos realmente pequeos. El siguiente programa usa args[0] para contener el dato fuente, y args[1] para contener el patrn regex que usa como delimitador:

Todo ocurre de una sola vez cuando el mtodo split() se invoca. La fuente String se divide en piezas, y las piezas se cargan un array de String por tokens. Todo el cdigo es para demostrar que la operacin de split se ha realizado. La siguiente invocacin:
% java SplitTest "ab5 ccc 45 @" "\d"

email: brujulato@yahoo.es

Pgina 226

Curso gratuito de Java en emuladores L2 Java


da como resultado:
count 4 >ab< > ccc < >< > @<

2012

(Nota: Recuerda que para representar \ en una cadena, necesitas usar la secuencia de escape \\. Por esto, y dependiendo de tu sistema operativo, tu segundo argumento podra ser \\d o incluso \\\\d.) Hemos puesto los tokens entre > < para mostrar los espacios en blanco. Fjate que cada dgito fue usado como delimitador, y que los dgitos contiguos creados tienen un token vaco. Un motivo ms para usar String.split() es que a menudo querrs mirar que es lo que producen los tokens, y posiblemente pares la operacin de tokenizacin prematuramente cuando tengas la cantidad de tokens que necesites. Por ejemplo, podras buscar en un fichero nmeros de telfono. Si el nmero aparece pronto en el fichero, querras detener la tokenizacin. La clase Scanner provee de cantidad de APIs para hacer tokenizaciones al vuelo.

18.2.2 Tokenizando con Scanner La clase java.util.Scanner es el Cadillac del token. Cuando necesites hacer una tokenizacin seria, no mires otra cosa que no sea Scanner, es perfecta en toda su forma. Adems de las capacidades bsicas que provee String.split(), la clase Scanner ofrece las siguientes caractersticas:

Un Scanner puede construirse usando ficheros, streams o cadenas como fuente. El tokenizado se ejecuta en un bucle del que puedes salir en cualquier momento. Los tokens pueden ser convertidos a sus tipos primitivos automticamente.

Echemos un vistazo a este programa que demuestra varios mtodos de Scanner y sus capacidades. Scanner por defecto usa como delimitador el espacio en blanco. El programa crea dos objetos Scanner, s1 que se itinera con el mtodo next() mas genrico, que devuelve cada token como una cadena, mientras s2 es analizado con varios mtodos especializados next Xxx() (donde Xxx es el tipo primitivo):

email: brujulato@yahoo.es

Pgina 227

Curso gratuito de Java en emuladores L2 Java

2012

Si este programa se invoca con:


% java ScanNext "1 true 34 hi"

produce
hits ssssibis2

Naturalmente no estamos haciendo nada con los tokens una vez que los tenemos, pero puedes ver que los tokens de s2 se convierten a sus respectivos primitivos. Como clave aqu, cabe sealar que los mtodos nombrados como hasNextXxx() evalan el siguiente token, pero no lo toman, no hacen ms que moverse al siguiente token en el dato fuente. El mtodo nextXxx() tiene dos funciones: consiguen el siguiente token y se mueven al siguiente token. La clase Scanner tiene nextXxx() (por ejemplo nextLong()) y tiene hasNextXxx() (por ejemplo hasNextDouble()) para cada tipo primitivo excepto para char. Adems, la clase Scanner usa un useDelimiter() que te permite seleccionar el delimitador, el cual puede ser cualquier expresin regex valida.

email: brujulato@yahoo.es

Pgina 228

Curso gratuito de Java en emuladores L2 Java


18.2.3 Formateando con printf() y format()

2012

Que divertido seria que las cuentas de cobro no tuvieran punto decimal, o los nmeros negativos sin parntesis! Los mtodos format() y printf() fueron aadidos a java.oi.PrintStream en Java 5. Estos dos mtodos se comportan de manera idntica, as que cualquier cosa que digamos de estos mtodos se aplicara a los dos. (Corren rumores de que Java aadi printf() solo para hacer a los programadores de C ms felices). Detrs del escenario, el mtodo format() usa la clase java.util.Formatter para hacer el trabajo duro. Puedes usar la clase Formatter directamente si quieres. La documentacin para estos argumentos puedes encontrarlas en el API de Formatter. Vamos a hacer una visita panormica de la sintaxis del formateado, que ser ms que suficiente para permitirte hacer bastantes formatos bsicos. Comencemos por parafrasear la documentacin del API para el formato de cadenas (una forma ms completa de decirlo, veamos el API de java.util.Formatter):

La cadena puede contener informacin literal no asociada a ningn argumento, y datos como argumento especfico para formatear. La clave est en determinar si ests buscando formatear algn dato, el formateo de dato siempre estar precedido del signo %. Veamos un ejemplo, y que no te entre el pnico, cubriremos todo lo que viene despus de %:

Esto da:
456 + 123

Veamos lo que ha pasado. Dentro de las comillas dobles hay un formato de cadena, luego un signo + y despus otro formato de cadena. Fjate que hemos mezclado dos literales de formato de cadena. Ahora indaguemos ms y miremos la construccin del formato de la cadena:

Los valores entre corchetes son opcionales. En otras palabras, los nicos elementos exigidos para formatear la cadena son el signo % y el carcter a convertir. En el ejemplo anterior slo los valores opciones que usamos fueron los argumentos para la indexacin. El 2$ representa el segundo argumento y el 1$ representa el primero. Puedes ver que no hay problema en intercambiar el orden de los argumentos. Despus de los argumentos, la letra d expresa la conversin del carcter (ms o menos el tipo de argumento). Aqu hay una lista de los elementos para formateo de cadena que necesitas saber: arg_index Un entero seguido por una $, indica que el argumento debera ser escrito en esta posicin.

email: brujulato@yahoo.es

Pgina 229

Curso gratuito de Java en emuladores L2 Java


flags Hay muchos marcadores disponibles, para el examen tienes que conocer:

2012

Justifica este argumento a la izquierda + Incluye un signo (+ o -) al argumento 0 Rellena el argumento con ceros , Se usa para separar los nmeros con comas (ejemplo 1,000) ( Encierra los nmeros negativos entre parntesis

width Este valor indica la cantidad de caracteres mnima para imprimir (si quieres que tus numeros encajen en columnas, usaras esto masivamente). precision Para el examen solo necesitaras esto cuando el formateo contenga un numero de coma flotante, la precisin indicia el nmero de dgitos decimales que se va a mostrar. conversion Al tipo de argumento que sera formateado. Necesitaras saber:

b boolean c char d integer f flotante s string

Veamos algunos de estos formatos de cadena en accin:

Esto da:
> (123)< >012,345< >+12345 < >false + -123<

Hemos aadido > < para ayudarnos a ver el ancho, el alineamiento y el relleno con ceros. Finalmente es importante recordar que si tienes algn error de comillado conseguirs una excepcin en tiempo de ejecucin:

email: brujulato@yahoo.es

Pgina 230

Curso gratuito de Java en emuladores L2 Java


Producir algo como:

2012

Exception in thread "main" java.util.IllegalFormatConversionException: d != java.lang.Double

email: brujulato@yahoo.es

Pgina 231

Curso gratuito de Java en emuladores L2 Java


19.1 Mtodos de Object Cualquier objeto de Java posee estos mtodos, heredados de Object.

2012

En este captulo veremos hashCode(), equals() y toString(). Sobrescribiremos estos mtodos para nuestra conveniencia, que sern resumidas en seguida.

19.1.1 El mtodo toString() Sobrescribe toString() cuando quieras que un mero mortal sea capaz de leer algo con significado sobre los objetos de tu clase. El cdigo puede llamar al mtodo toString() de tu objeto cuando quiera leer detalles tiles sobre el objeto. Por ejemplo, cuando pasas la referencia del objeto al mtodo System.out.println(), se invoca el mtodo toString() del objeto, y en este ejemplo se muestra la devolucin:

lo que devuelve un encantador y significativo:


% java HardToRead HardToRead@a47e0

La salida que obtienes cuando no sobrescribes el mtodo toString() del objeto es esta. Devuelve el nombre de la clase (al menos eso es significativo) seguido del smbolo @, seguido por un numero hexadecimal sin signo en representacin del hashCode. Intentar leer esta salida puede motivarte a sobrescribir toString() en tu clase, por ejemplo:

email: brujulato@yahoo.es

Pgina 232

Curso gratuito de Java en emuladores L2 Java

2012

Esto puede ser algo ms legible:


% java BobTest I am a Bob, but you can call me GoBobGo. My shoe size is 19

Algunas personas se dirigen de manera afectuosa a toString() como "el mtodo que derrama las entraas" porque la mayora de las implementaciones a toString() simplemente dividen el estado del objeto, en otras palabras, los valores actuales de la instancia. Esto es todo para toString(). Ahora vayamos a por equals() y hashCode().

19.1.2 Sobrescritura de equals() Has visto en capitulo anteriores el mtodo equals(), donde vimos cmo funcionaba con las clases envoltorio. Hemos visto como se comparan dos referencias a objetos usando el operador == que evala a true slo cuando ambas referencias se refieren al mismo objeto ( porque == solo comprueba los bits en la variable, y verifica si son idnticos o no). Viste que la clase String y las clases envoltorio tienen un mtodo equals() sobrescrito (heredado de la clase Object), as que puedes comparar dos objetos distintos (del mismo tipo) para ver si sus contenidos son significativos equivalentes (es decir, mismo tipo, distinto objeto). Si dos instancias Integer contienen el nmero 5, hasta ahora, ellos son iguales (equals). El hecho es que el valor 5 vive en dos objetos distintos y eso, no importa. Cuando quieras realmente saber si dos referencias son idnticas, tienes que usar ==. Pero cuando slo quieres saber si los objetos en s mismos (no sus referencias) son iguales, entonces tienes que usar equals(). En cada clase que escribes debes decidir si tiene sentido considerar dos instancias por igual. Para algunas clases, puedes decidir que dos objetos nunca podran ser iguales. Por ejemplo, imagina una clase Coche que tiene variables de instancia para cosas como modelo, ao, configuracion... realmente no quieres que tu coche de repente sea tratado como el coche de cualquiera. Tu coche es tu coche y no quieres que tu vecino Pepe lo conduzca porque, "oye! es realmente el mismo coche, el mtodo equals() dice que s". As que dos coches no deberan de ser consideradas la misma cosa. Si dos referencias se refieren a

email: brujulato@yahoo.es

Pgina 233

Curso gratuito de Java en emuladores L2 Java

2012

un Coche, tienes que saber que ambas estn hablando de un coche, no de dos coches que tienen los mismos atributos. As que puedas querer que un Coche no se use nunca, o quieras sobrescribir el mtodo equals(). Naturalmente, tienes que saber que no es el fin de la historia.

Qu pasa si no sobrescribes equals() Hay una limitacin potencial al acecho: si no sobrescribes equals(), quizs no puedas usar esos objetos como clave en un hashTable y probablemente no puedas precisar Set, as que no habr duplicados conceptuales. El mtodo equals() en la clase Object usa solo el operador == para las comparaciones, a menos que sobrescribas equals(), dos objetos son considerados iguales slo si las dos referencias apuntan al mismo objeto. Veamos lo que significa no poder usar un objeto con su hashTable como clave. Imagina que tienes un coche, un coche muy especfico (digamos, que el coche rojo de John es un Subaru, todo lo contrario al coche de Mary, que es un Mini purpura) y quieres ponerlo en un HashMap (un tipo de hashTable que veremos en este captulo), as que puedes buscar un coche en particular y drselo al objeto Persona correspondiente, que representa al propietario. As que aades la instancia del coche como clave para el HashMap (junto con el valor del objeto Persona correspondiente). Pero ahora lo que ocurre cuando quieres hacer la bsqueda. Quieres decirle a HashMap "Aqu est el coche, ahora dame a la persona que va con este coche". Pero ahora tienes un problema, a menos que aun conserves la referencia exacta del objeto que usaste como clave cuando lo aadiste a la Collection. En otras palabras, no puedes crear un coche idntico y usarlo para una bsqueda. Al final de la lnea queda esto: si quieres objetos que tu clase pueda usar como clave para un hashTable (o como elementos en cualquier estructura de datos que use la misma equivalencia para hacer bsquedas y/o obtener un objeto), entonces tienes que sobrescribir equals(), de manera que dos instancias distintas pueden ser consideradas la misma. As que como arreglamos el coche? Tienes que sobrescribir equals() de manera que tenga un nico VIN (Vehculo Identificacin Nmero) como comparacin bsica. De esa manera, puedes usar una instancia cuando quieras aadirla a la Collection, y esencialmente, recrear una instancia idntica cuando quieras hacer una bsqueda basada en la clave de los objetos. Naturalmente, la sobrescritura de equals() para Coche tambin te permite potencial para que ms de un objeto que representa un nico Coche pueda existir, lo cual puede ser no muy seguro en tu diseo. Afortunadamente, las clases String y de envoltorio funcionan bien como claves en hashTable -sobrescriben el mtodo equals(). As que ms que usar la actual instancia de Coche como clave en pares coche/dueo, podras simplemente usar un String que represente el nico identificador para el coche. De esta manera, nunca tendrs ms de una instancia representando a un coche especifico, pero puedes usar el coche, o uno de los atributos del coche como clave.

email: brujulato@yahoo.es

Pgina 234

Curso gratuito de Java en emuladores L2 Java


Implementado en mtodo equals() Digamos que decides sobrescribir equals() en tu clase. Puede parecerse a esto:

2012

Veamos este cdigo en detalle. En el mtodo main() de EqualTest, hemos creado dos instancias de Moof, pasndole el mismo valor, 8, al constructor de Moof. Ahora miremos a la clase Moof y veamos que hace constructor con el argumento, le asigna el valor de moofValue a la variable de instancia. Ahora imagina que has decidido que dos objetos Moof sean iguales si sus valores son idnticos. Sobrescribes el mtodo equals() y comparas los dos moofValues. Es simple. Pero veamos que pasa en el mtodo equals():

Lo principal de todo, tienes que seguir las reglas de la sobrescritura, y en la lnea 1 ciertamente estamos declarando una sobrescritura vlida para el mtodo equals() que heredamos de Object. En la lnea 2 es donde est la accin. Lgicamente tenemos que hacer dos cosas para crear una comparacin de igualdad valida. email: brujulato@yahoo.es Pgina 235

Curso gratuito de Java en emuladores L2 Java

2012

Lo primero es asegurarse de que el objeto que est siendo testado es del tipo correcto! Esto viene del polimorfismo, como el tipo Object, as que necesitas hacer que instanceof lo testee. Tener dos objetos de diferentes tipos de clase se considera igual, y normalmente no es una buena idea, pero eso es un problema de diseo y no vamos a meternos ah. Por otra parte, todava tienes que hacer el instanceof para asegurarte de que se puede castear el argumento del objeto a un tipo correcto al que puedas acceder por sus mtodos o variables para hacer la comparacin. Recuerda, si el objeto no pasa el test de instanceof, tendrs en tiempo de ejecucin ClassCastException. Por ejemplo:

El casteo (Moof) fallar si no pasa el test ES-UN. Segundo, tendremos cuidado al comparar los atributos (en este caso, solo el valor de moofValue). Solo el desarrollador puede decidir que hace que dos instancias sean iguales. (Para mejor rendimiento, vas a querer comprobar unos pocos nmeros de atributos.) En caso de que ((Moof)o).getMoofValue()) te cogiera por sorpresa, lo nico que hicimos fue castear el objeto de referencia, o, de otra manera, llamamos al mtodo que tiene la clase Moof, pero no Object. Recuerda, sin el casteo no podras compilar porque el compilador vera el objeto referenciado por o como un Object. Y ya que la clase Object no tiene el mtodo moofvalue(), el compilador podra graznar (trmino tcnico). Pero como dijimos antes, incluso con el casteo, el cdigo fallar en tiempo de ejecucin si el objeto referenciado por o no es algo que sea casteable a Moof. As que nunca olvides usar instanceof en primer lugar. Aqu hay otra razn por la que apreciar el atajo &&, si instanceof falla, nunca se ejecutar el cdigo del casteo, as que siempre estaremos a salvo en tiempo de ejecucin con lo siguiente:

As que ten cuidado con equals()... Whoa... no tan rpido. Si te fijas en la clase Object, la especificacin que encontrars en el API de Java vers que nosotros hacemos un contrato especificado en el mtodo equal(). Un contrato de Java es una serie de reglas que deberan obedecerse si quieres proveer de una correcta implementacin como los otros programadores esperan que lo hagas. O ponlo de otra

email: brujulato@yahoo.es

Pgina 236

Curso gratuito de Java en emuladores L2 Java

2012

manera, si no obedeces el contrato, tu cdigo podra compilar y ejecutarse, pero tu cdigo (o el de alguien ms) podra romperse en tiempo de ejecucin de alguna manera inesperada.

El contrato equals() En resumidas cuentas, Java docs dice:


Es reflexivo. Para cualquier referencia valor x, x.equals(x) debera devolver true. Es simtrico. Para cualquier referencia valor x e y, x.equals(y) debera ser true, si, y slo si y.equals(x) devuelve true. Es transitivo. Para cualquier referencia valor x, y, z, si x.equals(y) devuelve true e y.equals(z) devuelve true, entonces x.equals(z) tiene que devolver true. Es consistente. Para cualquier referencia valor x, e y, mltiples invocaciones de x.equals(y) devolvern siempre true, o devolvern siempre false, siempre y cuando no se provea de informacin en las comparaciones de igualdad del objeto modificado. Para cualquier referencia no null, el valor x.equals(null) debera devolver false.

Y an no est todo aqu. No hemos visto el mtodo hashCode(), pero equals() y hashCode() estn obligados por un contrato que especifica que si dos objetos son considerados iguales usando el mtodo equals(), entonces ellos tienen el mismo valor hashCode(). As que para estar verdaderamente seguros, tu regla de oro debera ser, que si rescribes equals(), tienes que rescribir hashCode() tambin. Ahora vamos a ver hashCode() y cmo se une al mtodo equals().

19.1.3 Sobrescritura de hashCode() Los hashCode se usan normalmente para incrementar el rendimiento de grandes colecciones de datos. El valor hashCode de un objeto se usa por algunas clases de colecciones (veremos las colecciones en este captulo). Aunque puedas pensar que esto es como la ID del objeto, no es necesariamente, nica. Las colecciones tales como HashMap y HashSet usan el valor hashCode del objeto para determinar cmo se debe almacenar el objeto en la coleccin, y el hashCode se usa otra vez para ayudar a localizar el objeto en la coleccin. Tienes que saber cules de las colecciones las usan (pero, uhm, todos tienen "hash" en el nombre as que estara bien saber algo mas). Debes ser capaz de reconocer una implementacin de hashCode() apropiada o correcta. Esto no significa legal ni siquiera eficiente. Es perfectamente legal tener un mtodo ineficiente de hashCode en tu clase, mientras no violes el contrato especificado en la clase Object (la veremos en un momento).

Entender el hashCode() Para entender que es apropiado y correcto, tenemos que echar un vistazo a como usan las colecciones los hashCode. Imagina una seria de cubos alineados en el piso de arriba. Alguien coge un trozo de papel con un nombre. Coge el nombre y calcula el numero entero usando, A es 1, B es 2 y as, y aade el email: brujulato@yahoo.es Pgina 237

Curso gratuito de Java en emuladores L2 Java

2012

valor numrico a todas las letras del nombre. El mismo nombre dado siempre dar el mismo cdigo.

No introducimos nada al azar, simplemente tenemos un algoritmo que se ejecutar siempre de la misma manera dada una entrada especfica, as que la salida ser la misma para dos entradas iguales. Todo bien hasta aqu? Ahora la manera de usar ese cdigo (al que llamaremos hashCode desde ahora) es determinar que en que cubo se va a poner el trozo de papel (imagina que cada cubo representa un cdigo distinto). Ahora imagina que alguien viene y te muestra un nombre y dice "Por favor, saque un trozo de papel que tenga este nombre". As que miras el nombre que te ensean, y ejecutas el algoritmo. El hashCode te dice en que cubo debera estar ese nombre. Seguro que te habrs dado cuenta que puede haber un fallo en este sistema. Dos nombres diferentes pueden tener el mismo valor, por ejemplo, Amy y May. Son nombres distintos con el mismo hashCode. Esto es aceptable, pero significa que cuando alguien te pida la pieza de papel de Amy, aun tienes que buscar los nombres que haya en el cubo hasta que encuentres el nombre. El hashCode te dice en que cubo est el papel, pero no te dice como localizar el nombre en el cubo.

As que para ms eficiencia, tu meta es tener los papeles distribuidos de manera igualitaria a travs de todos los cubos. Lo ideal es que tuvieras un nombre por cubo, as que cuando alguien te pregunte por el papel, slo tuvieras que realizar el clculo y coger el papel del cubo correcto (sin tener que estar mirando todos los papeles del cubo). El menos eficiente (pero funcional) generador de hashcode devolver el mismo hashcode (digamos 42) sin importar el nombre, as que todos los papeles estarn en el mismo cubo mientras los otros cubos estn vacos. Una vez en el cubo adecuado, hay que rebuscar entre todos los papeles hasta dar con el papel adecuado. Y as es como funciona, puedes pensar en no usar el hashCode, pero entonces el cubo seria an ms grande (en realidad, un nico cubo).

email: brujulato@yahoo.es

Pgina 238

Curso gratuito de Java en emuladores L2 Java

2012

Esta distribucin en cubos es una manera similar a la que el hashCode se usa en las colecciones. Cuando pones un objeto en una colecciones que usa hashCode, la coleccin usa el hashCode del objeto para decidir en qu cubo debera ir. Luego, cuando quieras coger el objeto (o, el objeto asociado al hashCode), tienes que darle a la coleccin una referencia del objeto que tiene en la coleccin. Hasta ahora, el objeto (almacenado en la coleccin, como el papel en el cubo) que estas intentando buscar se har por su hashCode. Pero... imagina que pasara si, volviendo a nuestro ejemplo de los nombres en el cubo, te muestran un nombre y calculas el cdigo basndote solo en la mitad de las letras en lugar de en todas ellas. Nunca encontraras el nombre en el cubo! Ahora puedes ver por qu si dos objetos son considerados iguales, sus hashCode deben ser iguales? De otra manera, no podras encontrar el objeto ya que el mtodo hashCode por defecto de la clase Object siempre va a otorgar un nico hashCode a cada objeto, aunque se sobrescriba equals() de tal manera que dos o ms objetos sean considerados iguales. No importa como de iguales sean esos objetos si sus hashCodes no dice que lo son. As que una vez ms: si dos objetos son iguales, sus hashcodes tienen que serlo tambin.

Implementado el hashCode() A qu diablos se parece un algoritmo de hashCode? La gente obtiene sus PhDs con el hasheo algortmico. La parte que nos concierne aqu es si respetas el contrato. Y al seguir el contrato, tienes que pensar lo que hace el mtodo equals(). Comparas atributos. La comparacin casi siempre involucra a variables de instancia (acurdate cuando vimos los dos objetos Moof y considerados iguales si sus valores moofValues eran el mismo). La implementacin de tu hashCode() debera usar las mismas variables de instancia. Por ejemplo:

Este mtodo equals() dice que dos objetos son equivalentes si ellos tienen el mismo valor x, as que los objetos con el mismo valor x tendrn el mismo hashCode.

email: brujulato@yahoo.es

Pgina 239

Curso gratuito de Java en emuladores L2 Java

2012

Normalmente, vas a ver los mtodos de hashCode en combinacin de ^-ing (XOR-ing) usado en sus variables de instancia (en otras palabras, haciendo girar sus bits), y quizs multiplicndolos por un numero primo. En cualquier caso, mientras la meta sea conseguir una amplia cantidad de cubos, el contrato, (no importa si el objeto se encuentra o no) exige solo que la igualdad del objeto tengan el mismo hashCode. No espero que puntes la eficiencia de un hashCode(), pero tienes que saber reconocer cual va a funcionar y cual no (funcionar significa "podr ser encontrado el objeto en la coleccin?"). Ahora que sabemos que dos objetos iguales tienen que tener el mismo hashCode, es lo contrario cierto? Dos objetos con idntico hashCode tienen que ser considerados igual? Piensa en ello, tener un motn de objetos que entren en el mismo cubo porque sus hashCode son idnticos, pero a menos que pasen el test de equals(), no se mostrarn en la bsqueda de la coleccin. Esto es exactamente lo que deberas conseguir con nuestro ineficiente "todo el mundo consigue el mismo hashCode". Es legal y correcto, pero extremadamente lento. As que para que un objeto sea encontrado, la bsqueda del objeto y el objeto en la coleccin deben tener idnticos hashCode y devolver true a la igualdad (equal()). As que no hay manera de sobrescribir ambos mtodos para que sea absolutamente cierto que tus objetos puedan ser usados en colecciones usando hash.

El contrato de hashCode() Ahora directo desde los fabulosos docs de API de Java para la clase Object, presentamos (redoble de tambor) el contrato de hashCode():

Si es invocado en el mismo objeto ms de una vez durante una ejecucin de una aplicacin de Java, el mtodo hashCode() debe devolver siempre el mismo entero, sin proveer informacin que modifique el resultado de equals(). Este entero no necesita permanecer constante en la ejecucin de una aplicacin a otra ejecucin de la misma aplicacin. Si dos objetos son iguales de acuerdo al mtodo equals(), entonces el mtodo hashCode() en cada uno de los dos objetos debe producir el mismo resultado de nmero entero. Si no se requiere que dos objetos sean no equals() (de acuerdo con equals de java.lang.Object), entonces la llamada a hashCode() en cada uno de los objetos debe producir un resultado distinto. Sin embargo el programador debera hacer que se produjeran distintos resultados para un objeto distinto, de manera que mejorase el rendimiento de hashtable.

Y esto quiere decir...

email: brujulato@yahoo.es

Pgina 240

Curso gratuito de Java en emuladores L2 Java

2012

Ahora veamos que ms puede hacer que un mtodo hashCode() falle. Qu pasa si incluyes una variable transient en tu hashCode()? Aunque es legal (el compilador no dir nada), bajo algunas circunstancias el objeto de la coleccin no va a ser encontrado. Como ya sabes, la serializacin guarda un objeto, as que puede ser rescatado ms tarde mediante la deserializacin. Pero peligro Will Robinson, recuerda que las variables transient no se graban cuando se serializa el objeto. Un mal escenario podra parecerse a esto:

Esto es lo que pasara usando el ejemplo anterior: 1. 2. 3. 4. 5. Le da al objeto algn estado (o sea, se asignan los valores a la variable de instancia). Pone el objeto en un HashMap usando el objeto como una clave. Graba el objeto a un fichero usando la serializacin sin alterar ninguno de sus estados. Recupera el objeto mediante la deserializacin. Usa el objeto deserializado para introducirlo al HashMap (trado a la vida en el heap).

Oops. El objeto de la coleccin y supuestamente el mismo objeto devuelto a la vida no son idnticos. El objeto con la variable transient vuelve con un valor por defecto en lugar del valor del que la variable tena cuando fue grabado (o puesto en el HashMap). As que al usar el cdigo SaveMe, si el valor de x era 9 cuando se puso en el HashMap, y ya que x se us para calcular el hashCode, cuando el valor de x cambia, el hashCode lo hace tambin. Y cuando la misma instancia de SaveMe es deserializado, entonces x == 0, no importa el valor que tuviera x cuando el objeto fue serializado. As que el nuevo clculo de hashCode da un resultado distinto y el mtodo equals() falla tambin desde el momento en que se ha usado x para calcular el hashCode.

email: brujulato@yahoo.es

Pgina 241

Curso gratuito de Java en emuladores L2 Java

2012

En ltimo lugar, las variables transient puede enredar bastante el uso de equals() y hashCode().Usa las variables no transient o, si tienen que estar marcadas como transient, no las uses para determinar la igualdad de un hashCode.

19.2 Colecciones Imagnate intentando escribir una aplicacin orientada a objetos sin usar estructuras de datos como hashTable o listas lincadas. Que haras cuando de verdad necesitases mantener un orden, digamos, todos los miembros que son fan de los Simpsons. Obviamente puedes hacerlo t mismo, Amazon.com tiene miles de libros con algoritmos que puedes comprar. Pero con la clase de agenda que tienen los programadores hoy, sera bastante doloroso de considerar. Las colecciones de Framework en Java, que se mejoraron en el 1.2 y se expandieron en el 1.4 y de nuevo en el 1.5, te dan listas, selecciones, mapas y colas para satisfacer la mayora de las necesidades de tu cdigo. Han sido probadas, testadas y ajustadas. Escoge la mejor para tu trabajo y conseguirs, por lo menos, un rendimiento razonable. Y cuando necesites algo un poco ms personalizado, las colecciones del Framework en java.util.package tiene interfaces y utilidades.

As que, que hago con una Collection? Hay operaciones bsicas que se hacen con una coleccin:

Aadir objetos a la coleccin. Borrar objetos de la coleccin. Averiguar si un objeto o grupo de objetos existe en la coleccin. Rescatar ese objeto de la coleccin (sin borrarlo). Itinerar (recorrer para los de la ESO :p) a travs de la coleccin, mirando cada elemento (un objeto) uno por uno.

Interfaces clave y clases de las colecciones en el Framework Para el examen tienes que conocer que colecciones debes elegir basndote en las exigencias del cdigo. Las APIs de las colecciones comienzan con un grupo de interfaces, pero tambin te da una gran cantidad de clases concretas. El core de las interfaces que necesitas conocer para trabajar (y para la vida en general) son siete:

El core concreto que necesitas saber para implementar las clases son 13 (hay otras, pero no vamos a verlas):

email: brujulato@yahoo.es

Pgina 242

Curso gratuito de Java en emuladores L2 Java

2012

No todas las colecciones del Framework implementan la interfaz Collection. En otras palabras, no todas las colecciones pasan el test ES-UN para Collection. Especialmente, ninguna de la clase Map hereda nada de Collection. As que SortedMap, HashTable, HashMap, TreeMap y LinkedHashMap son todas colecciones, y ninguna hereda Collection. Para hacer las cosas ms confusas, hay tres usos sobrecargados de la palabra "collection":

collection (c minscula), representa cualquier estructura de datos en la que los objetos se guardan y se itineran. Collection (C mayscula), la cual es la interfaz de en java.util.Collection, que Set, List y Queue heredan. (Correcto, heredan, no implementan. No hay implementaciones directas de Collection). Collections (C mayscula, termina en s) es la clase de java.util.Collections que contiene los mtodos estticos para el uso de las colecciones.

email: brujulato@yahoo.es

Pgina 243

Curso gratuito de Java en emuladores L2 Java


Las colecciones vienen en cuatro sabores bsicos:

2012

Lists Lista de cosas (clases que implementan List). Sets Cosa nica (clases que implementan Set). Maps Cosas con una nica ID (clases que implementan Map). Queues Cosas ordenadas por orden en la que han sido procesadas.

Este esquema representa la estructura de un List, un Set y un Map.

HashMap: el producto de salesman (Claves generados desde las IDs) Y dentro de estos refrescantes sabores existen otros cuatro sub-sabores para las colecciones.

Una implementacin en una clase puede ser unsorted y unordered, ordered y unsorted, o ordered y sorted. Pero una implementacin no puede ser nunca sorted y unordered, porque sorting especifica un tipo de orden, como vers en breve. Por ejemplo, HashSet es un unordered y unsorted, mientras que LinkedHashSet es ordered y unsorted, que ordena sus elementos en el orden que van siendo insertados. Quizs podramos ser ms explcitos con las diferencias entre sorted y ordered, pero primero vamos a discutir la idea de la iteracin.

email: brujulato@yahoo.es

Pgina 244

Curso gratuito de Java en emuladores L2 Java

2012

Cuando piensas en iteracin, podras pensar en recorrer los elementos usando un array, digamos, un for que accediera a cada elemento en orden ([0], [1], [2]...). Iterar a travs de una coleccin significa normalmente, recorrer los elementos uno despus de otro, despus del primero (de un orden establecido). Algunas veces, incluso el concepto de primer elemento es un poco extrao. En un HashTable no hay nocin de primero, segundo, tercero... En un HashTable, los elementos estn dispuestos en un orden catico dependiendo de su clave hashCode. Pero de alguna manera alguien tiene que ser el primero en ser itinerado. Pero hasta ahora lo que puedes decir, es que es completamente arbitrario y que puede cambiar aparentemente de forma aleatoria en la medida de que la coleccin vaya cambiando. Ordered Cuando una coleccin es ordered, quiere decir que puedes recorrerla en un orden especfico, no aleatorio. Una coleccin HashTable no est ordenada. Aunque una HashTable tiene un orden lgico para determinar el orden (basada en los hashCode y la implementacin de coleccin en s), quizs no querras encontrar un orden cualquiera cuando recorrieses la HashTable. Un ArrayList sin embargo mantiene el orden establecido por la indexacin de sus elementos (igual que un array). LinkedHashSet mantiene el orden establecido por insercin, as que el ltimo elemento introducido es el ltimo elemento en un LinkedHashSet (lo contrario a un ArrayList, donde puedes insertar un elemento en cualquier posicin del ndex). Finalmente, hay algunas colecciones que mantienen un orden referido al orden natural de los elementos, no son ordered, son de tipo sort. Veamos cmo funciona el orden natural de las colecciones sorted. Sorted Una coleccin sorted significa que el orden de la coleccin se determina de acuerdo a unas reglas, conocidas como orden de clasificacin. Un orden de clasificacin no tiene nada que ver con el momento en que se aade el objeto a la coleccin, o cuando fue la ltima vez a la que se tuvo acceso, o en que posicin fue aadido. El orden de clasificacin es creado en base a las propiedades del objeto. Pones el objeto en la coleccin y la coleccin averiguar en qu orden debe ponerlo, basado en su clasificacin. Una coleccin que ocupa un orden (tales como una List) no se considera realmente sorted a menos que mantenga alguna regla para clasificar los objetos. La mayora de las clasificaciones se hacen usando el llamado orden natural. Y esto que significa? Sabes cul es el orden alfabtico, la A viene primero, luego la B,... etc... Para una coleccin de objetos String, para una coleccin de objetos con nmeros Integer, el orden natural seria el 1, luego el 2, etc... Y para los objetos Foo, el orden natural seria... uhmm... no s. No existe un orden natural para ordenar Foo a menos, o hasta que el desarrollador nos de uno, a travs de una interfaz llamada Comparable que define como las instancias de una clase deben ser comparadas unas a otras (la instancia a viene despus de la b, o quizs la b antes que la a?). Si el desarrollador decide que los objetos Foo sean comparados usando el valor de alguna variable de instancia (digamos que hay una que se llama bar), entonces el orden de la coleccin ser el que marque las reglas de la clase Foo con su variable de instancia bar. Naturalmente la clase Foo podra heredar el orden natural de una superclase en lugar del que define su propio orden en algunos casos.

email: brujulato@yahoo.es

Pgina 245

Curso gratuito de Java en emuladores L2 Java

2012

Aparte de su orden natural especificado en la interfaz Comparable, tambin sera posible definir otra clase de orden con otra interfaz: Comparator. Veremos cmo usar Comparable y Comparator para crear ordenes ms tarde, en este mismo captulo. Por ahora, ocupmonos de clasificar y ordenar (incluyendo el orden natural) que no es lo mismo ordenar por insercin, por acceso o por indexacin. Ahora que sabemos cmo ordenar y clasificar, veremos cada una de las cuatro interfaces y profundizaremos en la implementacin.

List (Interface) Para una lista tiene importancia el ndice. Una cosa que las listas tienen que otras no tienen, es una serie de mtodos relacionados con el ndice. Estos mtodos claves incluyen cosas como get(int ndex), indexOf(Object o), add (int ndex, Object obj), etc... Las tres implementaciones de List ordenan sus elementos mediante su ndice. Una posicin la puedes determinar explcitamente, cambiado el valor de su ndex o aadindolo en una posicin especifica; o sin ella, en cuyo caso el objeto es aadido al final. Las tres implementaciones de List se describen en la siguiente seccin: ArrayList Imagnatelo como un array dinmico. Te da una rpida iteracin y acceso aleatorio. Para ser ms obvios, es una coleccin ordenada (por el ndice), pero no clasificada (ordered pero no sorted). Podra interesarte que desde la versin 1.4 se implement una interface de acceso aleatorio, RandomAccess, una interfaz marcada (significa que no tiene mtodos) que dice "esta lista soporta acceso aleatorio de alta velocidad (normalmente tiempo constante)". Elige esta en vez de LinkedList cuando necesites recorrer la coleccin de manera ms rpida, pero no si sueles hacer un montn de inserciones o borrados. Vector Vector es un vestigio de los tiempos ms tempranos de Java, Vector y HashTable fueron las dos colecciones originales de Java, y el resto de se fueron implementando con las versiones 1.2 y 1.4 de Java. Un Vector es prcticamente lo mismo que un ArrayList, pero los mtodos de un Vector son sincronizados, dando seguridad por hilos. Normalmente querrs usar ArrayList en lugar de Vector ya que los mtodos sincronizados desmerecen al rendimiento y podra ser que no los necesitases. Y si lo necesitases, hay mtodos en la clase Collection que pueden ayudar. El Vector es la otra clase adems de ArrayList que implementa RandomAccess. LinkedList Un LinkedList esta ordenado por posicin del ndex, como ArrayList, excepto que los elementos estn doblemente unidos el uno al otro. Este linkage proporciona nuevos mtodos (ms de los que consigues con la interface List) para aadir y borrar desde el principio o el final, lo cual facilita una fcil eleccin para implementar una pila o una cola. Acurdate, y no lo olvides, que un LinkedList puede itinerar ms lento que un ArrayList, pero es una buena eleccin cuando necesitas insertar y borrar rpidamente.

email: brujulato@yahoo.es

Pgina 246

Curso gratuito de Java en emuladores L2 Java

2012

En Java 5, la clase LinkedList ha sido mejorada al implementar la interface java.util.Queue. Asi que ahora adems, soporta los mtodos: peek(), poll() y offer(). Set (Interface) Para un Set es importante ser nico, no permite duplicados. Tu buen amigo, el mtodo equals() determina si dos objetos son idnticos (en cuyo caso, slo uno podr ir al Set). Las tres implementaciones de Set estn descritas en las siguientes secciones. HashSet Un HashSet es un Set unsorted y unordered. Usa el hashCode del objeto insertado, as que la mayor eficiencia reside en la implementacin que hagas de tu mtodo hashCode(). Usa esta clase cuando quieras una coleccin sin duplicados y que no te importe el orden cuando tengas que itinerarla. LinkedHashSet Un LinkedHashSet es una versin ordered de HashSet que mantiene un doble linkado en todos los elementos. Usa esta clase en lugar de HashSet cuando sea importante el orden al recorrer los elementos. Cuando recorres un HashSet el orden es predecible, mientras que en un LinkedHashSet, podrs recorrer los objetos en el orden que fueron insertados. TreeSet TreeSet es una de las dos colecciones sorted (la otra es TreeMap). Usa una estructura de rbol Red-Black (pero ya sabias eso), y garantiza que los elementos sern puestos en orden ascendentes de acuerdo a su orden natural. Opcionalmente puedes construir un TreeSet con un constructor que te permite dar a la coleccin tus propias reglas para el orden que debera tener (en lugar de confiar en el orden definido por la clase del elemento) usando un Comparable o Comparator.

email: brujulato@yahoo.es

Pgina 247

Curso gratuito de Java en emuladores L2 Java


Map (Interface)

2012

A un Map le importa que los identificadores de sus elementos sean nicos. Mapeas una nica clave (ID) para especificar valor, donde la clave y el valor, son naturalmente, objetos. Probablemente ests familiarizados con los mapas, ya que muchos lenguajes soportan datos de estructura tipo clave-valor o nombre-valor (Php por ejemplo). La implementacin de los mapas te permiten hacer cosas como buscar un valor basado en la clave, preguntarle a la coleccin solo valores, o preguntarle por solo las claves. Al igual que los Sets, los Maps confan en el mtodo equals() para determinar si dos claves son iguales o distintas. HashMap El HashMap te proporciona un Map unsorted y unordered. Cuando necesites un Map y no te importe el orden (cuando vayas a recorrerlo), entonces HashMap es lo correcto, los otros mapas aaden un poco ms de carga. HashMap se basa en el hashCode, al igual que HashSet, donde la eficiencia de tu hashCode mejorara el rendimiento. HashMap te permite una clave null y mltiples valores null en la coleccin. HashTable Al igual que Vector, HashTable ha existido desde tiempos inmemoriales en Java. Por diversin, no olvides anotar la inconsistencia en el nombre: HashMap vs HashTable. De cualquier manera, al igual que Vector es el opuesto sincronizado de ArrayList, HashTable es el opuesto sincronizado a HashMap. Recuerda que no sincronizas la clase, as que cuando decimos que Vector y HashTable estn sincronizados, lo que queremos decir es que los mtodos clave de estas clases estn sincronizados. Otra diferencia es que, mientras HashMap te permite tener valores nulos y una clave nula, en un HashTable no te permite almacenar nada nulo. LinkedHashMap Como su opuesto es Set (LinkedHashSet), LinkedHashMap mantiene la coleccin en orden (o, opcionalmente, en orden de acceso). Quizs es algo ms lento que HashMap cuando aades y borras elementos, pero la iteracin es ms rpida con LinkedHashMap. TreeMap Como probablemente ya has adivinado, un TreeMap es un Map ordenado. Y ya sabes que por defecto, esto significa ordenado en su orden natural de elementos. Al igual que TreeSet, TreeMap te permite organizar el orden a tu gusto, usando Comparable o Comparator, que especifica la manera en que los elementos deberan ser ordenados. Queue (Interface) Una cola (Queue) est diseada para almacenar una lista de "para-hacer" (to-do), o cosas para ser procesadas de la misma manera. Aunque otras rdenes son posibles, las colas normalmente van como FIFO (first-in, first-out, primero en entrar, primero en salir). Las colas soportan todos los mtodos estndar de las colecciones y tambin aaden mtodos para aadir elementos, quitar elementos y revisar la cola de elementos.

email: brujulato@yahoo.es

Pgina 248

Curso gratuito de Java en emuladores L2 Java


PriorityQueue

2012

Esta clase es nueva en Java 5. Desde que se mejor la clase LinkedList implementando la interfaz Queue, las colas pueden ser manejadas con LinkedList. El propsito de PriorityQueue es que los elementos sean ordenados por su orden natural (por lo que los elementos son almacenados primeros sern accedidos primero) o segn lo establecido en Comparator. En cualquier caso, el orden de los elementos representa una prioridad relativa. La siguiente tabla resume 11 de las 13 colecciones que tienes que aprender. Los Array y las Collection vienen enseguida!

19.2.1 ArrayList La clase java.util.ArrayList es una de las ms usadas de todas las colecciones de Framework. Es como un array con vitaminas. Algunas de las ventajas de ArrayList son:

Crece dinmicamente Provee de inserciones ms poderosas y mejores mecanismos de bsqueda que los arrays.

Echemos un vistazo al uso de ArrayList conteniendo cadenas. Una clave del diseo de las colecciones del Framework es que proveen de una rica funcionalidad al nivel de las principales interfaces: List, Set y Map. En la prctica, normalmente querrs instanciar un ArrayList de forma polifrmica:

Escrito en Java 5 (y en adelante) seria:

Esta clase de declaracin sigue el principio de "cdigo a interface", y crea el uso de genricos. Hablaremos bastante del uso de genricos en este captulo, pero por ahora, solo hay que saber que al igual que en Java 5 (y en adelante), la sintaxis <String> es la manera de declarar el tipo de coleccin. Antes de Java 5, no haba manera de especificar el tipo de una coleccin, y

email: brujulato@yahoo.es

Pgina 249

Curso gratuito de Java en emuladores L2 Java

2012

cuando cubramos los genricos, hablaremos de la implicacin al mezclar Java 5 (tipificado) con las versiones anteriores a Java 5 (sin tipificar) en las colecciones. En muchas maneras, ArrayList<String> es muy similar a String[], declara un contenedor que slo puede almacenar cadenas, pero es ms poderoso que un String[]. Echemos un vistazo a algunas de las capacidades que un ArrayList tiene:

lo que produce:
3 false true 2

Hay un motn de movimiento en este pequeo programa. Fjate que cuando declaramos el ArrayList no le dimos un tamao. Luego le preguntamos por su tamao, le pudimos preguntar si tena contenido especfico, tambin le borramos el objeto de en medio, y le volvimos a preguntar su tamao.

Autoboxing con colecciones En general, las colecciones almacenan objetos, pero no primitivos. Antes de Java 5, era muy comn el uso de clases de envoltorio para proveer a las clases de una manera de meter un primitivo en una coleccin. Antes de Java 5, tenas que envolver el primitivo a mano y despus podas meterlo en la coleccin. Con Java 5, los primitivos todava estn envueltos, pero el autoboxing lo hace por ti.

a partir de Java 5, podemos hacer:

email: brujulato@yahoo.es

Pgina 250

Curso gratuito de Java en emuladores L2 Java

2012

En este ejemplo, hemos puesto un objeto Integer a myInts (no un primitivo int), slo que el autoboxing envolvi el primitivo por nosotros.

Clasificando las colecciones y los arrays Las colecciones y los arrays pueden ser ordenados y hacer bsquedas usando mtodos del API.

Ordenando colecciones Comencemos por algo simple como ordenar un ArrayList de cadenas alfabticamente. Podra ser ms fcil? Bien, esperaremos un rato mientras vas y buscas el mtodo sort() de ArrayList. Lo tienes? Naturalmente, ArrayList no te da ninguna forma de ordenar su contenido, pero java.util.Collection si:

lo que da:
unsorted [Denver, Boulder, Vail, Aspen, Telluride] sorted [Aspen, Boulder, Denver, Telluride, Vail]

En la lnea #1 se declara un ArrayList de cadena, y en la lnea #2 se ordena el ArrayList alfabticamente. Hablaremos ms sobre la clase Collection a lo largo de la clase Array en otra seccin, por ahora nos ocuparemos de ordenar cosas. Imaginemos que estamos construyendo una aplicacin de un automatismo para una casa. Hoy estamos centrados en el home cinema, y ms especficamente, en el control del DVD. Ya tenemos el I/O Software para leer y escribir datos entre el dvdInfo.txt y las instancias de la clase DVDInfo. Aqu est la clave del aspecto de la clase:

email: brujulato@yahoo.es

Pgina 251

Curso gratuito de Java en emuladores L2 Java

2012

Aqui estan los datos de dvdinfo.txt:


Donnie Darko/sci-fi/Gyllenhall, Jake Raiders of the Lost Ark/action/Ford, Harrison 2001/sci-fi/?? Caddy Shack/comedy/Murray, Bill Star Wars/sci-fi/Ford, Harrison Lost in Translation/comedy/Murray, Bill Patriot Games/action/Ford, Harrison

En nuestra aplicacin, queremos crear una instancia de DVDInfo para cada lnea de datos que leemos del fichero dvdinfo.txt. Por ejemplo, queremos parsear la lnea datos (te acuerdas de String.split()?) y llenar las tres instancias de DVDInfo. Finalmente, queremos poner todas las instancias de DVDInfo en unArrayList. Imagina que el mtodo populateList() hace todo esto. Aqu hay una pequea pieza de cdigo de nuestra aplicacin:

Puedes obtener algo como esto:


[Donnie Darko sci-fi Gyllenhall, Jake , Raiders of the Lost Ark action Ford, Harrison , 2001 sci-fi ?? , Caddy Shack comedy Murray, Bill , Star Wars sci-fi Ford, Harrison , Lost in Translation comedy Murray, Bill , Patriot Games action Ford, Harrison ]

Nota: Hemos sobrescrito el mtodo de DVDInfo.toString(), as que cuando hemos invocado println() en el ArrayList, se invoca toString() para cada instancia. Ahora que tenemos ArrayList populado, ordenmoslo:

Oppps! ahora hemos obtenido esto:

email: brujulato@yahoo.es

Pgina 252

Curso gratuito de Java en emuladores L2 Java


TestDVD.java:13: cannot find symbol symbol : method sort(java.util.ArrayList) location: class java.util.Collections Collections.sort(dvdlist);

2012

Que ha pasado aqu? Sabemos que la clase Collection tiene el mtodo sort(), este error hace pensar que Collection no tiene un mtodo sort() que pueda tomar un dvdlist. Lo que significa que debe haber algo equivocado en el argumento que le hemos pasado (dvdinfo).

Si ya te lo has imaginado, adivinamos que lo hiciste sin la ayuda del error mostrado arriba... Como demonios ordenas las instancias de DVDInfo? Por qu pueden ordenarse las cadenas? Cuando miras el API de Collection.sort(), la primera reaccin puede ser pnico. Tranquilo, nuevamente los genricos te ayudarn con este mtodo que parece estar enloquecido. Si has ledo la descripcin del mtodo sort() con un argumento, veras que sort() toma como argumento una List, y que el objeto List debe implementar una interfaz llamada Comparable. Esto nos dice que String implementa Comparable, y esa es la razn de por qu pudimos ordenar una lista de cadenas usando el mtodo Collection.sort().

La interfaz Comparable La interfaz Comparable se usa en las colecciones con el mtodo Collection.sort() y java.utils.Array.sort() para ordenar listas y arrays de objetos respectivamente. Para implementar Comparable, una clase debe implementar un solo mtodo, compareTo(). Un ejemplo de invocacin de compareTo():

El mtodo compareTo() devuelve un int con las siguientes caractersticas:

negativo Si esteObjeto < otroObjeto zero Si esteObjeto == otroObjeto positivo Si esteObjeto > otroObjeto

El mtodo usa compareTo() para determinar como la lista o el objeto array debe ser ordenado. Al implementar compareTo() en tus clases, puedes usar cualquier criterio que se te ocurra para ordenar las instancias de tu clase. Volviendo al ejemplo de DVDInfo, nosotros vamos a coger la manera fcil y vamos a usar la implementacin de compareTo() de la clase String:

email: brujulato@yahoo.es

Pgina 253

Curso gratuito de Java en emuladores L2 Java

2012

En la lnea #1 declaramos que DVDInfo implementa Comparable, de esta manera los objetos DVDInfo podrn ser comparados con otros objetos DVDInfo. En la lnea #2 implementamos compareTo() para comparar los dos objetos ttulo del objeto DVDInfo. Ya que sabemos que los ttulos son cadenas, y que las cadenas implementan Comparable, esta es una manera sencilla de ordenar los objetos DVDInfo, por el ttulo. Antes de que vinieran los genricos en Java 5, tendramos que haber implementado Comparable de esta manera:

Esto es todava legal, pero puedes ver que es una manera dolorosa y arriesgada, ya que tienes que hacer un casteo, y necesitas verificar que el casteo no va a fallar antes de que sea ejecutado.

Ahora cuando invocamos Collections.sort(dvdlist); obtenemos:


[2001 sci-fi ?? , Caddy Shack comedy Murray, Bill , Donnie Darko sci-fi Gyllenhall, Jake , Lost in Translation comedy Murray, Bill , Patriot Games action Ford, Harrison , Raiders of the Lost Ark action Ford, Harrison , Star Wars sci-fi Ford, Harrison ]

Yeehaaa! Nuestro ArrayList ha ordenado esto por ttulo. Naturalmente, si queremos que nuestra automatizacin sea lo ms, probablemente querremos ordenar las colecciones de DVD de diferentes formas. Al implementar el mtodo compareTo() pudimos ordenar. Lo hicimos en

email: brujulato@yahoo.es

Pgina 254

Curso gratuito de Java en emuladores L2 Java

2012

una clase, pero como hacemos para que nuestras clases se ordenen de una manera especfica a compareTo()? Buena pregunta. Afortunadamente, la respuesta viene ahora.

Ordenando usando Comparator Mientras estabas mirando el mtodo Collections.sort() podras haberte dado cuenta de que es una versin sobrecargada de sort() que toma un List, y algo llamado Comparator. La interfaz Comparator te da la capacidad de ordenar una coleccin de un millar de maneras distintas. La otra cosa que hace la interfaz Comparator es que te permite ordenar las instancias de cualquier clase, incluso las clases que no puedes modificar, al contrario que Comparable, que te obliga a cambiar las instancias de las clases que quieres ordenar. La interface Comparator es tambin muy fcil de implementar, tiene slo un mtodo, compare(). Aqu hay una pequea clase que puede ser usada para ordenar una List de instancias de DVDInfo, por gnero.

El mtodo Comparator.compare() devuelve un int cuyo valor de retorno es el mismo que el de Comparable.compareTo(). En este caso estamos cogiendo ventaja al pedirle a compareTo() que haga el trabajo de comparacin por nosotros. Aqu hay un programa que nos permite testear ambos cdigos Comparable y nuestro nuevo cdigo Comparator:

Ya habias visto las dos primeras salidas, esta es la tercera:


[Patriot Games action Ford, Harrison , Raiders of the Lost Ark action Ford, Harrison

email: brujulato@yahoo.es

Pgina 255

Curso gratuito de Java en emuladores L2 Java


, , , , , ] Caddy Shack comedy Murray, Bill Lost in Translation comedy Murray, Bill 2001 sci-fi ?? Donnie Darko sci-fi Gyllenhall, Jake Star Wars sci-fi Ford, Harrison

2012

Ya que Comparable y Comparator son similares, podrs confundirte. Por ejemplo, se te puede pasar por la cabeza que implementar el mtodo compareTo() en la interface Comparator. Memoriza la siguiente tabla para conocer las diferencias entre estas dos interfaces:

Para acabar con este apartado, voy a mostrar un cdigo creado a partir de lo visto hasta aqu. Se trata de una agenda con nombres y nmeros donde se exponen tres maneras de ordenar la agenda. La primera manera, es la natural, el orden creado por indexacin. La segunda manera, es el orden creado por el orden alfabtico de los nombres. La tercera manera, es el orden creado por los nmeros, que por supuesto, son ficticios, pero sirven para la demostracin. Aqu el cdigo:

email: brujulato@yahoo.es

Pgina 256

Curso gratuito de Java en emuladores L2 Java

2012

y aqu el resultado obtenido:


Orden natural: [Pepe 112.233, Juan 908.978, Vicente 645.720, Bartolo 6.543.599]

email: brujulato@yahoo.es

Pgina 257

Curso gratuito de Java en emuladores L2 Java

2012

Orden alfabetico: [Bartolo 6.543.599, Juan 908.978, Pepe 112.233, Vicente 645.720] Ordenado por numeros: [Pepe 112.233, Vicente 645.720, Juan 908.978, Bartolo 6.543.599]

Te acuerdas de format()? lo he usado para aadir el punto a los nmeros, de esta manera se puede ver mejor su valor.

19.2.2 Arrays Hemos estado usando java.util.Collections para ordenar colecciones, ahora veamos el uso de java.util.Arrays para ordenar arrays. La buena noticia es que ordenar objetos con arrays es como hacerlo con Collections. El mtodo Arrays.sort() esta sobrescrito de la misma manera que lo est en Collections.sort().

Arrays.sort(arrayToSort) Arrays.sort(arrayToSort,Comparator)

Adems el mtodo Arrays.sort() esta sobrecargado sobre un milln de veces al proveer de un par de mtodos para cada tipo de primitivo. El mtodo Arrays.sort() ordena los primitivos siempre en su orden natural. No caigas en la trampa de intentar ordenar un array primitivo usando un Comparator. Finalmente, recuerda que el mtodo sort() para las colecciones y para los arrays son mtodos estticos, y que alteran los objetos que contiene en lugar de devolver un tipo de objeto ordenado.

19.3 Bsqueda en Arrays y Collections La clase Collections y la clase Arrays tienen mtodos que te permiten buscar un elemento especfico. Cuando buscamos a travs de las colecciones o de los arrays, se aplican las siguientes reglas:

Las bsquedas se usan usando el mtodo binarySearch(). Las bsquedas con xito devuelven un int que corresponde al ndex del elemento encontrado. Las bsquedas malogradas devuelven un int que representa el ndice de punto de insercin. El punto de insercin es el lugar en la collection/array donde el elemento sera insertado para ocupar un sitio apropiado en el orden que le corresponde. As que los valores positivos y el valor 0 indica una bsqueda exitosa, el mtodo binarySearch() usa nmeros negativos para indicar el punto de insercin. Ya que 0 es un resultado valido para una bsqueda, la primera posicin de una insercin es -1. As que si el punto de insercin de bsqueda de un elemento es 2, el punto de insercin seria -3. La bsqueda en una collection/array se hacen despus de que los elementos hayan sido ordenados. Si intentas hacer una bsqueda en un array o una coleccin que no ha sido ordenado, los resultados de la bsqueda no son predecibles. email: brujulato@yahoo.es Pgina 258

Curso gratuito de Java en emuladores L2 Java

2012

Si la collection/array donde quieres hacer la bsqueda ha sido ordenado siguiendo su orden natural, la bsqueda se har en ese orden. Esto implica que no enva un argumento Comparator para el mtodo binarySearch(). Si la collection/array donde haces la bsqueda fue ordenado usando Comparator, la bsqueda se har usando el mismo Comparator, la cual es pasado como segundo argumento al mtodo binarySearch(). Recuerda que los Comparator no pueden ser usados para bsqueda de primitivos en los arrays.

Echemos un vistazo al cdigo ejemplo que ejecuta el mtodo binarySearch():

Lo que produce:
four one three two one = 1 now reverse sort two three one four one = -1 one = 2

Que ha pasado: Lnea 1: Ordena el array alfabticamente (el orden natural). Lnea 2: Busca la localizacin del elemento "one", que est el segundo. Lnea 3: Crea una instancia Comparator. En la siguiente lnea se reordena el array usando el Comparator. Lnea 4: Intenta buscar el array. No pasamos el parmetro rs al mtodo binarySearch() que usamos en el array, as que obtenemos una respuesta (indefinida) incorrecta. Lnea 5: Nueva bsqueda, pasando el parmetro rs a binarySearch(). Esta vez obtenemos la respuesta correcta, 2. Lnea 6: Definimos el Comparator, es correcto anidarlo en una clase. Lnea 7: Al cambiar el uso de los argumentos en la invocacin de compareTo(), obtenemos el orden invertido.

email: brujulato@yahoo.es

Pgina 259

Curso gratuito de Java en emuladores L2 Java


Resumen de mtodos para Arrays y Collections.

2012

Aviso: la sintaxis T[] ser explicada ms tarde en este captulo, por ahora piensa que no se trata de un array de primitivos.

Convirtiendo Arrays a lista de Arrays Hay un par de mtodos que te permiten convertir Arrays a List, y List a Arrays. Las clases List y Set tienen mtodos toArray(), y la clase Arrays tiene un mtodo asList(). El mtodo asList() copia un array dentro de una List. El API dice, "Devuelve un lista de un array especificado". Cuando usas el mtodo asList(), el array y la List se unen. Cuando actualizas uno de ellos, el otro se actualiza automticamente. Veamos un ejemplo:

Esto da:
size 4 idx2 three one five three six

email: brujulato@yahoo.es

Pgina 260

Curso gratuito de Java en emuladores L2 Java


sl[1] five

2012

Fjate que cuando imprimimos el estado final del array y la List, ambas, la lista y el array, han actualizado los cambios. Esto tendrs que tenerlo siempre presente. Ahora veamos el mtodo toArray(). No hay nada ms divertido que el mtodo toArray(), viene en dos sabores, uno que devuelve un objeto nuevo, y el otro que usa el array de envo como array de destino:

email: brujulato@yahoo.es

Pgina 261

Curso gratuito de Java en emuladores L2 Java


19.4 Listas

2012

Recuerda que las listas se usan normalmente para almacenar cosas en alguna clase de orden. Puedes usar una LinkedList para crear una cola. Puedes usar un ArrayList para almacenar las localizaciones en orden. Fjate que en ambos ejemplos es perfectamente razonable asumir que podra haber un duplicado. Adems, las listas te permiten sobrescribir manualmente el orden de los elementos aadiendo o quitando elementos. Antes de Java 5 y sin la actualizacin de for, los elementos se examinaban uno a uno, con el uso de un Iterator. Todava encontraras Iterator en el cdigo de Java. Un Iterator es un objeto que se asocia a una coleccin. Te permite recorrer la coleccin paso a paso. Los dos mtodos de Iterator que necesitas saber son:

boolean hasNext() Devuelve true si al menos queda un elemento ms en la coleccin para ser recorrido. Invocar a hasNext() no significa que pases al siguiente elemento de la coleccin. object next() Este mtodo devuelve el siguiente objeto de la coleccin y te pasa al siguiente elemento de la lista.

Echemos un vistazo a como se usa un List con un Iterator:

esto produce
aiko clover magnolia

email: brujulato@yahoo.es

Pgina 262

Curso gratuito de Java en emuladores L2 Java


size 3 get1 clover aiko 0 oa aiko oa clover

2012

Primero de todo, hemos usado sintaxis genrica para crear el Iterator (un Iterator de tipo Dog). Por eso, cuando hemos usado el mtodo next(), no tuvimos que castear el objeto devuelto por next() a Dog. Pudimos haber declarado el Iterator de esta manera:

Pero entonces tendramos que haber hecho esto:

El resto del cdigo demuestra el uso de size(), get(), indexOf() y toArray(). No deberan haber sorpresas con estos mtodos. En breve listaremos todos los mtodos de List, Set y Map que deberan ser familiares. Como ltima advertencia, recuerda que List es una interface!

19.5 Set Recuerda que los Set se usan cuando no quieres que haya duplicados en tu coleccin. Si intentas aadir un elemento a un Set donde ya existe un elemento igual, el duplicado no ser aadido y el mtodo add() devolver false. Recuerda HashSet tiende a ser ms rpido porque, como dijimos antes, usa hashCode. Tambin puedes crear un TreeSet, el cual te permite ordenar los elementos. Debes tener precaucin cuando uses un TreeSet (estamos a punto de explicar el motivo):

Si insertas la siguiente lnea de cdigo, email: brujulato@yahoo.es Pgina 263

Curso gratuito de Java en emuladores L2 Java

2012

obtendrs una salida como esta:


true true true false true a java.lang.Object@e09713 42 b

Es importante que sepas que el orden de los objetos imprimidos en el segundo bucle for no son predecibles: HashSet y LinkedHashSet no te garantizan ningn orden. Tambin, tienes que saber que la cuarta invocacin de add() falla, ya que se intenta insertar una entrada duplicada (una cadena con el valor "a") en el Set. Si insertas esta lnea de cdigo

conseguirs algo como esto:


Exception in thread "main" java.lang.ClassCastException: java. lang.String at java.lang.Integer.compareTo(Integer.java:35) at java.util.TreeMap.compare(TreeMap.java:1093) at java.util.TreeMap.put(TreeMap.java:465) at java.util.TreeSet.add(TreeSet.java:210)

Este problema aparece cuando quieres que una coleccin sea ordenada, sus elementos deben ser mutuamente comparables. Recuerda que a menos que se especifique otra cosa, los objetos de diferentes tipos, no son mutuamente comparables.

19.6 Map Recuerda que cuando usas una clase que implemente Map, cualquier clase que uses como parte de la clave de Map, debe sobrescribir los mtodos hashCode() y equals(). Solo tienes que sobrescribirlos si ests interesado en rescatarlos ms tarde de tu mapa. Realmente es legal usar una clase que no sobrescriba equals() y hashCode() como clave en tu mapa. El cdigo compilar y se ejecutar, tan solo pasar que no podrs encontrar nada. Ejemplo de uso de HashMap:

email: brujulato@yahoo.es

Pgina 264

Curso gratuito de Java en emuladores L2 Java

2012

lo que devuelve:
Dog@1c DOG CAT key Dog key null 5

Revisemos la salida. El primer valor recuperado es un objeto Dog (tu valor variar). El segundo valor recuperado es una enumeracin (DOG). El tercer valor es una String, fjate que la clave fue un valor de enumeracin. Cuestin sorpresa, cual es la implicacin del hecho de que fuera posible usar una enumeracin como clave? La implicacin reside en que la enumeracin sobrescribe equals() y hashCode(). Y si miras el API de java.lang.Enum, veras que en efecto, estos mtodos han sido sobrescritos. La cuarta salida es un String. El punto importante de esto es que la clave usada para sacar el String fue un objeto Dog. La quinta salida es un null. El punto importante aqu es que el mtodo get() fall al buscar el objeto Cat que insertamos antes. (La ultima lnea de la salida

email: brujulato@yahoo.es

Pgina 265

Curso gratuito de Java en emuladores L2 Java

2012

confirma que ciertamente, la clave/valor 5 usa una instancia de Dog como clave, cuando se usa una instancia de Cat como es que la clave falla?) Es fcil de ver que Dog sobrescribe equals() y hashCode() mientras que Cat no lo hace. Echemos un vistazo rpido a los hashCode. Usbamos un hashCode increblemente fcil en la clase Dog, el hashCode de Dog era la longitud del nombre. As que en este ejemplo, el hashCode era 4. Comparemos los dos mtodos hashCode:

Hora de otra pregunta sorpresa: son estos dos mtodos de hashCode legales? sern capaces de encontrar el objeto en el mapa? Cual ser ms rpido? La respuesta para la 1a pregunta es s y s. Ninguno de estos hashCode son muy eficientes (de hecho ambos son increblemente ineficientes), pero ambos son vlidos y harn su trabajo. La respuesta a la ltima pregunta es que el hashCode primero ser algo ms rpido que el segundo. En general, la frmula ideal sera la que crease un nico hashCode, logrando la mayor velocidad y eficiencia. El primer hashCode crear un hashCode basado en la longitud del nombre, por ejemplo Pepe dar uno distinto a Antonio. La segunda frmula siempre produce el mismo, as que ser ms lento que el primero. Nuestro ltimo tpico de este mapa es lo que ocurre cuando un objeto usado como clave tiene su valor cambiado. Si aadimos dos lneas de cdigo al final de Maptest.main(),

conseguiramos algo como esto:


Dog@4 DOG CAT key Dog key null 5 null

Antes encontramos al Dog, pero ahora no. Eso es porque la variable usada para crear el hashCode, cambio el valor del nombre y claro, el valor del hashCode. Como quid final para los hashCode, determinar la salida para las siguientes lneas de cdigo si fueron aadidas al final del MapTest.main():

email: brujulato@yahoo.es

Pgina 266

Curso gratuito de Java en emuladores L2 Java

2012

Recuerda que el hashCode() es igual a la longitud del nombre. Cuando estudias este problema, tienes que tener en cuenta lo que necesitas para obtener el objeto que buscas: 1. Usar el mtodo hashCode() para encontrar el cubo adecuado. 2. Usar el mtodo equals() para encontrar el objeto en el cubo. En primer lugar, la llamada a get(), el hashCode es 8 (magnolia) y debera ser 6, (clover), as que la obtencin falla en el paso 1 y obtendremos un null. En el segundo get(), el hashCode en ambos es 6, as que la obtencin no da error. Una vez que tenemos el cubo correcto, se invoca el mtodo equals(), y como el mtodo equals() de Dog compara nombres, funciona, y la salida es la clave de Dog. En la tercera invocacin de get(), el test de hashCode funciona, pero el test de equals() falla porque arthur no es igual que clover. En breve mostraremos unas tablas que resumen los mtodos que deberas conocer.

19.7 Priority Queue La ultima coleccin que necesitas aprender es PriorityQueue. Al contrario que las estructuras bsicas de cola, que son por defecto, primero en entrar, primero en salir, un PriorityQueue ordena sus elementos usando la prioridad definida por el usuario. La prioridad puede ser tan simple como el orden natural (por ejemplo, la entrada de 1 podra tener la prioridad ms alta frente al 2). Adems, PriorityQueue puede ser ordenado usando Comparator, lo que te permite definir el orden que quieras. Las colas tienen algunos mtodos que no existen en otras colecciones: peek(), poll() y offer().

email: brujulato@yahoo.es

Pgina 267

Curso gratuito de Java en emuladores L2 Java

2012

lo que arroja:
1 3 5 6 7 8 9 size 7 peek 9 size 7 poll 9 size 6 8 7 6 5 3 1 null

Miremos en detalle el cdigo. El primer bucle for recorre el array ia, y usa el mtodo offer() para aadir elementos al PriorityQueue pq1. El segundo bucle recorre pq1 usando el mtodo poll(), que devuelve la entrada ms alta en pq1 y elimina la entrada de la cola. Date cuenta que los elementos son devueltos en orden de prioridad (en este caso, el orden natural). Luego creamos un Comparator que ordena los elementos al contrario de su orden natural. Usamos este Comparator para construir un segundo PriorityQueue llamado pq2, y lo cargamos en el mismo array que usamos antes. Finalmente comprobamos el tamao de pq2 antes y despus de la llamada a peek() y poll(). Esto confirma que peek() devuelve el elemento ms alto de la cola sin eliminarlo, y poll()devuelve el elemento de la cola y lo elimina. Finalmente, revisamos los elementos que quedan en la cola.

email: brujulato@yahoo.es

Pgina 268

Curso gratuito de Java en emuladores L2 Java


Resumen de mtodos para List, Set, Map y Queue

2012

Para estas cuatro interfaces hemos cubierto todos los trucos de los mtodos. La siguiente tabla muestra un resumen de los mtodos de List, Set, y Map de los que deberas guardar cuidado.

19.8 Tipos de genricos Los Arrays en Java siempre son de tipo seguro, un array declarado como tipo String (String []) no puede aceptar Integer (o int), Dogs o cualquier cosa que no sea String. Pero recuerda que antes de Java 5 no haba sintaxis para declarar el tipo de coleccin. Para hacer un ArrayList de String, tenas que hacer:

o el polimrfico equivalente:

No haba sintaxis a la izquierda para especificar que myList tomara String y slo String. Y sin manera de especificar el tipo de ArrayList, el compilador no te poda obligar a poner los elementos de tipo especficos en la lista. Al igual que en Java 5, podemos usar genricos, y no solo sirven para hacer las colecciones seguras, sino que adems la mayora de los

email: brujulato@yahoo.es

Pgina 269

Curso gratuito de Java en emuladores L2 Java

2012

desarrolladores usan genricos. As que los genricos no son solo para las colecciones, piensa que las colecciones son la razn principal para aadir genricos al lenguaje. No fue una decisin fcil, no tuvo una calurosa bienvenida. Ya que todos estaban felices con el tipo seguro, los genricos vinieron con un montn de equipaje. La mayora nunca los veras o no querrs verlos, pero hay muchas cosas que pueden sorprendernos con rapidez. Cubriremos los ms comunes que muy probablemente usars en tu propio cdigo, y tambin los problemas que pueden surgir. El desafo ms grande para Sun aparte de aadir genricos al lenguaje (y la razn principal de por qu les llevo tanto tiempo) fue como lidiar con elegancia el cdigo construido sin los genricos. Los ingenieros de Java obviamente no queran que nadie pudiera quebrar su cdigo, as que tuvieron que encontrar una manera para que las clases de Java de ambos tipos, genricos y tipos no seguros, pudiesen trabajar juntos. Su solucin no es una lista amiga, pero te permite el uso de los viejos cdigos no genricos, y tambin el uso de cdigo genrico que permite el uso de cdigo no genrico. Pero date cuenta que dijimos "permite", y no "permite sin problemas". Mientras en Java 5 se te permite integrar genrico con no genrico, las consecuencias pueden ser desastrosas, y desafortunadamente, la mayora de los desastres ocurren en tiempo de ejecucin, no en tiempo de compilacin. Afortunadamente, la mayora de compiladores generaran advertencias para decirte que estas usando colecciones no seguras (no quiere decir que sean no genricas). Vamos a cubrir los genricos y no genricos, y espero que entiendas y veas el problema que puede venir del uso de mezclar genricos y no genricos al mismo tiempo. Y como algunos de los tpicos de este manual, se podra llenar un libro completo si realmente quieres cubrir todos los detalles de los genricos, pero mi propsito, cubre ms de lo que los desarrolladores necesitan usar.

19.8.1 Las colecciones al estilo "Legacy" Aqu hay un ejemplo de un ArrayList que tena la intencin de almacenar String. (Decimos intencin porque es todo lo que haba, buenas intenciones, para asegurarse de que el ArrayList solo contena objetos String).

Una coleccin no genrica poda almacenar cualquier clase de objeto! Una coleccin no genrica es feliz almacenando cualquier cosa que no sea un primitivo. Esto significa que cualquier programador tendra que ser bastante... cuidadoso. No haba garantas de que la coleccin fuese programada de manera amigable para que fuese dirigida a un solo tipo de elemento. Solamos usar el compilador para pararnos, por ejemplo, al asignar

email: brujulato@yahoo.es

Pgina 270

Curso gratuito de Java en emuladores L2 Java

2012

un int a una referencia booleana o de cadena, saltaba la advertencia, pero con las colecciones era todo "venga! adentro! Barra libre!" Ya que las colecciones podan almacenar cualquier cosa, los mtodos que usaban estas colecciones solo podan tener un tipo de retorno, java.lang.Object. Lo que quiere decir que la cadena devuelta haba que castearla:

Y ya que no podas garantizar que lo que vena era un String (ya que se te permite almacenar cualquier cosa en la lista), el casteo fallaba en tiempo de ejecucin. As que los genricos tomaron parte en el asunto, forzando a las colecciones a aceptar solo un tipo:

Perfecto. Eso es exactamente lo que queremos. Al usar sintaxis de genricos, lo hicimos al poner entre ngulos String (<String>), le estamos diciendo al compilador que esta coleccin solo almacenara objetos String. El tipo se especifica dentro de los ngulos, a esto se le llama tipo de parmetro, tipo parametrizado, o naturalmente, como siempre le hemos llamado, tipo. En este captulo nos referiremos a ambos de las dos maneras. As que ahora que puedes poner una garanta a lo que entra, tambin puedes poner una garanta a lo que sale, y eso quiere decir que puedes deshacerte del casteo cuando saques algo de la coleccin. En lugar de:

donde no se garantiza que el objeto sea un String, podemos hacer:

El compilador ya sabe que myList solo contiene cosas que pueden ser referenciadas a variables String, as que ahora no necesitas castear. Hasta ahora, todo parece bastante simple. Y con el nuevo for mejorado, puedes recorrer la variable con la garanta de encontrar siempre un String.

email: brujulato@yahoo.es

Pgina 271

Curso gratuito de Java en emuladores L2 Java

2012

Y naturalmente puedes declarar un tipo de parmetro para un argumento del mtodo, que tome el argumento de forma segura:

El mtodo no compilara si lo cambiamos como:

El tipo de devolucin obviamente solo puede ser declarado como tipo seguro tambin:

El compilador parar cualquier retorno no compatible con un Set<Dog> (aunque lo que es y no es compatible va a ser muy interesante en un minuto). Y ya que el compilador garantiza que solo un tipo seguro Dog es lo que va a ser devuelto, esas llamadas a los mtodos no necesitan castearse al Set:

Antes de Java 5, el cdigo no genrico, getDogList() sera:

y una llamada podra necesitar un casteo:

El casteo del ejemplo se aplica a lo que viene del mtodo get de Set, no estamos casteando lo que devuelve getDogList(), que es un Set. Pero qu hay del beneficio de una coleccin completamente heterognea? En otras palabras, que pasa si quisieras meter genricos en un ArrayList que fuese genrico?

email: brujulato@yahoo.es

Pgina 272

Curso gratuito de Java en emuladores L2 Java


es casi idntico a:

2012

Declarando una lista con el tipo de parmetro Object, hace que la coleccin funcione en casi de la misma manera que el original anterior a Java 5, puedes poner cualquier clase de coleccin y colecciones de tipo <Object> es prcticamente lo mismo, pero la mayora de las veces, las diferencias no importan. Oh, si slo esto fuera el final de la historia... pero an quedan unos pocos truquillos pendientes con los argumentos de los mtodos, polimorfismo, y la integracin de genricos y no genricos en el cdigo. Solo estamos calentando motores.

19.8.2 Genricos y el cdigo Legacy La cosa ms fcil que necesitas saber de los genricos para el examen es saber cmo actualizar cdigo no genrico a genrico. Simplemente tienes que aadir los ngulos justo despus del tipo de coleccin y entre los ngulos poner el nombre del tipo, a ambos lado de la igualdad (esto significa que los argumentos devolvern tipos tambin). Una lista anterior a Java 5 que solo almacenaba objetos de tipo Integer:

se convierte en:

y una lista que almacena solo String:

se convierte en

Fcil. Y si hay cdigo que antes sola usar la versin no genrica y ejecutaba un casteo para evitar romper el cdigo:

email: brujulato@yahoo.es

Pgina 273

Curso gratuito de Java en emuladores L2 Java


19.8.3 Mezclando colecciones genricas y no genricas

2012

Aqu empieza lo interesante, imagina que tenemos un ArrayList de tipo Integer, y lo pasamos a un mtodo de una clase cuyo cdigo no tiene acceso. Esto funcionar?

El cdigo ms viejo, no genrico, que queremos usar:

Si, y funciona perfectamente. Puedes mezclar cdigo genrico con el viejo no genrico, y todo el mundo es feliz. En el ejemplo anterior, el mtodo legacy addAll() asume (confa? espera?) que la lista pasada es ciertamente restringida a Integer, incluso cuando el cdigo fue escrito, no haba garantas. Depende del cuidado del programador. Ya que el mtodo addAll() no estaba haciendo nada excepto pillar Integer (usando casteo) de la lista y accediendo a su valor, no haba problemas. En ese ejemplo no haba riesgo para el invocador del cdigo, pero el cdigo legacy podra haber estallado si la lista pasada hubiera contenido cualquier cosa que no fuera un Integer (causara una ClassCastException). Ahora imagina que invocas al mtodo legacy que no solo lee un valor, sino que adems aade algo al ArrayList. Funcionar?

email: brujulato@yahoo.es

Pgina 274

Curso gratuito de Java en emuladores L2 Java

2012

Seguro, este cdigo funciona. Compila y se ejecuta. Si, tristemente, lo hace. Ambos compilan y se ejecutan. No hay excepciones en tiempo de ejecucin. Aun, alguien podra haber metido una String en el supuesto tipo seguro, en este ArrayList de tipo <Integer>. Como puede ser? Recuerda, el antiguo legacy permite poner cualquier cosa (excepto primitivos) en una coleccin. Y para darle soporte al cdigo legacy, Java 5 te permite mezclar en tu cdigo nuevo, con el viejo. Lo ltimo que quera hacer Sun era pedirle a millones de desarrolladores que modificaran su cdigo. As que, el compilador de Java 5 esta forzado a permitirte compilar nuevos tipos de cdigo seguro con mtodos de clases antiguas que toman tipos de colecciones no seguras como argumento y quien sabe que ms. Sin embargo, que Java 5 permita compilarlo no quiere decir que est de acuerdo. El hecho es que el compilador te advertir de que estas creando un alto riesgo al enviar tu ArrayList<Integer> a un mtodo que puede tener maneras de insertar cualquier cosa, incluso Dogs. Cuando haces una llamada al mtodo addAll() en el ejemplo anterior, no inserta cualquier cosa en la lista (solo aade un valor en la coleccin), as que no hay riesgo de que el invocador vaya a alterar la lista de alguna manera. El compilador funciona y corre perfectamente. Pero en la segunda versin, el mtodo legacy insert() que aade una String, el compilador genera advertencias:
javac TestBadLegacy.java Note: TestBadLegacy.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.

Recuerda que las advertencias del compilador no se consideran errores. El compilador genera una clase perfectamente vlida, pero en otras palabras te estaba diciendo "Espero seriamente que sepas lo que estas haciendo porque este viejo cdigo no respeta (o incluso no sabe) que tu tipo <Integer> puede ser violado en tu precioso ArrayList<Integer>". De vuelta a nuestro ejemplo con el cdigo legacy que no inserta, que se te grabe en mente que ambas versiones del mtodo insert() (ese que aade un Integer y el otro que aade un String) el compilador dar advertencias. El compilador no sabe si el mtodo insert() est aadiendo enteros o cadenas. La razn de que el compilador produzca advertencias es porque el mtodo email: brujulato@yahoo.es Pgina 275

Curso gratuito de Java en emuladores L2 Java

2012

est aadiendo algo a la coleccin. En otras palabras, el compilador sabe que hay un riesgo de que puedan entrar cosas equivocadas a la coleccin, y el invocador piensa que la coleccin es de tipo seguro. As que hasta ahora, hemos mirado como el compilador genera advertencias si parece que hay posibilidades de que el tipo seguro sea vulnerado por cdigo antiguo, no seguro. Pero una de las preguntas ms frecuentes de los desarrolladores es "de acuerdo, compila, pero porque se ejecuta?". Porque el cdigo que inserta cosas equivocadas dentro de mi lista funciona en tiempo de ejecucin? En otras palabras, por qu la JVM permite que viejo cdigo meta una String dentro de mi lista <Integer> sin problemas? No hay excepciones, nada. Slo un escenario en silencio con total violacin de mi tipo seguro que no puede ser descubierto hasta el peor momento. Hay una gran verdad que tienes que saber para entender porque se ejecuta sin problemas, la JVM no tiene idea de lo que tu ArrayList debera almacenar. Escribir la informacin no tiene salida en tiempo de ejecucin. Todo tu cdigo genrico es estrictamente para el compilador. Aunque el proceso se llama "tipo borrador", el compilador verifica todo tu cdigo genrico y transcribe la informacin a bytecode. En tiempo de ejecucin, todo el cdigo sobre colecciones, tanto el legacy como el genrico, parecen exactamente iguales. Ninguno de tus tipos existe en tiempo de ejecucin. En otras palabras, aunque tu escribieras:

es lo mismo que sino tipeas. Cuando se ejecuta la clase en bytecode por la JVM, lo que realmente hace es:

El compilador incluso crea el casteo por ti, el casteo que tenas que hacer antes de Java 5. Piensa que los genricos slo te protegen en tiempo de compilacin. El compilador usa los genricos para asegurarse de que tu cdigo no contiene algo equivocado y quiebre tu coleccin de tipo seguro. Pero esta proteccin no existe en tiempo de ejecucin. Esto difiere un poco de los Arrays, que te dan proteccin en tiempo de compilacin y ejecucin. Por qu hicieron los genricos de esta manera? Por qu no hay tipos de informacin en tiempo de ejecucin? Para darle soporte al cdigo legacy. En tiempo de ejecucin, las colecciones son colecciones, al igual que en sus primeros das. Lo que ganas al usar genricos es proteccin en tiempo de compilacin que te garantiza que no vas a poner nada equivocado en una coleccin tipada como segura, y tambin elimina la necesidad de castear cuando obtienes algo de las colecciones. La verdad es que no necesitas proteccin en tiempo de ejecucin... hasta que empiezas a mezclar genricos con legacy, como hicimos en el ejemplo anterior. Entonces es cuando puedes esperar desastres en tiempo de ejecucin. El nico consejo que tenemos es que prestes atencin a las advertencias del compilador:

email: brujulato@yahoo.es

Pgina 276

Curso gratuito de Java en emuladores L2 Java


javac TestBadLegacy.java Note: TestBadLegacy.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.

2012

Estas advertencias no fueron muy descriptivas, pero la segunda nota sugiere que compiles con -Xlint:unchecked. Si lo haces, conseguiras algo como:
javac -Xlint:unchecked TestBadLegacy.java TestBadLegacy.java:17: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.List list.add(new String("42")); ^ 1 warning

Cuando compilas con -Xlint:unchecked, el compilador de muestra exactamente que mtodo podra estar haciendo algo peligroso. En este ejemplo el argumento de la lista no fue declarada con un tipo, el compilador lo trata como cdigo legacy y no asume riesgos para el mtodo. Tienes que saber reconocer cuando estas compilando cdigo que producir advertencias, pero que an compila. Y cualquier cdigo que compile (incluso con advertencias) se ejecutar. No se capturaran violaciones de tipo en tiempo de ejecucin hasta que esas violaciones enreden tu cdigo de alguna manera. En otras palabras, la accin de aadir una cadena a una lista de enteros, no fallar en tiempo de ejecucin hasta que se intente tratar la cadena como si fuese un entero. Por ejemplo, imagina que quieres que tu cdigo saque algo de ArrayList, supuestamente <Integer> y un viejo cdigo pone una cadena dentro. Compila (con advertencias). Se ejecuta... o al menos el cdigo que actualmente aade la cadena dentro de la lista. Pero cuando tomas la cadena, que supuestamente era un entero fuera de la lista, e intentas asignarle la cadena a una variable de tipo integer, o un mtodo lo usa como argumento, estas muerto. Mantn en tu mente, que el problema de poner cosas equivocadas en las colecciones no muestran errores. Solo se muestran ms tarde, cuando lo que intentas usar no caza con el tipo que se esperaba. En los viejos das de Java (antes de Java 5), siempre se asuma que podas obtener cosas equivocadas en las colecciones (todas eran de tipo no seguro), as que tomaban medidas defensivas para en el cdigo. El problema de mezclar genricos y legacy, es que no esperas que esta cosas puedan pasar. Solo recuerda que en el momento que tu coleccin es de tipo seguro se mezcla con cdigo legacy, tu proteccin se desvanece.

19.9 Polimrficos y genricos Las colecciones genricas te dan los mismos beneficios de tipo seguridad que lo que tienes con los arrays, pero hay diferencias cruciales que te pueden coger por sorpresa. La mayora tienen que ver con el polimorfismo. Ya has visto que el polimorfismo se aplica a la base del tipo de la coleccin:

email: brujulato@yahoo.es

Pgina 277

Curso gratuito de Java en emuladores L2 Java

2012

En otras palabras, podemos asignar un ArrayList a una referencia List, porque List es un supertipo de ArrayList. No hay nada de especial aqu, esta asignacin polifrmica funciona de la manera que siempre lo hace Java, no importa de que genrica se trate. Pero que pasa aqu?

Parmonos a pensar un minuto... No... esto no funciona. Hay una regla muy simple aqu, el tipo de declaracin debe ser el mismo tipo que le pasas al objeto. Si declaras List<Foo>, cuando asignes una referencia debe ser genrica del tipo <Foo>, no del subtipo de <Foo>. Tampoco un supertipo de <Foo>. Slo <Foo>. Estas asignaciones estn mal:

pero estos estn bien:

Hasta ahora bien. Solo tienes que mantener el tipo de genrico en la referencia y el tipo de genrico en el objeto para que su referencia sea idntica. En otras palabras, el polimorfismo se aplica solo a la base del tipo. Y por base, queremos decir el tipo de coleccin en si mismo, que puede ser personalizado con un tipo. En este cdigo,

List y ArrayList son el tipo de base y JButton es el tipo genrico. As que un ArrayList puede asignarse a una List, pero una coleccin de <JButton> no puede asignarse a una referencia <Object>, aunque JButton es subtipo de Object. La parte que parece afectar a los desarrolladores es esta, la de trabajar con arrays, donde se te permite hacer esto,

email: brujulato@yahoo.es

Pgina 278

Curso gratuito de Java en emuladores L2 Java

2012

significa que tambin se puede hacer esto:

pero no esto:

Por qu existen estas reglas de tipificar los arrays de diferente manera que las reglas para los genricos? Lo sabremos en un minuto. Por ahora, solo concntrate en que en el polimorfismo, los genricos no funcionan como los arrays.

Mtodos genricos Sino estabas familiarizado con los genricos, podras haberte sentido muy incmodo con las implicaciones de asignaciones polifrmicas para tipos genricos. Y por qu no deberas sentirte incomodo? Uno de los grandes beneficios del polimorfismo es que puedes declarar, digamos, un argumento de un mtodo para un tipo particular y en tiempo de ejecucin tener la posibilidad de tener un argumento que se refiera a un subtipo, incluyendo aquellos de los que nunca has odo hablar cuando escribiste el mtodo con el argumento del supertipo. Por ejemplo, imagina un clsico (simplificado) ejemplo de polimorfismo de una clase veterinario (AnimalDoctor) con un mtodo checkup(). Hasta ahora todo bien, tienes tres supertipos de Animal, Dog, Cat y Bird, cada cual implementa el mtodo abstracto checkup() de Animal:

email: brujulato@yahoo.es

Pgina 279

Curso gratuito de Java en emuladores L2 Java

2012

Olvidando las colecciones/array por un momento, solo imagina que la clase AnimalDoctor necesita el cdigo que tiene la clase Animal para invocar el mtodo checkup(). Intentar sobrecargar la clase AnimalDoctor con checkup() para cada clase de animal es ridculo, y obviamente no es extensible. Tendras que cambiar la clase AnimalDoctor cada vez que alguien aadiera un subtipo de Animal. As que en la clase AnimalDoctor, probablemente tendras un mtodo polimrfico:

Y naturalmente, queremos que AnimalDoctor tenga cdigo que pueda tomar arrays de Dog, Cat, o Bird para cuando el veterinario venga con perros, gatos o pjaros kennel. Otra vez, no queremos sobrecargar mtodos con arrays para cada subtipo de animal en potencia, as que usamos el polimorfismo en la clase AnimalDoctor:

Aqu se muestra el ejemplo completo con un test de array polimrficos que toman cualquier tipo de array de animal (Dog[], Cat[], Bird[]).

email: brujulato@yahoo.es

Pgina 280

Curso gratuito de Java en emuladores L2 Java

2012

Esto funciona perfectamente, naturalmente (lo sabemos, lo sabemos, no es nada nuevo). Pero aqu est el por qu hemos trado esto, esta aproximacin no funciona de la misma manera con las colecciones de tipo seguro! En otras palabras, un mtodo que toma, digamos un ArrayList no aceptar en la coleccin subtipos de Animal! Lo que significa que ArrayList no puede pasar como mtodo a un argumento de ArrayList , aunque sabemos que funciona perfectamente con los viejos arrays planos. Obviamente esta diferencia entre arrays y ArrayList es consistente con las reglas de asignacin del polimorfismo que ya hemos visto, el hecho es que no puedes asignar un objeto de tipo ArrayList a una lista. Pero esto es donde realmente empiezas a sentir el dolor de la distincin entre arrays tipados y las colecciones tipadas. Sabemos que no funcionar, pero intentemos cambiar el cdigo de AnimalDoctor usando arrays genricos en su lugar: email: brujulato@yahoo.es Pgina 281

Curso gratuito de Java en emuladores L2 Java

2012

Y que pasa?
javac AnimalDoctorGeneric.java AnimalDoctorGeneric.java:51: checkAnimals(java.util. ArrayList<Animal>) in AnimalDoctorGeneric cannot be applied to (java.util.List<Dog>) doc.checkAnimals(dogs); ^ AnimalDoctorGeneric.java:52: checkAnimals(java.util. ArrayList<Animal>) in AnimalDoctorGeneric cannot be applied to (java.util.List<Cat>) doc.checkAnimals(cats); ^ AnimalDoctorGeneric.java:53: checkAnimals(java.util. ArrayList<Animal>) in AnimalDoctorGeneric cannot be applied to (java.util.List<Bird>) doc.checkAnimals(birds); ^ 3 errors

El compilador nos para con errores, no con advertencias. Simplemente, no puedes asignar a un ArrayList de Animal, subtipos de Animal (<Dog>, <Cat>,<Bird> ). Esto es uno de los grandes hndicaps para los programadores que estn familiarizados con arrays, donde el mismo escenario (Animal[] puede referirse a sus subtipos) funciona como esperas. As que tenemos dos problemas reales. 1. Por qu no funciona? 2. Como lo solucionas? Seguro que ya estas empezando a odiarnos y tambin a los ingenieros de Sun si te decimos que no hay manera de hacerlo, que has aceptado escribir en este cdigo tan inflexible que

email: brujulato@yahoo.es

Pgina 282

Curso gratuito de Java en emuladores L2 Java

2012

intenta anticiparse y codificar en mtodos sobrecargados para cada tipo especfico. Afortunadamente, hay una manera de hacerlo. Pero primero de todo, por qu no funciona si sirve con los arrays? Por qu no le pasas un ArrayList<Dog> al mtodo con un argumento ArrayList<Animal>? Bien por llegar hasta aqu, pero primero volvamos por un minuto y consideremos este escenario perfectamente legal:

Parte de los beneficios de declarar un array usando un subtipo ms abstracto es que el array puede contener objetos de muchos subtipos, y puedes manipular el array asumiendo que puede hacer todo lo que tiene la interface Animal (en otras palabras, todo en el array puede responder a la llamada definida en la clase Animal). As que, usamos polimorfismos no para los objetos que apuntan al array, sino para los que el array almacena ahora mismo, en este caso, cualquier subtipo de Animal. Puedes hacer lo mismo con los genricos:

As que esta parte funciona en genricos y colecciones, podemos aadir un subtipo dentro del array o coleccin del supertipo declarado. Puedes aadir Dog, Cat a un array Animal (Animal[]) o una coleccin Animal (ArrayList Animal[]). Y con los arrays, esto se aplica a lo que pasa dentro de un mtodo:

As que si esto es verdad, y si puedes poner Dog dentro de un ArrayList<Animal>, entonces, por qu no puedes usar la misma clase de mtodo en el escenario? Por qu no puedes hacer esto?

Verdaderamente, se puede hacer esto bajo ciertas condiciones. El cdigo de arriba compilar perfectamente, SI, lo que le pasas al mtodo es tambin un ArrayList<Animal>. Esta es la parte donde difiere de los arrays, porque en la versin del array, puedes pasar un Dog[] al mtodo que toma Animal[]. email: brujulato@yahoo.es Pgina 283

Curso gratuito de Java en emuladores L2 Java

2012

La nica cosa que puedes pasar a un mtodo como argumento de un ArrayList<Animal> es otro ArrayList<Animal>! (Asumiendo que no estas intentando pasar un subtipo de ArrayList, ya que la base puede ser polimrfica). La pregunta est todava en el aire, por qu est mal? Y por qu est mal para un ArrayList pero no para los arrays? Por qu no puedo pasar un ArrayList<Dog> como argumento de ArrayList<Animal>? El problema es que es peligroso, si estas usando arrays o genricos. El compilador y JVM se comportan de distinta manera para los arrays que para las colecciones. La razn de porque es peligroso pasar una coleccin (array o ArrayList) de un subtipo a un mtodo que toma colecciones de un supertipo, es porque puedes aadir algo. Y quiere decir que puedes aadir cosas equivocadas! Esto es probablemente realmente obvio, pero (y para reforzar) vayamos a ver algunos escenarios. El primero es simple:

No hay problema. Hemos pasado un Dog[] al mtodo, y hemos aadido un Dog al array (lo cual est permitido ya que el mtodo tiene como parmetro el tipo Animal[], lo que significa que puede almacenar cualquier subtipo de Animal). Pero qu pasa si cambiamos la llamada del cdigo:

y el mtodo original se queda como estaba:

El compilador sabe que es perfectamente vlido aadir un Dog a Animal[], ya que Dog puede ser asignado a una referencia Animal. El problema es, cuando le pasas a un array de Animal un subtipo (Cat, Dog, o Bird), el compilador eso no lo sabe. El compilador no se da cuenta que en el head hay un array de tipo Cat[], no de tipo Animal[], y que estas intentando aadir un Dog. Lo nico que sabe el compilador, es, que ests pasando un array de tipo Animal, as que no tiene manera de reconocer el problema.

email: brujulato@yahoo.es

Pgina 284

Curso gratuito de Java en emuladores L2 Java

2012

Este es el escenario del cual estamos intentando prevenirte, no importa si es un array o un ArrayList. La diferencia es, que el compilador te permite llegar ms lejos con los arrays, pero no con las colecciones genricas. La razn de que el compilador no te deje pasar un ArrayList<Dog> a un mtodo que toma un ArrayList<Animal>, es porque dentro del mtodo, cuyo parmetro es <Animal>, puedes meter cualquier tipo de animales en el. No hay manera de que el compilador no te permita poner perros donde deberan ir gatos, ya que la referencia Animal te permite acoger cualquier tipo de animal. An quedan un par de preguntas... como salvas la situacin y porque demonios el compilador te permite ponerte en riesgo usando arrays y no con los ArrayList (o cualquier otra coleccin genrica)? La razn de que puedes llegar ms lejos compilando con arrays es porque hay una excepcin en tiempo de ejecucin (ArrayStoreException) que previene el error de insertar tipos de objetos en un array. Si envas un perro al mtodo que toma animales, y solo aades perros (incluyendo sus subtipos) en el array referenciado por Animal, no hay problema. Pero si intentas aadir un gato que casualmente es un array de tipo perro, te devolver la excepcin. Pero no hay excepciones equivalentes en los genricos, porque es un tipo de "borrador". En otras palabras, en tiempo de ejecucin, JVM conoce los tipos de los arrays, pero no conoce los tipos de las colecciones. Toda la informacin sobre los tipos de informacin se borra durante la compilacin, as que cuando la JVM consigue el cdigo, no hay manera de reconocer el desastre que sera poner un gato en un ArrayList de perros, y al revs (se convierte en el problema que tenas cuando usabas el cdigo legacy, el cdigo no seguro). As que ahora mismo, el cdigo valido es:

Hasta ahora, la una cosa que le pasas a addAnimals(List<Animal>) es un ArrayList<Animal>, el compilador est encantado sabiendo que cualquier subtipo que tu aadas ser valido (siempre puedes aadir un Dog a la coleccin, yada, yada, yada...). Pero si intentas invocar addAnimal() con el argumento de otro tipo de ArrayList, el compilador te frenar, ya que en tiempo de ejecucin, la JVM no sabra lo que est pasando, y no podra lanzar ninguna excepcin. email: brujulato@yahoo.es Pgina 285

Curso gratuito de Java en emuladores L2 Java

2012

Por ejemplo, este cdigo que cambia el tipo genrico <Dog>, pero sin cambiar el mtodo addAnimal(), no compilara:

el compilador dira algo como:


javac AnimalDoctorGeneric.java AnimalDoctorGeneric.java:49: addAnimal(java.util.List<Animal>) in AnimalDoctorGeneric cannot be applied to (java.util. List<Dog>) doc.addAnimal(animals); ^ 1 error

Fjate en el mensaje, es virtualmente idntico al mismo que conseguiras al intentar usar cualquier mtodo con el argumento equivocado. Te est diciendo que no puedes invocar addAnimal(List<Animal>) usando la referencia declarada como List<Dog>. (A mi entender, personalmente, lo que estoy viendo es que no se puede insertar algo no genrico a un genrico). Es el tipo de referencia, y no el tipo de objeto lo que importa, pero recuerda, el tipo genrico de un objeto es siempre el mismo del genrico declarado en la referencia. List<Dog> puede referirse solo a las colecciones que son subtipos de List, no las que fueron instanciadas con el tipo genrico <Dog>. De nuevo, recordar que una vez dentro del mtodo addAnimals(), todo lo que importa es el tipo de parmetro, en este caso, List<Animal>. Lo hemos cambiado de ArrayList a List para mantener nuestra "base", el tipo de polimorfismo es ms claro. De vuelta a la pregunta clave, como salvamos esto? Si el problema es solo el riesgo de aadir cosas equivocadas a la coleccin, que hay sobre el mtodo checkup() que usamos en la coleccin pasado como solo lectura? En otras palabras, que hay de un mtodo que invoque los mtodos de Animal en cada clase de coleccin, que funcionar sin importar que clase de subtipo de ArrayList vaya a ser pasado? Y esa es la clave! El mtodo add() es el problema, as que necesitamos una manera de decirle al compilador "Hey! Estoy usando una coleccin slo para invocar los mtodos de los elementos, y prometo no aadir nada a la coleccin". Y hay un mecanismo para decirle al compilador que puedes tomar cualquier subtipo de genrico del tipo declarado en el argumento porque no vas a poner nada en la coleccin. Y ese mecanismo es el comodn <?>. email: brujulato@yahoo.es Pgina 286

Curso gratuito de Java en emuladores L2 Java


El mtodo cambiar de:

2012

Al decir <? extends Animal> estamos diciendo "Puedo asignar cualquier coleccin que sea subtipo de List y asignarle el tipo <Animal> o cualquier cosa que herede Animal. Y si, prometo no aadir nada a la coleccin." (La historia es un poco ms larga, pero la cortaremos aqu). As que naturalmente el mtodo addAnimal() no compilar incluso con el comodn, porque el mtodo an est aadiendo algo:

Vas a obtener un error bastante raro que se parecer a esto:


javac AnimalDoctorGeneric.java AnimalDoctorGeneric.java:38: cannot find symbol symbol : method add(Dog) location: interface java.util.List<capture of ? extends Animal> animals.add(new Dog()); ^ 1 error

Lo que bsicamente dice es "no puedes aadir un Dog aqu", Si cambiamos el mtodo para que no aada nada, si funciona. Pero espera, hay ms (de cualquier manera, todo lo que hemos cubierto en esta seccin de genricos va a ser evaluada en la vida real, con las excepciones de tipo borrador, las cuales no se te pide conocer ningn detalle). Primero el <? extends Animal> quiere decir que puedes tomar cualquier subtipo de Animal, sin embargo, ese subtipo puede ser una subclase de una clase (abstracta o concreta) o un tipo que implemente la interfaz despus de la herencia. En otras palabras, la palabra clave extends en el contexto de un comodn representa ambas subclases y las interfaces implementadas. No existe <? implements Serializable>. Si quieres declarar un mtodo que tome cualquier cosa y adems implemente Serializable, deberas usar extends de esta manera:

Una cosa ms, solo hay un comodn que represente ambas interfaces y subclases. Y la palabra clave es extends. Pero cuando lo veas, piensa que "ES-UN", como en algo pasa el test instaceof().

email: brujulato@yahoo.es

Pgina 287

Curso gratuito de Java en emuladores L2 Java

2012

Sin embargo, hay otro escenario donde puedes aadir un comodn y aun puede aadir a la coleccin, aunque de forma segura, es super. Imagina por ejemplo, que declaras el mtodo de esta manera:

Ahora lo que hemos dicho en esta lnea:

es esencialmente "Hey compilador, por favor acepta cualquier List con un tipo genrico del tipo de Dog, o un supertipo de Dog. Nada ms bajo en el rbol de herencias puede entrar, pero cualquier cosa ms alta que Dog es vlido". Probablemente reconoces por qu funciona. Si pasas una lista de tipo Animal, entonces es perfectamente aceptado aadir un perro en ella. Si pasas una lista de tipo perro, es perfectamente vlido aadir un perro. Y si pasas una lista de tipo Objeto, todava es correcto pasar un perro. Cuando veas <? super ...>, le estas diciendo al compilador que aceptas los tipos de la derecha de super o cualquier supertipo ya que, esta es la clave que hace que funcione una coleccin declarada como cualquier supertipo de Dog ser aceptada como un elemento. List<Object> puede tomar Dog. List<Animal> puede tomar <Dog> y List<Dog> puede tomar Dog. As que pasar cualquier de ellos funcionara. De manera que la palabra clave super en connotacin de comodn te permite tener una coleccin restringida pero tambin una manera de aadir objetos a la coleccin. As, que el comodn te da asignaciones polimrficas, pero con ciertas restricciones que no tienes en los arrays. Pregunta rpida: estos mtodos son idnticos?

Si hay una diferencia (y no estamos diciendo an que la haya) cul es? Hay una gran diferencia. List<?>, que usa un comodn <?> sin la palabra clave extends o super, solo significa "cualquier tipo". As que quiere decir que cualquier tipo de List puede ser asignado al argumento. Podra ser un <Dog>, un <Integer>, un <JButton>, <Socket>... que

email: brujulato@yahoo.es

Pgina 288

Curso gratuito de Java en emuladores L2 Java

2012

importa. Y al usar el comodn solo, sin la palabra clave super (seguido del tipo), quiere decir que puedes aadir cualquier cosa a la lista referida como List<?>. List<Object> es completamente diferente de List<?>. List<Object> quiere decir que el mtodo solo puede tomar List<Object>. No un List<Dog>, o un List<Cat>. Sin embargo, significa que puedes aadir a la lista, ya que el compilador tiene ya la certeza de que vas a pasar solo objetos validos List<Object> al mtodo. Basado en lo anterior, imagnate si lo siguiente funcionar:

Si no, cual es el problema? El problema est en list.add() sin doInsert(). El comodn <?> te permite insertar cualquier clase de lista pasada al mtodo, pero el mtodo add() no es vlido, por la razn que vimos antes (que no puedes poner la clase equivocada de objeto en la coleccin). As que esta vez, la clase TestWildcards est bien, pero la clase Bar no compilar debido al mtodo add() que usa un comodn(sin super). Si cambiamos el mtodo:

email: brujulato@yahoo.es

Pgina 289

Curso gratuito de Java en emuladores L2 Java


Ahora funcionar? Si? No? por qu no?

2012

Esta vez la clase Bar, con el mtodo doInsert() compile bien. El problema es que el cdigo TestWildcards est intentando pasar un List<Integer> al mtodo que slo toma List<Object>. Y nada puede ser sustituido por <Object>. Sin embargo, List<? extends Object> y List<?> son absolutamente idnticos. Ambos dicen "puedo referirme a cualquier tipo de objeto". Pero como puedes ver, ninguno de e ellos son lo mismo que List<Object>. Una manera de recordar esto es que si ves un comodn (?), quiere decir muchas posibilidades. Si no ves el signo de interrogacin, entonces quiere decir <tipo> y absolutamente nada ms. List<Dog> significa <Dog> y no List<Beagle>, List<Poodle> o cualquier otro subtipo de Dog. Pero List <? extends Dog> podra querer decir List<Beagle>, List <Poodle> y etc... naturalmente List<?> significara, cualquier cosa. Que se te grabe en la cabeza que los comodines pueden usados solo por declaraciones de referencias (incluido argumentos, variables, tipos de retorno, etc...). No pueden ser usados como tipo de parmetros cuando creas una coleccin tipada. Piensa en que, mientras una referencia puede ser abstracta y polimrfica, el objeto actual creado debe ser de un tipo especfico. Tienes que bloquear el tipo (inmutable) cuando crees un objeto usando new. Como una pequea demostracin antes de meternos con los genricos, fjate en estos estamentos y adivina cul de ellos compilar:

Las respuestas correctas son, 1,2 y 5. Las tres que no compilaran son:

List<?> foo = new ArrayList<? extends Animal>(); El problema es que no puedes usar un comodn para la creacin del objeto. As que new ArrayList<? extends Animal>() no compilar.

List<? extends Dog> cList = new ArrayList<Integer>(); El problema es que no puedes asignar un Integer a una lista de referencia que toma solo Dog (incluyendo sus subtipos, claro).

Statement: List<? super Animal> dList = new ArrayList<Dog>(); El problema es que no puedes asignar un Dog a <? super Animal>. El Dog est demasiado "bajo" en el rbol de herencia. Solo <Animal> o <Object>podran ser vlidos. email: brujulato@yahoo.es Pgina 290

Curso gratuito de Java en emuladores L2 Java


Declaraciones de genricos.

2012

Hasta ahora, hemos hablado de crear tipos de colecciones seguras, y como declarar variables de referencia incluyendo argumentos y tipos de retornos usando genricos. Pero aqu hay unas pocas preguntas: Como sabemos que se nos permite especificar un tipo para una clase de coleccin? Funciona si escribo genrico con otras clases de API? Y finalmente, podemos declarar nuestra propia clase como tipo genrico? En otras palabras, podemos hacer una clase que requiera que alguien pase un tipo cuando la declaran y la instancian? Primero, obviamente la respuesta al API te dice cuando se espera a que parametrices un tipo. Por ejemplo, este es el API para la declaracin de java.util.List:

La <E> es el filtro para el tipo que le pasas. La interfaz List tiene comportamiento de "plantilla genrica", y cuando escribes tu cdigo, cambias la lista genrica a la lista <Dog> o List<Integer>, etc... La E, es solo una convencin. Cualquier identificador valido de Java funcionaria aqu, pero la E significa "Elemento", y se usa cuando la plantilla es una coleccin. La otra convencin es una T (de tipo), que se usa para, bueno, para cosas que no son colecciones. Ahora que has visto la declaracin de interface para List, que piensas de este mtodo add()?

En otras palabras, no importa cuando declares E en tu List, lo que importa es lo que t le aades. Imagina este cdigo:

La E en el API de la List tiene su forma colapsada, y viene del abstracto <tu tipo va aqui>, para la List de Animals. Y si hay una List de Animals, entonces aade el mtodo add() de List, que obviamente se comportar de esta manera:

Cuando miras un API de la clase genrica o de las interface, escoges un tipo de parmetro (Dog, JButton, incluso Object) y de manera mental encuentra y reemplaza cada instancia de E (o cualquiera de los identificadores usados en el filtro usado como parmetro). Creando tus propias clases genricas Intentemos crear nuestras propias clases genricas, para entender cmo funciona, y miraremos unos pocos detalles sobre las sintaxis sobre los genricos. Imagnate a alguien que ha creado una clase Rental, que maneja una piscina (piscina, caja de cosas, recipiente... llmalo como quieras) de objetos que se pueden alquilar.

email: brujulato@yahoo.es

Pgina 291

Curso gratuito de Java en emuladores L2 Java

2012

Ahora imagina que queras hacer una subclase de Rental que es slo para alquilar coches. Podras comenzar con algo como esto:

Pero cuando lo miras, te das cuenta de que: 1. Vas a hacer tu propio tipo de chequeo en el mtodo returnRental(). No puedes cambiar el tipo de argumento de returnRental() para coger un Car, ya que es una sobrescritura, no un mtodo sobrecargado de la clase Rental. 2. Realmente no quieres hacer subclases separadas para cada posible clase de cosa que se puede alquilar (coches, ordenadores, zapato de bolos, nios... etc.). Pero dado tu talento natural (aumentado en este escenario), rpidamente te das cuenta que puedes hacer un tipo genrico de la clase Rental, una plantilla para cualquier clase de cosa Rental, y haces bien en hacerlo. (Dijimos contribuido... ya que en realidad, puedes muy bien querer un comportamiento diferente para cada clase de cosa alquilable, pero aunque pueda ser resuelto limpiamente con comportamientos opuestos a la herencia, (usando la estrategia de diseo de patrn por email: brujulato@yahoo.es Pgina 292

Curso gratuito de Java en emuladores L2 Java

2012

ejemplo). Y no, los diseos de patrones no entran en el examen, pero aun pensamos que deberas de leer nuestro libro de diseo de patrones. As que aqu est la clase Rental actualizada:

Pongmoslo a prueba:

y nos sale un error:


kathy% javac1.5 RentalGeneric.java RentalGeneric.java:38: cannot find symbol symbol : method add(Cat) location: interface java.util.List<Car> carList.add(new Cat("Fluffy")); ^ 1 error

Ahora tenemos una clase Rental que puede ser tipada a lo que el programador elija, y el compilador lo forzar a ello. En otras palabras, funciona igual que la clase Collecion. Veamos email: brujulato@yahoo.es Pgina 293

Curso gratuito de Java en emuladores L2 Java

2012

ms ejemplos de sintaxis genricos que puedes encontrar en el API o en el cdigo fuente. Aqu tenemos otro ejemplo de una clase simple que usa el tipo de mtodo parametrizado de la clase de varias maneras:

Obviamente este uso de genricos es ridculo, y de hecho vers genricos fuera de las colecciones muy rara vez. Pero, necesitas entender la diferencia sintctica entre los diferentes tipos de genricos que te podrs encontrar, as que continuaremos con estos ejemplos hasta que los hayamos cubierto todos. Puedes usar ms de un tipo de parmetro en la definicin de una clase:

Y puedes usar comodines en la definicin de la clase, para especificar un rango (conocido como "bounds") para el tipo que puede ser usado en el parmetro:

email: brujulato@yahoo.es

Pgina 294

Curso gratuito de Java en emuladores L2 Java


Creando mtodos genricos

2012

Hasta ahora, cada ejemplo que hemos visto usa el tipo de parmetro en la clase, el tipo declarado con el nombre de la clase. Por ejemplo, en el UseTwo<X,Y>, usamos los filtros X e Y en el cdigo. Pero es posible definir un tipo parametrizado en un nivel ms granular, un mtodo. Imagina que quieres crear un mtodo que tome una instancia de cualquier tipo, instancias de un ArrayList de ese tipo, y aade la instancia al ArrayList. La clase en si misma no necesita ser genrica, bsicamente, solo queremos un mtodo que pueda pasar un tipo y que pueda usar ese tipo para construir un tipo de coleccin segura. Al usar un mtodo genrico, podemos declarar el mtodo sin un tipo especfico y ms tarde conseguir el tipo de informacin basada en el tipo del objeto pasado al mtodo. Por ejemplo:

En el cdigo anterior, si invocas el mtodo makeArrayList con la instancia de un perro (Dog), el mtodo se comportar como lo que se muestra a continuacin:

Y naturalmente si invocas el mtodo con un Integer, entonces la T es reemplazada por la I (no en bytecode, recuerda, estamos describiendo cmo se comporta, no como acta). Lo ms extrao sobre los mtodos genricos es que tienes que declarar el tipo de la variable antes del tipo de retorno del mtodo:

La <T> antes del void solo define que T es lo que usas como tipo en el argumento. Tienes que declarar el tipo especificado en la clase. En CreateAnArrayList, la clase no es genrica, as que no hay tipo de parmetro. Tambin eres libre de poner lmites (bounds) en el tipo que declaras, por ejemplo si quieres restringir el mtodo makeArrayList() solo a nmeros o sus subtipos (Integer, Float, etc...) diras

email: brujulato@yahoo.es

Pgina 295

Curso gratuito de Java en emuladores L2 Java

2012

El 98% de lo que normalmente se hacen con los genricos es solamente declarar y usar el tipo seguro de colecciones, incluyndole (y pasndole) los argumentos. Pero ahora ya sabes mucho ms (que no quiere decir todo) sobre cmo funcionan los genricos. Si esto ha sido fcil, excelente. Si fue doloroso... que sepas que aadir genricos al lenguaje Java estuvo muy cerca de causar una revuelta entre algunos de los desarrolladores ms experimentados de Java. La mayora de las crticas son de infelicidad, o de que no estn convencidos sobre la introduccin de las colecciones seguras, que hay 10 millones de reglas que tienen que aprenderse ahora. Es verdad que a partir de Java 5, aprender Java es algo ms difcil. Pero confa en nosotros... nunca hemos tomado ms de dos das para entender los genricos. Eso son 48 horas consecutivas.

email: brujulato@yahoo.es

Pgina 296

Curso gratuito de Java en emuladores L2 Java


Tema 20, clases anidadas

2012

Las clases anidadas (incluyendo las estticas anidadas) aparecen en la vida real.. Lo ms importante, es el cdigo para representar preguntas que incluyan las clases anidadas. A menos que entiendas en profundidad las reglas y sintaxis de estas clases, seguramente puedes equivocarte. Este captulo de clases anidadas se parece al de entrada y salida (inners y outers?) y se muestra las clases de sintaxis que encontrars habitualmente (incluso puede que unas sintaxis bien extraas). As que realmente tenemos dos objetivos en este captulo, aprender lo que necesitas para contestar preguntas evaluando tu conocimiento con las clases anidadas, y aprender como leer y entender el cdigo de las clases anidadas de manera que puedas entender el desarrollo de otros programadores. Entonces, de que va esto de las clases anidadas? Antes de meternos de lleno, tenemos que advertirte (sino lo sabes ya) que las clases internas han inspirado un profundo amor o un gran odio desde que fue introducido en la versin 1.1 del lenguaje. Por una vez, vamos a intentar guardarnos nuestras opiniones y mostrarte solo los hechos que necesitas saber. Depende de ti como usar las clases anidadas para tus propios desarrollos. Creemos que ellas tienen usos muy poderosos y eficientes en situaciones especficas, incluyendo cdigo mas legible y de mas fcil mantenimiento, aunque tambin se puede abusar y llevar un cdigo claro a convertirlo en un laberinto de un campo de maz, y caer en el sntoma conocido como "reuseless": cdigo no reutilizable. Las clases anidadas te permiten definir una clase dentro de otra. Esto las provee de un tipo de alcance ya que puedes hacer una clase miembro de otra clase. Las clases, como tienen variables y mtodos, una clase tambin puede tener como miembro otras clases. Estas vienen en varios sabores, dependiendo de cmo y dnde definas la clase interna, incluyendo una clase especial conocida como "clase anidada top-level" (una clase anidada marcada como esttica), que tcnicamente no es una clase anidada. Ya que una clase anidada es an una clase definida con el alcance de otra clase, vamos a cubrirlas en este captulo. Al contrario de los otros captulos de este manual, los objetivos para la clases anidadas no tienen un objetivo definido, sino que se trabajan paralelamente a todo lo que llevamos visto. As que para este captulo, mi objetivo est en la siguiente lista, que representa las cuatro clases anidadas que veremos en este captulo:

Clases internas Mtodos locales de las clases internas Clases internas annimas Clase esttica anidada

email: brujulato@yahoo.es

Pgina 297

Curso gratuito de Java en emuladores L2 Java


20.1 Las clases internas

2012

Eres un programador orientado a objetos, as que sabes que reusar el cdigo, flexibilidad y extensibilidad es lo que necesitas para las clases especializadas. En otras palabras, una clase debera tener cdigo solo para cosas que ese objeto en particular necesita hacer, cualquier otro comportamiento debera formar parte de otra clase mejor situada para hacer ese trabajo. Algunas veces, te podrs ver diseando una clase donde descubras que necesitas el comportamiento que pertenece a otra clase especializada, pero que tambin necesitara estar ntimamente ligada a la clase que estas diseando. Los manejadores de eventos son quizs el mejor ejemplo (y de hecho, no conozco otro mejor, ya que son una de las principales razones por las que se aadieron clases internas al lenguaje en primer lugar). Si tienes una clase GUI que realiza el trabajo, digamos, un cliente chat, podras querer mtodos especficos para ese cliente (aceptar entradas, leer nuevos mensajes, enviar al usuario de vuelta al server...) en tu clase. Pero como invocas esos mtodos en primer lugar? Un usuario clica un botn. O escribe algo en el textbox. O separadamente, hace un trabajo de tipo I/O para conseguir los mensajes que tiene de lado del servidor. As que tienes tus mtodos especficos para ese cliente chat, pero tambin necesitas mtodos para manejar eventos, el botn presionado, escribir con el teclado, I/O disponible, etc... sobre los mtodos del cliente. El escenario ideal, desde una perspectiva OO, es mantener los mtodos del chat en el chat del cliente, y poner el manejador de eventos en una clase aparte. Esto no es nada inusual, despus de todo, estas diseando clases orientadas a objetos. Pero el problema con el cliente chat se presenta en este escenario: cuando el usuario presiona el botn enviar (indicando que su mensaje quiere que sea escrito en el servidor), el cliente chat que enva el mensaje necesita leer un textbox en particular. En otras palabras, si el usuario clica el botn A, el programa se supone que tiene que extraer el texto del textbox B, de una instancia en particular del cliente chat. No de otros textbox de otros objetos, sino del textbox especfico de la instancia especifica del cliente chat al que se est referenciando. As que el manejador de eventos necesita acceso a los miembros del cliente-chat, para ser una ayuda eficaz a una instancia de cliente-chat en particular. Y que pasa si la clase ChatClient necesita heredar de una clase, pero el cdigo manejador de evento hereda de otra clase? no puedes heredar ms de una clase, as que poniendo todo el cdigo (el cliente-chat especifico y el cdigo del manejador de eventos) en una clase no funcionara en este caso. As que realmente lo que te gustara tener es el beneficio de poner tu evento en una clase separada (mejor OO, encapsulacin, y la habilidad de heredar a otra clase adems de la que ClientChat hereda) y permitir que el cdigo que maneja los eventos tengan acceso a los miembros de ChatClient (as que el cdigo que maneja los eventos puede, por ejemplo, actualizar las variables de instancia privadas de ChatClient). Podras manejarlo haciendo que los miembros de ChatClient sean accesibles desde el manejador de eventos, por ejemplo, hacindolos pblicos, aunque tampoco es una buena solucin. Ya sabes cmo va esto, una de las claves de los beneficios de una clase interna es esa relacin de amistad, que se comparte con una instancia de la clase ms exterior. Esa relacin especial, da cdigo a la clase interna para acceder a sus miembros de la clase externa, como si la clase interna fuera parte de la clase externa. El hecho es que es exactamente as, la clase interna es parte de la clase externa. No tan solo una parte, sino una clase de pleno derecho con todos los privilegios para acceder a los miembros de la clase externa. Si, una clase interna puede acceder incluso a los miembros privados de su clase externa. (Relax, esa es la clave, recuerdas? Queremos establecer una relacin intima entre la clase interna y la externa, pero tambin

email: brujulato@yahoo.es

Pgina 298

Curso gratuito de Java en emuladores L2 Java

2012

queremos que las dems clases se queden al margen. Escribiste una clase externa y adems una interna, no estas violando una encapsulacin, sino que estas diseando de esta manera).

Escribiendo un cdigo "regular" de una clase interna Vamos a usar el trmino "regular" para representar las clases internas que no son:

estticas contienen mtodos locales no son annimas

Para el resto de esta seccin, simplemente usaremos el trmino "clase interna". (Cuando cambiemos de la una a cualquier de los otros tres tipos de la lista, lo sabrs). Puedes definir una clase interna dentro de las llaves de la clase externa:

y lo puedes compilar:

y terminars con dos clases:


MiClaseExterna.class MiClaseExterna$MiClaseInterna.class

La clase interna esta al final, una clase separada, as que se genera una fila para ella. Pero la clase interna no es accesible de la manera habitual, no puedes hacer:

y esperar a que se ejecute el mtodo main() de la clase interna, porque una clase regular no tiene declaraciones estticas de ninguna clase. La una manera en la que puedes acceder a una clase interna es a travs de la instancia de la clase externa! En otras palabras, solo puedes hacerlo en tiempo de ejecucin cuando ya exista una instancia de la clase externa que est atada a la clase interna. Vers todo en un momento, primero reforzamos las clases un poco:

email: brujulato@yahoo.es

Pgina 299

Curso gratuito de Java en emuladores L2 Java

2012

Este cdigo es perfectamente vlido. Fjate que la clase interna ciertamente est accediendo a los miembros privados de la clase externa. As que como cualquier miembro de la clase externa (como un mtodo) puede acceder a otros miembros, privados o no. Ok, as que ahora sabemos cmo escribir el cdigo para que una clase interna acceda a los miembros de una clase externa, para que lo usaras?

Instanciando una clase interna Para instanciar una clase interna, tienes que instanciar la clase externa. No hay excepciones a esta regla: la instancia de una clase interna jams puede estar sola sin una relacin directa con la instancia externa.

Instanciando una clase interna del interior de una clase externa Lo ms comn, es que la clase externa cree la instancia de la clase interna, ya que normalmente la clase externa querr usar la clase interna para su uso personal. Modificaremos la clase MyOuter para crear una instancia de MyInner.

Puedes ver en el cdigo anterior que MyOuter trata a MyInner como si MyInner fuera otra clase accesible, la instancia usando el nombre de la clase y luego invoca el mtodo en una variable de referencia. Pero la nica razn por la que esto funciona es porque el cdigo del mtodo de la clase externa est haciendo la instanciacin. En otras palabras, ya exista una instancia de la clase externa, la instancia que ejecuta el mtodo makeInner(). As que como se instancia un objeto MyInner desde cualquier parte de la clase MyOuter? Es posible?

email: brujulato@yahoo.es

Pgina 300

Curso gratuito de Java en emuladores L2 Java


Creando una clase interna desde una clase externa

2012

Si queremos crear una instancia de una clase interna, tenemos que tener una instancia de la clase externa, ya sabias eso, pero piensa en las implicaciones... quiere decir que, sin una referencia a la instancia de la clase externa, no puedes instanciar la clase interna desde un mtodo esttico de la clase externa (porque, no olvides, que en el cdigo esttico no existe la referencia this), o desde otro cdigo en otra clase. El compilador cuida de ello, as que nunca veras ninguna excepcin. El cdigo para crear una instancia desde cualquier parte de la clase exterior cuyo cdigo no sea esttico, es muy simple, pero memorzalo!

El cdigo anterior es el mismo sin importar que el mtodo main est dentro de la clase MyOuter o de otra clase (asumiendo que la otra clase tenga acceso a MyOuter y ya que MyOuter tiene acceso por defecto, significa que el cdigo debe estar en una clase dentro del mismo paquete que MyOuter. Si lees entre lneas, puedes ver esto:

Puedes pensar de esto como si estuvieras invocando un mtodo de una instancia externa, pero el mtodo est en una clase interna, y se invoca usando la palabra new. Instanciar una clase interna es el nico escenario en el que tienes que invocar new, lo opuesto a invocar el constructor de una clase usando new. Aqu hay un pequeo resumen de las diferencias entre la clase interna que se instancia desde la clase externa (pero no esttica), y la clase interna instanciada en la clase externa.

Desde dentro, el cdigo de la clase externa, usa el nombre de la clase de forma normal: MyInner mi = new MyInner(); Desde el exterior de la clase (incluyendo mtodos estticos dentro de la clase externa), la clase interna debe ahora incluir el nombre de la clase externa: MyOuter.MyInner Para instanciarla, tienes que usar una referencia a la clase externa: new MyOuter().new MyInner(); referenciaObjetoExterior.new MyInner(); si ya tienes una instancia de la clase externa.

email: brujulato@yahoo.es

Pgina 301

Curso gratuito de Java en emuladores L2 Java

2012

Referenciando la instancia de la clase externa o interna desde la clase interna Como se hace para que un objeto se refiera a el mismo normalmente? usando la referencia this. Aqu hay un pequeo resumen:

la palabra clave this puede ser usada slo En otras palabras, no en cdigos estticos.

dentro de

una instancia.

La referencia this es una referencia al objeto ejecutado en este momento. En otras palabras, el objeto cuya referencia fue usada para invocar el mtodo que est corriendo actualmente. La referencia this es la manera en que un objeto puede pasar una referencia de si mismo a otro cdigo, como argumento del mtodo:

En el cdigo de una clase interna, la referencia this se refiere a la instancia de la clase interna, como probablemente esperabas, ya que this siempre se refiere al objeto que se est ejecutando actualmente. Pero qu pasa si el cdigo de la clase interna quiere una referencia explcita a la clase externa? Aunque normalmente el cdigo de la clase interna no necesita una referencia a la clase externa, ya que implcitamente tiene acceso a los miembros de la clase externa, slo necesitara una referencia de la clase externa como sigue:

Si ejecutamos el cdigo completo como sigue:

email: brujulato@yahoo.es

Pgina 302

Curso gratuito de Java en emuladores L2 Java

2012

la salida sera
Outer x is 7 Inner class ref is MyOuter$MyInner@113708 Outer class ref is MyOuter@33f1d7

As que las reglas para una clase interna que se referencia as misma a una instancia de la clase externa es como sigue:

Para referenciar la clase interna a s misma, desde el cdigo de la clase interna, se usa this. Para referenciar un this externo desde el interior de la clase interna, se usa el nombreDeLaClase.this (ejemplo, ClaseExterna.this).

Modificadores aplicados a los miembros de las clases internas Una clase regular interna es un miembro de la clase exterior al igual que lo son sus variables y mtodos, as que estos modificadores pueden ser aplicados a la clase interna:

final abstract public private protected static pero la clase esttica se convierte en una clase esttica anidada, no en una clase interna. Strictfp

email: brujulato@yahoo.es

Pgina 303

Curso gratuito de Java en emuladores L2 Java


Mtodos locales de las clases internas.

2012

Una clase regular tiene el rango de visibilidad dentro de las llaves de la clase externa, al igual que un mtodo (en otras palabras, al mismo nivel que una variable de instancia). Pero tambin puedes definir una clase interna dentro de un mtodo:

Este cdigo declara una clase, MyOuter2, con un mtodo, doStuff(). Pero dentro de doStuff(), una clase declarada, MyInner, y tiene su mtodo propio seeOuter(). El cdigo es completamente intil sin embargo, porque nunca podra instanciarse la clase interna. Declarar una clase no significa que puedas instanciarla. As que si quieres usar la clase interna (digamos, para invocar sus mtodos), entonces tienes que hacer una instancia de ella en alguna parte dentro del mtodo, pero por debajo de la definicin de la clase interna. El siguiente cdigo es legal y muestra como instanciar un mtodo de la clase local interna:

Que es lo que puede y no puede hacer una clase interna en un mtodo local. La clase interna de un mtodo local puede ser instanciado solo dentro del mtodo donde la clase interna fue definida. En otras palabras, ningn otro cdigo en otro mtodo dentro o fuera de la clase exterior, puede instanciar la clase interna creada en el mtodo local. Al igual que las clases regulares, las clases internas de los mtodos locales comparten una relacin muy estrecha con la clase, y puede acceder a sus miembros privados (o cualquier otra cosa). Sin embargo, las clases internas no pueden usar las variables locales del mtodo donde est la clase. Por qu no?

email: brujulato@yahoo.es

Pgina 304

Curso gratuito de Java en emuladores L2 Java

2012

Piensa en ello. Las variables locales de los mtodos viven en el Stack, no en el Heap, y existen por un tiempo limitado en el mtodo. Ya sabes que el alcance de una variable local se limita al mtodo donde la variable ha sido declarada. Cuando el mtodo finalice, el marco donde la variable vive, desaparece y es historia. Pero incluso despus de que el mtodo se complete, la clase interna creada en su interior puede estar an viva en el Heap si, por ejemplo, una referencia de la misma ha sido pasada a otro cdigo y almacenada en una variable de instancia. Ya que las variables locales no estn garantizadas para vivir tanto como la clase interna del mtodo, la clase interna no puede usarlas. A menos que la variable sea marcada como final! El siguiente cdigo intenta acceder a una variable local desde el interior de una clase interna en un mtodo local:

y al compilar el cdigo, el compilador se mosquea seriamente y nos lanza:


MyOuter2.java:8: local variable z is accessed from within inner class; needs to be declared final System.out.println("Local variable z is " + z);

Marcando la variable como final, arreglamos el desatino:

Tan solo recuerda que los modificadores de un mtodo, se aplican las mismas reglas en las clases internas de los mtodos. Por ejemplo, no puedes marcar un mtodo local con una clase interna pblica, privada, protegida, esttica, transient y dems. Los nicos modificadores que puedes aplicar a una clase creada dentro de un mtodo local son abstracta y final, pero como siempre, nunca las dos a la vez.

20.2 Clases internas annimas Hasta ahora hemos visto en definicin una clase encerrada en otra clase (una clase regular interna) y dentro de un mtodo (un mtodo local con una clase interna). Finalmente, vamos a ver la clase ms inusual que jams vers en Java, las clases internas declaradas sin nombre (en el mundo se le conoce como annima). Y si eso no fuera suficientemente desquiciado, puedes definir estas clases sin mtodos, incluso sin argumentos en el mtodo. Primero de todo, vamos a verlo en el clsico texto plano (como si ese texto plano fuese la clase annima, aunque incluso la versin de texto plano viene en dos sabores), y luego veremos el argumento declarado en la clase annima interna. email: brujulato@yahoo.es Pgina 305

Curso gratuito de Java en emuladores L2 Java

2012

Quizs lo ms importante de tu trabajo aqu es aprender a manejarte cuando veas esta sintaxis. En el mundo real podras verte de lleno con clases annimas anidadas, podras verlas en hilos, envoltorios, sobrescritura, el recolector de basuras, y... bueno, ya has pillado la idea.

Clases internas annimas en texto plano, estilo nmero uno Observa este cdigo vlido y legal aunque bien raro la primera vez que lo ves:

Miremos lo que pasa en el cdigo:


Definimos dos clases, Popcorn y Food. Popcorn tiene un mtodo, pop(). Food tiene una variable de instancia, declarada como tipo Popcorn. Eso es todo en Food. Food no tiene mtodos.

Y aqu viene la interesante: La variable de referencia Popcorn no es una instancia de Popcorn, es una instancia de una subclase annima de Popcorn. Veamos solamente la clase annima:

Lnea 2, comienza con la declaracin de una variable de tipo Popcorn, pero en lugar de parecerse a esto:

hay unas llaves al final de la lnea 2, donde habra de estar el punto y coma.

email: brujulato@yahoo.es

Pgina 306

Curso gratuito de Java en emuladores L2 Java

2012

Puedes leer en la lnea 2: Declara una variable de referencia p, de tipo Popcorn. Luego declara un clase nueva que no tenga nombre, pero que es subclase de Popcorn. Y es aqu donde la llave abre la definicin de la clase. Linea 3, ahora tenemos la definicin de la nueva clase. Y que est haciendo? Sobrescribir el mtodo pop() de la superclase Popcorn. Esto es todo para lo que sirve hacer una clase annima interna, sobrescribir uno o ms mtodos de la superclase! (o implementar mtodos de una interface, pero lo veremos ms tarde). Linea 4, es el primer (y nico en este caso) estamento que sobrescribe el mtodo pop(). Nada especial. Linea 5, est cerrando las llaves del mtodo pop(). Linea 6, aqu es donde tienes que prestar atencin, la lnea 6 incluye una llave que cierra la definicin de la clase, pero hay ms! Tambin tiene un punto y coma que marca el final del estamento comenzado en la lnea 2, el estamento es donde todo empieza, se declara e inicializa la referencia de variable a Popcorn, y a la izquierda es la referencia a una nueva instancia, en tiempo real, annima (sin nombre) de una subclase de Popcorn. El polimorfismo entra en juego cuando una clase annima interna interviene. Recuerda que, en el ejemplo de Popcorn, estamos usando una variable de referencia a la superclase para referirnos al objeto de la subclase. Qu implicaciones tiene esto? Puedes llamar slo a los mtodos annimos en la clase interna donde se define la variable de referencia. Esto no es distinto de otras referencias polimrficas, por ejemplo:

As que tienes que ser capaz de ver las clases annimas internas que, ms que sobrescribir el mtodo de la superclase, definen su propio mtodo. La definicin del mtodo no es problema, el problema real es como invocas ese nuevo mtodo? La superclase no sabe nada del nuevo mtodo (definido en la subclase annima), as que el compilador se quejara si intentas invocar cualquier mtodo de la clase interna que no est en la definicin de la superclase. Observa este cdigo ilegal:

email: brujulato@yahoo.es

Pgina 307

Curso gratuito de Java en emuladores L2 Java

2012

al compilar esto, nos arroja:


Anon.java:19: cannot resolve symbol symbol : method sizzle () location: class Popcorn p.sizzle(); ^

Esta es la manera en que nos dice el compilador "No puedo encontrar el mtodo sizzle() en la clase Popcorn seguido por, "Dame una pista".

Clases internas annimas en texto plano, estilo nmero dos. La nica diferencia entre el manera uno y dos es que de la primera manera se crea una subclase annima de un tipo especificado, mientras que de la segunda manera se crea una implantacin de un tipo de interface. En el ejemplo anterior, definimos una subclase annima del tipo Popcorn:

Pero si Popcorn fuera una interfaz en lugar de un tipo de clase, entonces el nuevo annimo sera un implementador de interface en lugar de una subclase de la clase. Mira este ejemplo:

email: brujulato@yahoo.es

Pgina 308

Curso gratuito de Java en emuladores L2 Java

2012

Este cdigo, al igual que el ejemplo del Popcorn, crea una instancia de una clase annima interna, pero esta vez la nueva clase creada en tiempo de ejecucin es un implementador de la interfaz Cookable. Y fjate bien porque es la nica vez que vas a ver esta sintaxis:

donde Cookable es una interfaz en vez de una clase no abstracta. Porque, piensa en esto, no puedes instanciar una interface, si, eso es lo que el cdigo parece que est haciendo. Pero naturalmente, no est instanciando Cookable, est creando una instancia de un nuevo, annimo, implementador de Cookable. Puedes leer esta lnea as:

"Declaro una variable de referencia de tipo Cookable que, obviamente, se referir a un objeto de la clase que implementa la interfaz Cookable. Pero, no tenemos una clase que implementa Cookable, as que vamos a hacer las cosas bien aqu, ahora. No necesitamos un nombre para la clase, pero ser una clase que implemente Cookable, y estas llaves darn la definicin de la nueva clase implementada". Una cosa ms a tener en cuenta sobre la implementacin de clases annimas, slo se puede implementar una. Simplemente es que no existe ningn mecanismo que pueda implementar mltiples interfaces en una clase interna annima. La verdad, es que una clase interna annima nunca hereda una clase e implementa una interfaz al mismo tiempo, la clase interna tiene que ser elegida tambin para ser una subclase de una clase dada, y no directamente implementada por cualquier interface, o implementa una sola interface. Pero directamente, queremos decir que se usa de la palabra clave implements como parte de la declaracin de la clase. Si la clase interna annima es una subclase de un tipo, automticamente implementa cualquier interface implementada por la superclase.

Argumento definido en una clase interna annima Si entendiste lo que hemos cubierto en este captulo, entonces esta ltima parte ser simple. Si lo tienes borroso, tendras que volver e leer las secciones. Si no estn completamente claras, nos gustara tomar toda la responsabilidad por la confusin. Pero somos felices por compartir. Bien, si ests leyendo esto, vamos a asumir que entendiste las secciones anteriores, y ahora vamos a aadir una nueva locura. Imagnate el siguiente escenario. Estas escribiendo la clase

email: brujulato@yahoo.es

Pgina 309

Curso gratuito de Java en emuladores L2 Java

2012

perfecta, cuando escribes cdigo para llamar a un mtodo de la clase Bar, y ese mtodo toma un objeto de tipo Foo (por ejemplo).

No hay problema, excepto que no tienes un objeto de la clase que implementa Foo, y no puedes instanciar una tampoco, ya que no tienes tampoco una clase que implemente Foo para crear una sola instancia. As que lo primero que necesitas es esa clase que implementa Foo, y luego una instancia de esa clase para pasarle el parmetro al mtodo doStuff() del a clase Bar. Como programador sabio que eres, simplemente defines una clase annima interna, justo dentro del argumento. Eso es correcto, ya que es donde esperas encontrar una clase. Y as es como queda:

Toda la accin empieza en la lnea 4. Estamos llamando a doStuff() en el objeto Bar, pero el mtodo toma una instancia Foo, Foo es una interfaz. As que tenemos que hacer una implementacin y una instancia de la clase, el argumento doStuff() es correcto. As que, que es lo que hacemos? Escribimos new Foo() { que comienza la definicin de la nueva clase para la clase annima que implementa la interface Foo. Foo slo tiene un mtodo, foof(), as que en las lneas 5, 6, y 7 implementamos el mtodo foof(). Luego, la lnea 8, whoa! ms raro todava. La primera llave cierra la definicin de la clase annima. Pero no olvides que todo sucedi como parte de un mtodo, as que hay que cerrar el parntesis al terminar la invocacin del mtodo, y luego tenemos an que terminar el estamento que comenz en la lnea 4, as que finalizamos con un punto y coma. email: brujulato@yahoo.es Pgina 310

Curso gratuito de Java en emuladores L2 Java

2012

Estudia esta sintaxis! Vers clases annimas internas, y tendrs que ser muy perspicaz y asegurarte que estn cerradas. Si son argumentos locales, terminan as: }}; La sintaxis no es lo que se usa virtualmente en la otra parte de Java, as que ten cuidado. Cualquier mtodo puede involucrar clases annimas internas como parte del cdigo.

email: brujulato@yahoo.es

Pgina 311

Curso gratuito de Java en emuladores L2 Java


20.3 Clases estticas internas Hemos dejado lo mas fcil para el final :)

2012

Algunas veces oirs de las clases anidadas estticas que son clases estticas internas, pero realmente no son clases internas del todo, no cumple la definicin estndar de una clase interna. Mientras una clase interna (no importa de qu clase) disfruta de una relacin especial con la clase externa (o ms bien que las instancias de las dos clases comparten una relacin), una clase anidada esttica no lo hace. Es simplemente una clase no interna (tambin conocida como "top level") con visibilidad dentro de la otra clase. De manera que las clases estticas internas son ms por el nombre que tienen que por la simplicidad entre la clase interna y la externa.

La clase en si misma no es realmente esttica, eso no existe. El modificador static en este caso dice que la clase anidada es una miembro esttico de la clase externa. Lo que quiere decir que es accesible, al igual que los otros miembros estticos, sin tener que instanciar desde la clase externa, la clase esttica.

Instanciando y usando una clase esttica anidada Se usa la sintaxis estndar para acceder a las clases estticas anidadas desde la clase donde se crea. La sintaxis para instanciar una clase esttica anidada desde otra clase es un poco distinto de una clase interna normal, se parece a esto:

lo que produce:
hi hi 2

email: brujulato@yahoo.es

Pgina 312

Curso gratuito de Java en emuladores L2 Java


21 Trabajando con hilos

2012

Imagnate una aplicacin con un montn de habilidades. Una de sus funciones es descargar el ltimo stock de precios y otra es la de comprobar precios para advertencias, y una tercera operacin es la de analizar un dato histrico de la compaa XYZ. En una aplicacin de un solo hilo, estas acciones se ejecutaran una detrs de la otra. La siguiente accin slo podr ocurrir cuando la anterior haya finalizado. Si un anlisis de datos histricos tarda media hora, y el usuario ejecuta una descarga y una comprobacin, la advertencia podra venir demasiado tarde, como la de vender o comprar tales productos. Solo imaginemos una aplicacin que sea multihilos. Lo ideal sera que la descarga sucediese en un trasfondo (en otro hilo). De esta manera otros procesos podran ejecutarse a la misma vez, por ejemplo, una advertencia podra ser ejecutada instantneamente. Todo mientras el usuario esta interactuando con otras partes de la aplicacin. El anlisis, tambin podra ir en otro hilo, as el usuario puede trabajar en el resto de las aplicaciones mientras los resultados estn siendo calculados. As que es exactamente un hilo? En java, un hilo significa dos cosas:

Una instancia de la clase java.lang.Thread Un hilo de ejecucin

Una instancia de Thread es solo... un objeto. Al igual que los otros objetos de Java, tiene variables y mtodos, y vive y muere en el Heap. Pero un hilo de ejecucin es un proceso individual (un proceso de peso ligero) que tiene su propio Stack (pila). En Java, hay un hilo por llamada a la pila (y en cualquier momento puedes obtener un Stack trace de cualquier cosa que ocurra despus de que comience el main, pero no dentro de otro hilo), y veras main() como su primer mtodo en la pila. Tan pronto como creas un hilo nuevo, se crea una nueva pila y mtodos que son llamados desde el hilo que ejecuto la pila un una pila aparte. La segunda llamada a esa pila se ejecuta concurrentemente con la pila principal (la que ejecut la segunda pila), pero afinaremos mas esta nocin a lo largo de este captulo. Puedes encontrar algo confuso el hablar de cdigo que se ejecuta concurrentemente, como si fuera en paralelo, dado que solo hay una CPU en la mayora de las maquinas que ejecutan Java. Como se come eso? La JVM, que es la que consigue entenderse con la CPU, tiene un mecanismo que el sistema operativo usa, opera como un mini sistema operativo y lo que hace es darle un tiempo de ejecucin al hilo sin importar el sistema operativo del que estemos hablando. En algunas JVMs, los hilos de Java estn mapeados con el sistema operativo nativo, pero no vamos a discutir eso aqu, esto no nos interesa saberlo hoy. Tampoco es un requisito entender cmo se comportan los hilos en los diferentes entornos virtuales. El hecho es que el concepto ms importante para entender en este captulo es: Cuando se convierte en un hilo, se garantizan pocas cosas. As que seamos cautos cuando interpretemos el comportamiento que vers en una mquina de "cmo funciona un hilo". Se espera que sepas que es lo que un hilo garantiza y lo que no, as que puedes disear un programa que de una manera funcione sin importar la JVM. Es la clave de Java. El desarrollo sobre hilos es lo ms difcil de un proyecto. El hecho es que la mayora de la gente caen en este punto. Si no te familiarizas con ellos, probablemente suspenders el examen de programador SCJP si alguna vez quieres presentarte, as que necesitars pasar un tiempo con email: brujulato@yahoo.es Pgina 313

Curso gratuito de Java en emuladores L2 Java

2012

ellos experimentando. Tambin una nota de alegacin final, este captulo no intenta ensearte como disear una aplicacin multihilos sana y segura. Solo vamos a araar la piel de este gran tpico que es este captulo! Vas a aprender lo bsico del manejo de hilos, y lo que necesitas para conseguir pasar las preguntas del examen de programador. Despus podrs escribir un cdigo decente multihilos, sin embargo necesitas estudiar ms complejidades sobre el cdigo multihilos. Nota: el tpico de daemon no est en el examen. Todos los hilos discutidos en este captulo son hilos de "usuario". Tu y el sistema operativo podis crear una segunda clase de hilo llamada hilo daemon. La diferencia entre estos (usuario y daemon) es que la JVM sale de una aplicacin solo cuando todos los hilos de usuario se completan, la JVM no contempla si los hilos daemon han finalizado, as que una vez que los hilos de usuario terminan, la JVM sale, sin importar el estado de los hilos daemon. Nuevamente, este tpico no entra en el examen.

21.1 Creando un hilo Un hilo en Java comienza con una instancia de java.lang.Thread. Encontrars mtodos en la clase Thread que maneje hilos incluyendo la creacin, comienzo y pausa de ellos. Para el examen, necesitaras conocer, como mnimo estos mtodos: start() yield() sleep() run() La accin comienza en el mtodo run(). Imagnate un cdigo que quieres ejecutar en un hilo separado como un trabajo para hacer. En otras palabras, tienes algunas tareas que necesitas ser hechas, digamos descargar un stock de precios mientras otras cosas suceden en el programa, as que realmente quieres que esa tarea se ejecute en su propio hilo. As la tarea que quieres est en job, y la cosa que ests haciendo, est en el hilo. El job siempre comienza con un mtodo run():

Siempre escribirs el cdigo que necesita ser ejecutado por separado en un mtodo run(). El mtodo run() invocar los otros mtodos, naturalmente, pero el hilo de ejecucin, la nueva pila, siempre ser invocada por run(). As que donde va el mtodo run()? En una de las dos clases que puedes definir para que tu hilo funcione. Puedes definir e instanciar un hilo de dos maneras:

Heredando java.lang.Thread Implementando una interfaz Runnable

Necesitas saber ambas para el examen, quizs en el mundo real lo ms normal es implementar Runnable en lugar de heredar Thread. Heredar Thread es ms fcil, pero no es

email: brujulato@yahoo.es

Pgina 314

Curso gratuito de Java en emuladores L2 Java

2012

una buena prctica. Por que? Porque la subclase tendran que reservar especializaciones para las superclases. As que la nica vez que realmente tiene sentido (desde una perspectiva OO) heredar la clase Thread es cuando tienes una versin ms especializada de Thread. En otras palabras, porque tienes ms de un hilo especializado en un comportamiento. Oportunidades de que trabajes en un hilo que t quieras que realice una tarea, existen. En ese caso, tu disearas una clase que implementase una interfaz Runnable, que tambin permite a tu clase heredar otras clases libremente.

Definiendo un hilo Para definir un hilo, necesitas un lugar para poner tu mtodo run(), y como nosotros hemos discutido antes, puedes hacerlo heredando la clase Thread o implementando la interfaz Runnable. Miraremos ambas en esta seccin.

Heredando java.lang.Thread La manera ms simple de definir el cdigo que se ejecutar en un hilo separado es

Heredar java.lang.Thread Sobrescribir el mtodo run()

Se parece a esto:

La limitacin de esto (adems de que el diseo es pobre en la mayora de los casos) es que si heredas Thread, no puedes heredar nada ms. Y adems, si no necesitas realmente heredar el comportamiento de Thread tampoco tiene necesidad de hacerse, ya que, para usar un hilo, necesitaras instanciarlo de alguna manera. No olvides que eres libre de sobrecargar el mtodo run() en tu subclase de Thread:

Pero tienes que saber esto: el mtodo sobrecargado run(String s) ser ignorado por la clase Thread a menos que se invoque el mismo. La clase Thread est esperando un mtodo run() sin argumentos, que ejecutara el mtodo run() por ti en una pila nueva despus de que el hilo haya comenzado a ejecutarse. email: brujulato@yahoo.es Pgina 315

Curso gratuito de Java en emuladores L2 Java

2012

La clase Thread no va a invocar el mtodo run(String s) por ti, e incluso si invocas al mtodo directamente, la ejecucin no tomar parte en un hilo nuevo separado de la pila actual. Se ejecutar en la misma pila desde donde el cdigo fue invocado.

Implementando java.lang.Runnable Al implementar la interfaz Runnable puedes heredar tu clase a otra, pero run() se ejecutar en un nuevo hilo. Sera como esto:

No importa el mecanismo que elijas, el cdigo es el mismo, que puede ser ejecutado por un hilo. Ahora vamos a instanciar tu clase y veremos como esta cosa funciona.

21.2 Instanciando un hilo Recuerda que cada hilo de ejecucin comienza como una instancia de la clase Thread. No importa si ejecutas el mtodo run() en una subclase de Thread o por implementar Runnable en tu clase ya est hecho, todava tienes que hacer que ese objeto funcione. Si heredas la clase Thread, la instanciacin es bastante simple (veremos algunos constructores sobrecargados en un momento):

Si implementas Runnable, las instanciacin es solo un poco menos simple. Para ejecutar el cdigo en un hilo nuevo, tendrs que instanciar Thread. Pero en lugar de combinar el hilo y el cdigo (el cdigo del mtodo run()) en una clase, vas a dividirlo en dos clases, la clase Thread para el cdigo especifico del hilo y tu implementacin de Runnable para el cdigo que debe ejecutarse en un hilo nuevo. Otra manera muy comn de hacer esto es que el Thread es el trabajo que se est haciendo, y Runnable lo que tiene que hacerse. Lo primero es instanciar tu clase Runnable:

Luego, instancias Thread (alguien tiene que ejecutar tu cdigo...) y dale la tarea!

Si creas un hilo usando el constructor sin argumentos, el hilo se invocar a si mismo (el mtodo run()) cuando sea la hora de empezar. Eso es exactamente lo que quieres cuando heredas Thread, pero cuando usas Runnable, necesitas decirle al nuevo hilo que use tu email: brujulato@yahoo.es Pgina 316

Curso gratuito de Java en emuladores L2 Java

2012

mtodo run() en lugar del suyo. El Runnable que le pasas al constructor de Thread se le llama target, o target Runnable. Puedes pasarle una sola instancia Runnable a mltiples objetos Thread, as que el mismo Runnable se convierte en el target de muchos hilos, tal como se muestra:

Dando el mismo target a mltiples hilos ests haciendo que varios hilos de ejecucin realicen la misma tarea (y que la misma tarea se realice varias veces). Adems del constructor sin argumentos y el constructor que toma un Runnable (el target, la tarea a realizar), hay otros constructores sobrecargados en la clase Thread. Los constructores con los que tenemos que tener cuidado son:

Thread() Thread(Runnable target) Thread(Runnable target, String name) Thread(String name)

Necesitas reconocerlos todos! Un poco ms tarde veremos todos los constructores de la lista. As que ahora tienes una instancia de Thread, y sabe que mtodo run() tiene que ejecutar. Pero an no ha sucedido nada. Llegados a este punto tenemos un objeto Java de tipo Thread. No es an un hilo de ejecucin. Para tener una nueva pila, tenemos que ejecutar el hilo. Cuando un hilo ha sido instanciado pero no ejecutado (en otras palabras, el mtodo start() no ha sido invocado en la instancia de Thread), el hilo pasa a un nuevo estado. En esta etapa, el hilo no est an considerado alive. Una vez que start() se invoca, el hilo se le considera alive (aunque run() no se haya ejecutado aun). Un hilo se le considera dead (muerto) despus de que el mtodo run() se complete. El mtodo isAlive() es el mejor para determinar si un hilo se est ejecutando. El mtodo getState() es muy til para depurar el cdigo, no tienes que conocerlo para el examen pero si para poder debuguear tu cdigo.

email: brujulato@yahoo.es

Pgina 317

Curso gratuito de Java en emuladores L2 Java


21.3 Ejecutando un Thread

2012

Tienes un objeto Thread, y sabes su target (pasado por Runnable o porque hereda Thread). Ahora toca que todo el hilo se ejecute, se lance en una nueva pila. Es tan simple como esto:

Antes de llamar a start() en una instancia de Thread, al hilo se le dice que est en un nuevo estado, como dijimos antes. El nuevo estado significa que tenemos un objeto Thread pero an no est en marcha. As que, que pasa despus de invocar a start()?

Se crea un hilo de ejecucin (con su propia pila). El hilo cambia desde su estado new, al estado de runnable. Cuando el hilo tiene una oportunidad de ejecutarse, el mtodo run() se ejecuta.

Asegrate de recordar lo siguiente: Tu ejecutas un Thread, no un Runnable. Tu invocas el mtodo start() de la instancia Thread, no la instancia Runnable. El siguiente ejemplo demuestra lo que hemos cubierto hasta ahora, definir, instanciar y ejecutar un hilo:

Al ejecutar el codigo, imprime lo que esperabas:


% java TestThreads Runnable running Runnable running Runnable running Runnable running Runnable running

(Si esto no es lo que esperabas, revisa nuevamente todo lo que hemos hecho hasta ahora). As que, qu pasa si ejecutamos mltiples hilos? Bien, ejecutaremos un ejemplo sencillo en un momento, pero lo primero que necesitamos saber es como imprimir el hilo que se est ejecutando. Podemos usar el mtodo getName() de Thread, e imprimir en cada Runnable usando el mtodo run(). El siguiente ejemplo instancia un hilo y le da un nombre, y luego el nombre se imprime con el mtodo run():

email: brujulato@yahoo.es

Pgina 318

Curso gratuito de Java en emuladores L2 Java

2012

Al ejecutar este cdigo produce el siguiente resultado:


% java NameThread NameRunnable running Run by Fred

Para obtener el nombre del hilo que invocas, se usara getName() en la instancia de Thread. Pero el target del Runnable no tiene una referencia a esa instancia de Thread, as que lo primero que invocamos es el mtodo esttico Thread.currentThread(), que devuelve una referencia del hilo actual que se est ejecutando, y luego invocamos getName() en la referencia que nos devuelve. Aunque no le des un nombre explcitamente al hilo, tiene un nombre. Fjate en el cdigo anterior, vamos a poner como comentario, el estamento que selecciona el nombre del hilo:

y esto nos devuelve


% java NameThread NameRunnable running Run by Thread-0

Y ya que podemos conseguir el nombre del hilo actual usando el mtodo esttico Thread.currentThread(), podemos obtener el nombre del hilo de nuestro cdigo principal,

email: brujulato@yahoo.es

Pgina 319

Curso gratuito de Java en emuladores L2 Java

2012

lo que nos da
% java NameThreadTwo thread is main

As es, el hilo principal ya tiene un nombre, main. El siguiente grfico ilustra el proceso de ejecucin de un hilo:

Iniciando y ejecutando mltiples hilos Ya hemos jugado bastante, ahora vamos a hacer algo con mltiples hilos (eso es ms de dos). Antes tenamos dos hilos, porque el mtodo main() tiene el suyo propio, y luego t.start() crea otro nuevo. Ahora, haremos ms. El siguiente cdigo crea una instancia Runnable, y a cada hilo se le da un nombre. Finalmente, los tres hilos se inician invocando start() en las instancias de Thread.

email: brujulato@yahoo.es

Pgina 320

Curso gratuito de Java en emuladores L2 Java

2012

al ejecutar esto nos devuelve:


% java Run by Run by Run by Run by Run by Run by Run by Run by Run by ManyNames Fred, x is 1 Fred, x is 2 Fred, x is 3 Lucy, x is 1 Lucy, x is 2 Lucy, x is 3 Ricky, x is 1 Ricky, x is 2 Ricky, x is 3

Bien, al menos eso es lo que nos ha arrojado cuando lo hemos ejecutado, esta vez, en nuestra mquina. Pero el comportamiento que acabas de ver, no est garantizado. Esto es algo muy importante que no tienes que olvidar, deberas tomar un respiro, y pararte a memorizar "este comportamiento no esta garantizado". Necesitas saber, como futuro programador de Java y tambin para el examen, que no hay nada en las especificaciones de Java que diga que el hilo se iniciara en el orden que lo hemos ejecutado (en otras palabras, el orden en el que fue invocado cada hilo). Y tampoco hay garantas de que una vez el hilo se ejecute, mantenga la ejecucin hasta que se complete. O que un bucle se habr completado antes de que comience otro hilo. No hay nada garantizado en el cdigo excepto: Cada hilo se iniciar, y cada hilo se ejecutar hasta completarse Dentro de cada hilo, las cosas sucedern de una manera predecible. Pero las acciones de diferentes hilos pueden mezclarse de diferente manera. Si ejecutas el programa varias veces, o en mltiples maquinas, veras diferentes resultados. Aunque no tuvieses diferentes resultados, tienes que darte cuenta de que el comportamiento que ves, no est garantizado. Algunas email: brujulato@yahoo.es Pgina 321

Curso gratuito de Java en emuladores L2 Java

2012

veces un pequeo cambio en la manera en la que el programa se ejecuta causara que la diferencia aparezca. Solo por diversin hemos atacado 400 veces en lugar de 3, y comenzamos a ver algunas variaciones:

Al ejecutar el cdigo, cada hilo se ejecuta 400 veces, de tal manera que no se ejecuta de forma lineal. Aqu se muestra un pedazo de resultado arrojado a la consola:
Run Run Run Run Run Run Run Run Run Run Run Run Run Run Run Run Run by by by by by by by by by by by by by by by by by Fred, x is 345 Lucy, x is 337 Ricky, x is 310 Lucy, x is 338 Ricky, x is 311 Lucy, x is 339 Ricky, x is 312 Lucy, x is 340 Ricky, x is 313 Lucy, x is 341 Ricky, x is 314 Lucy, x is 342 Ricky, x is 315 Fred, x is 346 Lucy, x is 343 Fred, x is 347 Lucy, x is 344

etc... Fjate que no hay un patrn aqu, Si miramos solo la salida de Fred, vemos que los nmeros se incrementan de uno en uno, tal como se esperaba:
Run by Fred, x is 345 Run by Fred, x is 346 Run by Fred, x is 347

Y de la misma forma, si nos fijamos en Licy o Ricky. Cada uno, individualmente est teniendo el mismo comportamiento. Pero juntos, caos! En el fragmento de arriba, vemos Fred, luego Lucy y luego Ricky (en el mismo orden que el hilo comenz), pero luego Lucy se mete cuando era el turno de Fred. Que folln! Y luego Ricky y Lucy se vienen un rato mientras Fred se queda rezagado. Estn as durante un rato. Luego (lo mostramos arriba) Fred termina, luego Ricky y finalmente Lucy con una larga secuencia de salidas. As que aunque Ricky era el tercero, termino su tarea el segundo. Y si lo ejecutamos otra vez, tenemos otro resultado. Por qu? Porque depende del procesador de tareas, no existe un control para el procesador de tareas! Lo que me trae otro punto clave que hay que recordar, solo porque una serie de hilos comiencen en un orden en particular no quiere decir que ese orden se mantenga en la ejecucin. Para cualquier grupo que inicie hilos, el orden no est garantizado por el administrador de tareas. La duracin tampoco est garantizada. No sabes, por ejemplo, si un hilo se ejecutara para completarse antes de que los otros tengan una oportunidad de email: brujulato@yahoo.es Pgina 322

Curso gratuito de Java en emuladores L2 Java

2012

ejecutarse, o si ellos se turnaran para ejecutarse, o cualquier combinacin de ambos. Hay una manera, sin embargo, de iniciar un hilo y decirle que no se ejecute hasta que otro hilo haya terminado. Puedes hacer esto con el mtodo join() que veremos ms tarde.

Un hilo se completa cuando el target del mtodo run() se completa Cuando un hilo completa la tarea del mtodo run(), el hilo muere. La pila del hilo se disuelve. Pero aun que el objeto Thread, que no es un hilo de ejecucin. As que si haces referencia a una instancia Thread, aunque el hilo no est en ejecucin, puedes usar los mtodos de Thread, al igual que cualquier objeto de Java. Lo que no puedes hacer es llamar a start() otra vez.

Una vez que un hilo se ha iniciado, no se puede iniciar de nuevo.

Si tienes la referencia de un Thread, e invocas start(), se inicia. Si llamas a start() por segunda vez, causar una excepcin (IllegalThreadStateException, que es una tipo de RunTimeException). Esto ocurre si el mtodo run() ha completado o no, la primera llamada de start(). Solo se puede iniciar una sola vez el hilo. Un hilo runnable o un hilo muerto no pueden ejecutarse por segunda vez. Hasta ahora, hemos visto tres estados, new, runnable y dead. Ahora veremos ms estados.

El administrador de tareas El administrador de tareas para hilos es una parte de la JVM (aunque la mayora del mapeado de hilos de la JVM trabajan directamente con los hilos nativos del sistema operativo) es la que decide que hilo debe ser ejecutado en un momento dado, y tambin cambia el estado de ejecucin del hilo. Asumiendo que la mquina solo posea un procesador, solo se puede ejecutar un hilo al mismo tiempo. Solo una pila puede ser ejecutada a la misma vez. Y es el administrador de tareas el que decide cual es el hilo de todos los disponibles el que va a ejecutarse. Cuando decimos disponibles, nos referimos al estado de runnable. Cualquier hilo en el estado de runnable es suscitable de ser elegido por el administrador de tareas para ser el nico hilo que se est ejecutando. Si un hilo no est en estado runnable, entonces no podr ser elegido. El orden en que los hilos son elegidos para ejecutarse no est garantizado. Aunque el comportamiento de la cola es tpico, no est garantizada. El comportamiento de la cola quiere decir que cuando un hilo termina su turno, se mueve al final de la lnea de la piscina de ejecutables (runnable) y espera su turno para seguir ejecutndose. El hecho es que podemos llamarlo piscina de runnables en lugar de cola de runnables, para ayudar a reforzar que los hilos alineados no estn en ningn orden garantizado. Aunque no controlemos el calendario de tareas (no podemos decir por ejemplo, cual es el hilo que se va a ejecutar), podemos influenciar en el. El siguiente mtodo nos da algunas

email: brujulato@yahoo.es

Pgina 323

Curso gratuito de Java en emuladores L2 Java

2012

herramientas para influenciar al administrador de tareas. No cometas el error de confundir, influenciar, con tomar el control.

Mtodos de la clase java.lang.Thread Algunos de los mtodos que nos ayudaran a influenciar sobre el administrador de tareas son los siguientes:

Una nota, sleep() y join() tiene versiones sobrecargadas que no se muestran aqu.

Mtodos de la clase java.lang.Object Cada clase de Java hereda los siguientes mtodos:

El mtodo wait() tiene tres versiones sobrecargadas (incluyendo la que se muestra aqu). Vamos a mirar el comportamiento de cada uno de estos mtodos en este captulo, lo primero, vamos a mirar los diferentes estados que cada hilo puede tener.

21.4 El estado de los hilos y transiciones Ya hemos visto tres estados, new, runnable y dead. Pero espera! Hay ms! El trabajo del administrador de tareas es mover los hilos del estado de ejecucin al estado runnable. Otros factores pueden causar que un hilo deje de ser runnable, pero no volver a ser runnable. Uno de estos es cuando le mtodo run() se completa, en este caso el hilo pasa directamente al estado de dead. Ahora veremos las otras maneras en las que un hilo puede dejar el estado de ejecucin, y donde va.

email: brujulato@yahoo.es

Pgina 324

Curso gratuito de Java en emuladores L2 Java


Estado de los hilos Un hilo puede tener slo uno de los cinco estados en un determinado tiempo.

2012

New Este es el estado despus de que el hilo instanciado se haya creado, pero el mtodo start() no ha sido invocado todava. Es un objeto Thread vivo, pero aun sin ejecutar. En este punto el hilo se considera not alive. Runnable En este estado, el hilo puede ser elegido para ser ejecutado por el administrador de tareas, pero el administrador de tareas aun no lo ha elegido. El hilo primero entra en el estado de runnable cuando el mtodo start() se ha invocado, pero un hilo puede tambin volver al estado de runnable despus de haber estado ejecutndose, o volviendo del estado de espera/bloqueo, o sleep. Cuando el hilo est en estado runnable se le considera alive. Running Aqu es donde est la accin. Es el estado donde el hilo ha sido seleccionado por el administrador de tareas desde la piscina de runnables para entrar al proceso de ejecucin. Un hilo puede cambiar del estado de running a otro estado por varias razones. incluyendo porque "al administrador de tareas se le antoja". Bien, veremos esas razones en breve. Fjate en el grfico anterior, hay varias maneras de volver al estado runnable, pero solo una para ser running, el administrador de tareas lo elige de entre todos los posibles de la piscina de runnables. Waiting/blocked/sleeping Este estado se puede alcanzar cuando un hilo est disponible para ser ejecutado. Bien, as que estos son tres estados combinados en uno, pero todos ellos tienen algo en comn, el hilo esta aun alive, pero actualmente, no estn disponibles para ser ejecutados. En otras palabras, no estn en la piscina de runnables, pero pueden volver al estado ms tarde si algn evento en particular los activa. Un hilo puede ser bloqueado porque est esperando alguna fuente (como un objeto tipo I/O), en tal caso el evento lo enva de vuelta al estado runnable porque la fuente ya est disponible, por ejemplo, si un dato viene a travs de un input stream de donde se est leyendo un cdigo, o si el objeto bloqueado de repente est disponible. Un hilo puede estar sleeping porque el hilo donde corre se lo dice, durmete por un periodo de tiempo, en tal caso, el evento que lo enva de vuelta a runnable cuando el tiempo ha expirado. O el hilo puede estar waiting porque el cdigo lo dice as, en tal caso el evento que lo enva de vuelta a runnable es otro hilo que enva una notificacin, ya no tienes que seguir esperando. La cosa importante en un hilo es que no le dice a otro que se email: brujulato@yahoo.es Pgina 325

Curso gratuito de Java en emuladores L2 Java

2012

bloquee. Algunos mtodos puede parecer como si bloqueasen a otro hilo, pero no lo hacen. Si tienes una referencia de un hilo, puedes escribir algo como esto: t.sleep(); o t.yield() Pero estos mtodos de la clase Thread no afectan a la instancia t, en su lugar ellos se definen para afectar al hilo donde se ejecutan actualmente. (Esto es un buen ejemplo de por qu es una mala idea usar una variable de instancia para acceder a un mtodo esttico, se puede perder. Hay un mtodo, suspend() en la clase Thread, que nos permiten suspender uno o ms hilos, pero el mtodo suspend() est obsoleto y no entra en el examen (su contrapartida seria resume()). Hay tambin un mtodo stop(), pero tambin est obsoleto y no vamos ni tan siquiera a verlo. Ambos, suspend() y stop() se volvieron peligrosos, as que mejor no los uses, porque aparte de estar obsoletos, no entran en el examen. No los estudies, no los uses. Fjate, que los hilos en estado blocked se les considera alive.

Dead Un hilo se le considera dead cuando su mtodo run() se ha completado. Puede estar todava viable el objeto Thread, pero ya no est separado del hilo principal de ejecucin. Una vez que el hilo est muerto, no puede ser devuelto a la vida! Si invocas start() en un una instancia de un hilo muerto, conseguirs una excepcin en tiempo de ejecucin.

A tener en cuenta en las ejecuciones de los hilos Cuando un hilo ha sido detenido, normalmente significa que su estado ha pasado a ser dead. Pero en nuestro objetivo tambin se evala la habilidad para reconocer cuando un hilo va a ser expulsado del estado de running, no pero se te va a pedir el reconocer cuando un hilo vuelve a ser runnable. Para el propsito, no nos vamos a centrar en un hilo bloqueado de I/O (dicho de otro modo, esperando a que algo llegue desde el servidor). Nos vamos a concentrar en lo siguiente:

Sleeping Waiting Blocked porque necesita un objeto que lo desbloquee

Sleeping El mtodo sleep() es un mtodo esttico de la clase Thread. Puedes usarlo en tu cdigo para forzar a un hilo a pasar al modo sleep despus de que haya estado en modo runnable. Cuando un hilo duerme, se desva a alguna parte y no vuelve al estado runnable hasta que se despierta. As que, por qu querramos poner un hilo a dormir? Bien, puedes pensar que el hilo se est moviendo demasiado rpido en su cdigo. O puedes necesitar forzar al hilo a que pase un turno, ya que Java no garantiza el orden de ejecucin de los hilos. O imagina que un hilo esta email: brujulato@yahoo.es Pgina 326

Curso gratuito de Java en emuladores L2 Java

2012

ejecutndose en un bucle, descargando los ltimos stocks de precios y los est analizando. La descarga de precios uno despus del otro podra ser una prdida de tiempo, al igual que muchas cosas similares, e incluso ms importantes, podra ser increble el desperdicio del ancho de banda. La manera ms simple de resolver esto es poner el hilo a dormir cinco minutos despus de cada descarga. Puedes hacer esto invocando el mtodo esttico Thread.sleep() dndole el tiempo en milisegundos, tal que as:

Fjate que sleep puede arrojar una excepcin comprobada (sabrs esto porque si hay una posibilidad, ya que otro hilo puede interrumpir esta explcitamente), as que debes saber que tienes que declarar o manejar esta excepcin. Lo normal es envolver la invocacin entre un try/catch. Volvamos a modificar nuestro cdigo Fred, Lucy, Ricky usando sleep() para forzar a los hilos a alternar entre ellos permitiendo a uno dominar por un periodo de tiempo. Donde crees que debera ir el mtodo sleep?

Este cdigo muestra como alterna a Fred, Lucy y Ricky:


% java Run by Run by Run by Run by Run by Run by Run by Run by Run by ManyNames Fred Lucy Ricky Fred Lucy Ricky Fred Lucy Ricky

email: brujulato@yahoo.es

Pgina 327

Curso gratuito de Java en emuladores L2 Java

2012

Recuerda que el comportamiento de este cdigo, todava no est garantizado. No puedes saber a ciencia cierta cuanto tiempo tardar un hilo en ejecutarse antes de conseguir esta salida, as que no puedes saber con certeza que slo uno de los tres hilos estar en runnable cuando el hilo se vaya a dormir. En otras palabras, si hay dos hilos despiertos en la piscina de runnables, no podrs saber con certeza que el ltimo hilo usado ser el siguiente seleccionado para ejecutarse. Aun, usando sleep() es la mejor manera para ayudar a todos los hilos para que tengan ocasin de ejecutarse! O al menos de garantizar que uno de ellos no va a entrar hasta que est preparado. Cuando un hilo encuentra una invocacin para dormir, debe ir a dormir al menos por una cantidad de milisegundos especificada (a menos que sea interrumpido antes, en tal caso arrojara inmediatamente un InterruptedException). Atencin, solo porque el tiempo de sleep expire y se despierte, no quiere decir que inmediatamente va a ser ejecutado (running). Recuerda, cuando un hilo se despierta pasa al estado de runnable. As que el tiempo especificado en sleep() es la duracin mnima en la que el hilo no va a ejecutarse, pero no es la duracin exacta en la que el hilo va a estar sin ejecutarse. As que por ejemplo, no puedes darle al mtodo sleep() un tiempo exacto y confiar en el. Aunque en muchas aplicaciones se use sleep() como temporizador, realmente no es suficiente, debes saber que sleep() no garantiza el tiempo en que un hilo comenzar a ejecutarse.

Prctica Crear un hilo y ponerlo a dormir. En esta prctica vamos a crear un simple hilo. Contar hasta 100, con una pausa de un segundo entre cada numero. Tambien, mantendra la cuenta, y cada diez numeros mostrara un mensaje. 1. Crea una clase que herede Thread. Como opcin puedes implementar la interfaz Runnable. 2. Sobrescribe el mtodo run() de Thread. Aqu es donde ir el cdigo que mostrara los nmeros. 3. Crea un bucle for que funcione 100 veces. Usa el mdulo de la divisin para evaluar si hay ms nmeros divisibles entre 10. 4. Usa el mtodo esttico Thread.sleep() para hacer la pausa. Un numero long representa los milisegundos.

Solucin:

email: brujulato@yahoo.es

Pgina 328

Curso gratuito de Java en emuladores L2 Java

2012

que devuelve:
Hey! 0 Iniciando Hey! 10 Iniciando Hey! 20 Iniciando Hey! 30 Iniciando Thread-0 Thread-0 Thread-0 Thread-0

Prioridades del hilo y yield() Para entender yield() tienes que entender el concepto de prioridad de los hilos. Los hilos se ejecutan siempre en alguna prioridad, normalmente representados por un nmero entre 1 y 10 (aunque el algunos casos en rango no llega a 10). El administrador de tareas en la mayora de los casos con JVM usa una preventiva, prioridad basada en la tarea (lo cual implica alguna clase de corte en la tarea). Esto no quiere decir que JVM use el tiempo cortado (es decir, una tarea que para terminar se ejecuta y se detiene para alternar ejecuciones). Las especificaciones de JVM no requieren una VM para implementar una tarea de tipo de corte, donde a cada hilo se le asigna una cantidad de tiempo determinada para ejecutarse. Aunque muchas JVMs usan el tiempo cortado, algunas pueden usar una agenda que permitan un hilo estar ejecutndose hasta completar su mtodo run().

email: brujulato@yahoo.es

Pgina 329

Curso gratuito de Java en emuladores L2 Java

2012

En la mayora de las JVMs, sin embargo, el administrador de tareas usa prioridad en los hilos de una forma muy importante: Si un hilo entra en estado runnable, y tiene una prioridad ms alta que el hilo que cualquier hilo de la piscina de runnables, los hilos de menor prioridad son puestos al final de la cola y los hilos ms altos tienen mejor opcin para ser ejecutados. En otras palabras, en cualquier momento dado, del hilo que se est ejecutando actualmente, normalmente no tendr una prioridad ms baja que cualquiera de los hilos que estn en la piscina. En la mayora de los casos, el hilo que se est ejecutando ser igual o mayor su prioridad que los que estn en la piscina. Esto es lo ms cerca de una garanta que tendrs sobre que un hilo se ejecute antes que otro, as que nunca relegues en la prioridad de los hilos para el correcto funcionamiento de un programa. Lo que tampoco est garantizado es el comportamiento cuando los hilos de la piscina tienen la misma prioridad, o cuando el hilo que se est ejecutando tiene la misma prioridad que los que estn en la piscina. Cuando todas las prioridades son iguales, el administrador de tareas es libre de hacer lo que le plazca. Esto quiere decir que el administrador de tareas podra hacer una de estas cosas (entre otras):

Coger un hilo para ejecutarlo, y que se est ejecutando hasta que termine o se bloquee. Partir el tiempo de los hilos para darles a cada uno la oportunidad de ejecutarse.

Seleccionando la prioridad de los hilos Un hilo toma una prioridad por defecto cuando se crea el hilo de ejecucin. Por ejemplo:

el hilo referenciado por t tendr la misma prioridad que el hilo principal, ya que el hilo principal se est ejecutando el cdigo que crea la instancia de MyThread. Tambin puedes seleccionar la prioridad de un hilo directamente invocando el mtodo setPriority() en la instancia de Thread:

La prioridad se selecciona con un numero positivo, normalmente entre 1 y 10, y la JVM jams cambiara la prioridad. Sin embargo, los valores entre 1 y 10 no estn garantizados. Algunas JVMs no reconocen 10 valores. Tal como una JVM puede tener de 1 a 10, otras pueden tener de 1 a 5, as que si tienes, digamos, diez hilos con prioridades distintas, y la aplicacin est email: brujulato@yahoo.es Pgina 330

Curso gratuito de Java en emuladores L2 Java

2012

ejecutndose en una JVM que reconoce 5 prioridades, entonces dos o ms hilos pueden ser mapeados para tener una prioridad. Aunque la prioridad por defecto es 5, la clase Thread tiene tres constantes (static final variables) que definen el rango de la prioridad de los hilos:

El mtodo join() El mtodo no esttico join() de la clase Thread nos permite unir un hilo al final de otro hilo. Si tienes un hilo B que no puede funcionar hasta que otro hilo haya completado su tarea, entonces querrs unir el hilo B al hilo A. Esto quiere decir que B no ser runnable hasta que A haya terminado (y haber pasado al estado de muerto).

El cdigo anterior toma el hilo actual (si fuera en el mtodo main(), entonces sera el hilo principal) y lo une al final del hilo referenciado por t. Esto bloquea el hilo actual para no pasar al estado de runnable hasta que el hilo referenciado por t est muerto. En otras palabras, el cdigo t.join() significa "neme (el hilo actual) al final de t, as que cuando t haya terminado, yo pueda seguir ejecutndome". Tambin puedes llamar a la versin sobrecargada de join() que toma una duracin de tiempo, as que le puedes decir "espera hasta que t haya terminado, pero si toma mas de 5.000 milisegundos, entonces deja de esperar y ejectate de todos modos". El siguiente grafico ilustra el efecto del mtodo join():

email: brujulato@yahoo.es

Pgina 331

Curso gratuito de Java en emuladores L2 Java

2012

As que lo que hemos visto hasta ahora, han sido tres formas de ejecutar un hilo que pudo dejar su estado running debido a :

Una llamada a sleep(). Esta garantizado parar la ejecucin del hilo al menos por el tiempo especificado (aunque puede ser interrumpido antes del tiempo especificado).

Una llamada a yield(). No se garantiza del todo, aunque lo normal es que cause que el hilo actual vuelva al estado de runnable para darle una oportunidad al hilo de poder ser ejecutado.

Una llamada a join(). Se garantiza que el hilo actual dejara de ejecutarse hasta que el hilo al que se une (en otras palabras, el hilo al que invoca join()) complete su mtodo run(), o si el hilo no est vivo, sin embargo, el hilo actual no necesita regresar.

Adems de estos tres, tambin tenemos los siguientes escenarios, en los cuales un hilo puede dejar el estado de running:

El mtodo run() se completa.

email: brujulato@yahoo.es

Pgina 332

Curso gratuito de Java en emuladores L2 Java

2012

Una llamada a wait() en un objeto (no invocando a wait() en un hilo, lo veremos en un momento). Un hilo que no puede desbloquear el objeto de cuyos mtodos el cdigo est intentando ejecutar. El administrador de tareas decide mover ese hilo para dar la oportunidad a otro de ejecutarse. No hay razn, es necesario, el administrador de tareas puede administrar los hilos de la manera ms conveniente.

21.5 Sincronizando cdigo Puedes imaginarte el apocalipsis que puede ocurrir cuando dos hilos de diferentes clases tienen acceso a una nica instancia de una clase, y ambos hilos invocan mtodos en ese objeto... y estos mtodos modifican el estado del objeto. En otras palabras, que puede pasar si dos hilos distintos invocan, digamos un mtodo setter en un nico objeto? Un escenario como este puede corromper cualquier objeto (modificando sus variables de instancia de manera inconsistente) y si el estado del objeto es un dato compartido por otras partes del programa, bien... da miedo imaginarse el resultado. Pero tan solo porque disfrutamos con el terror, vamos a ver un ejemplo de lo que puede suceder. El siguiente cdigo demuestra lo que ocurre cuando dos hilos distintos acceden al mismo dato de una cuenta. Imagina que dos personas tienen un libro para comprobar cuentas bancarias (o dos personas que tienen tarjetas de crdito, pero ambas tarjetas estn unidas a la misma cuenta). En este ejemplo tenemos una clase Account que representa la cuenta del banco. Para mantener el cdigo en orden, esta cuenta empieza con un balance positivo de 50, y puede ser usado solo para reintegros en cajeros. El reintegro ser aceptado incluso si no hay suficiente dinero para cubrirlo. La cuenta simplemente resta el saldo de la cuenta por el dinero reintegrado:

Ahora viene la diversin. Imagnate una pareja, Fred y Lucy, que tienen acceso a una cuenta y quieren hacer reintegros. Pero ellos no quieren que la cuente se quede en rojo, as que uno de ellos hace un reintegro y el otro antes de hacer la gestin, comprueba el saldo. Tambin, los reintegros estn limitados a 10, as que por lo menos deben de quedar 10 en la cuenta para que el balance no sea negativo. Suena razonable. Pero es un proceso de dos pasos:

email: brujulato@yahoo.es

Pgina 333

Curso gratuito de Java en emuladores L2 Java

2012

1. Evala el saldo. 2. Si hay suficiente en la cuenta (en este ejemplo, al menos 10), se efecta el reintegro. Qu ocurre si alguien separa el paso 1 del paso 2? Por ejemplo, imagina que pasara si Lucy mira el saldo y ve que hay lo justo en la cuenta, 10. Pero despus de que haga el reintegro, Fred mira el saldo y ve que hay suficiente para su reintegro. Ya que Lucy ha verificado el saldo, pero an no ha hecho su reintegro, Fred est viendo datos errneos (bad data). Est viendo la cuenta antes de que Lucy haya hecho efectivo el reintegro. Mientras Lucy va a sacar el dinero, Fred est viendo que hay disponible para l tambin. As que imagnate que Lucy efecta su reintegro, y ahora no hay bastante para Fred, pero l piensa que si hay. Ais... En un minuto veremos el cdigo del banco con Fred y Lucy representados por dos hilos, cada accin en el mismo Runnable, y ese Runnable mantiene una referencia a uno y solo a una nica instancia de la cuenta, as que, dos hilos, una cuenta. La lgica de nuestro ejemplo es como sigue: 1. El objeto Runnable mantiene la referencia a una nica cuenta. 2. Los dos hilos se inician, representado a Lucy y Fred, y a cada hilo se le da una referencia del mismo Runnable (el cual mantiene la referencia de la misma cuenta). 3. El saldo inicial es de 50, cada uno saca exactamente 10. 4. En el mtodo run(), vamos a crear un bucle de 5 repeticiones donde o Se har un reintegro (si hay suficiente en la cuenta). o Imprime un estamento si la cuenta esta sin fondos (lo cual, nunca debera suceder, ya que miraremos antes si el balance no ser negativo). 5. El mtodo makeWithdrawal() en la clase Test (representando el comportamiento de Fred o Lucy) har lo siguiente: o Evaluar el balance para ver si hay suficiente para un reintegro. o Si hay suficiente, imprimir el nombre de uno de los dos haciendo el reintegro. o Ir a dormir 500 milisegundos, solo para darle una oportunidad al otro compaero de entrar antes de que se efectu el reintegro. o Despertar, completar el reintegro e imprimirlo. o Si no hay suficiente en primer lugar, imprime un estamento mostrando quien eres y que en efecto, no haba bastante. As que estamos intentando descubrir si lo siguiente es posible: para un compaero evaluar la cuenta y ver que hay bastante saldo, pero antes de hacer el reintegro, el otro compaero verifica la cuenta y ve que hay bastante para la transaccin, pero despus de hacer el reintegro, el otro compaero verifica la cuenta y ve que hay bastante. Cuando la cuenta llegue a 10, si ambos compaeros verifican antes de hacer el reintegro, ambos vern que es correcto, y querrn sacar 10 cada uno quedando realmente, -10 de saldo. Este es el cdigo:

email: brujulato@yahoo.es

Pgina 334

Curso gratuito de Java en emuladores L2 Java

2012

As que, qu ha pasado? Es posible que, por ejemplo, Lucy evalu el saldo, se quede dormida, Fred evalu el saldo, Lucy se despierte y complete su reintegro, entonces Fred completa su reintegro, y al final, ellos dejaran la cuenta en saldo negativo. Mira la salida:
% java AccountDanger 1. Fred is going to withdraw 2. Lucy is going to withdraw 3. Fred completes the withdrawal 4. Fred is going to withdraw 5. Lucy completes the withdrawal 6. Lucy is going to withdraw 7. Fred completes the withdrawal 8. Fred is going to withdraw 9. Lucy completes the withdrawal 10. Lucy is going to withdraw 11. Fred completes the withdrawal 12. Not enough in account for Fred to withdraw 0 13. Not enough in account for Fred to withdraw 0 14. Lucy completes the withdrawal 15. account is overdrawn! 16. Not enough in account for Lucy to withdraw -10

email: brujulato@yahoo.es

Pgina 335

Curso gratuito de Java en emuladores L2 Java


17. account is overdrawn! 18. Not enough in account for Lucy to withdraw -10 19. account is overdrawn!

2012

Aunque cada vez que ejecutes este cdigo, la salida podra ser distinta, vamos a examinar este particular ejemplo usando las lneas numeradas de la salida. Para empezar, los primeros intentos van bien. Fred mira su cuenta y el saldo en la lnea 1 y lo ve bien. En la lnea 2, Lucy tambin lo ve bien. En la lnea 3, Fred efecta su reintegro. En este punto el saldo evaluado por Lucy (que piensa estar bien) ha cambiado desde la ltima vez que lo evalu. Y ahora le toca a Fred que evala otra vez el saldo, antes incluso, Lucy completa su primer reintegro. En este punto, incluso Fred est viendo un saldo potencialmente errneo, porque nosotros sabemos que Lucy va a completar su reintegro. Es posible, naturalmente, que Fred complete el reintegro antes que Lucy lo haga, pero esto no suceder aqu. En la lnea 5, Lucy completa su reintegro y luego Fred lo completa, antes que la siguiente evaluacin de Lucy en la lnea 6. As contina hasta llegar a la lnea 8, donde Fred evala el balance y ve que es 20. En la lnea 9, Lucy completa su reintegro (justo lo que verific antes), y este da un balance de 10. En la lnea 10, Lucy verifica la cuenta otra vez, mira el balance, es 10, as que ella sabe que puede hacer otro reintegro. Pero ella no saba que Fred ya haba verificado el saldo en la lnea 8, as que ella piensa que no hay problema en hacer el reintegro! En la lnea 11, Fred completa el reintegro aprobado en la lnea 8. Esto lleva el saldo a cero. Pero Lucy an tiene pendiente un reintegro que aprob en la lnea 10! Ya sabes lo que va a pasar. En la lnea 12 y 13, Fred verifica el saldo y encuentra que no hay suficiente en la cuenta, pero en la lnea 14 Lucy completa su reintegro y ZAS! La cuenta en rojo, -10, algo que esperbamos que pudiese pensar, lo prevenimos evaluando el saldo antes de hacer el reintegro. El siguiente grfico muestra la lnea de tiempo de lo que puede pasar cuando dos hilos concurrentes acceden al mismo objeto.

Este problema conocido como "carrera de condicin", donde mltiples hilos pueden acceder al mismo recurso (tpicamente la variable de instancia de un objeto), y que puede producir datos

email: brujulato@yahoo.es

Pgina 336

Curso gratuito de Java en emuladores L2 Java

2012

corruptos si un hilo en la carrera es demasiado rpido antes de que una operacin "atmica" se haya completado.

Prevenir los nmeros rojos en la cuenta As que, que vamos a hacer? La solucin es muy simple. Tenemos que garantizas que los dos pasos del reintegro, la verificacin del saldo y el reintegro, no se dividen. Necesitamos siempre ejecutarlos como una operacin, aunque nos caigamos dormidos en el paso 1 y 2! A esto lo llamamos operacin atmica (aunque la fsica est un poco desfasada, en este caso, atmica significa, indivisible) porque la operacin, no importa el nmero de estamentos (o cdigo de fondo) se completa antes de que otro hilo actu en el mismo dato. No puedes garantizar que un solo hilo este corriendo en toda la operacin atmica. Pero puedes garantizar que aunque el hilo atmico que se est ejecutando no se haya completado, ningn otro hilo puede tener acceso al mismo dato. En otras palabras, si Lucy cae dormida despus de verificar su saldo, podemos parar los pies a Fred para que no pueda verificar el saldo hasta que Lucy se haya despertado y complete el reintegro. As que como protegemos el dato? Debes hacer dos cosas:

Marcar las variables como privadas (private). Sincronizar el cdigo que modifica las variables.

Recuerda proteger las variables de manera normal, usando un modificador. Este es el cdigo que debes proteger, as que solo un hilo a la vez puedan ejecutarlo. Debes hacer esto con la palabra clave synchronized. Podemos resolver todos los problemas de Lucy y Fred aadiendo una palabra al cdigo. Marcaremos los mtodos makeWithdrawal() sincronizados como sigue:

email: brujulato@yahoo.es

Pgina 337

Curso gratuito de Java en emuladores L2 Java

2012

Ahora hemos garantizado que una vez que un hilo (Lucy o Fred) comienzan un reintegro (invocando makeWithdrawal()), el otro hilo no puede entrar hasta que ese mtodo complete el proceso. La nueva salida muestra los beneficios de la sincronizacin:
% java AccountDanger Fred is going to withdraw Fred completes the withdrawal Lucy is going to withdraw Lucy completes the withdrawal Fred is going to withdraw Fred completes the withdrawal Lucy is going to withdraw

email: brujulato@yahoo.es

Pgina 338

Curso gratuito de Java en emuladores L2 Java


Lucy completes the withdrawal Fred is going to withdraw Fred completes the withdrawal Not enough in account for Lucy Not enough in account for Fred Not enough in account for Lucy Not enough in account for Fred Not enough in account for Lucy

2012

to to to to to

withdraw withdraw withdraw withdraw withdraw

0 0 0 0 0

Fjate que ahora ambos hilos, Lucy y Fred, siempre verifican la cuenta y completan el reintegro antes de que el otro hilo pueda evaluar el saldo.

Sincronizacin y candados Como funciona una sincronizacin? Con candados. Cada objeto en Java tiene un candado que entra en juego cuando el objeto tiene un mtodo sincronizado. Cuando entramos a un mtodo sincronizado no esttico, automticamente adquirimos el candado asociado con la instancia de la clase cuyo cdigo estamos ejecutando (la instancia this). Adquirir un candado para un objeto tambin se le denomina, conseguir un candado/cerrojo, bloquear un objeto, o sincronizar el objeto. Podemos usar el trmino monitor para referirnos al objeto cuya cerradura hemos adquirido. Tcnicamente la cerradura y el monitor son cosas distintas, pero la mayora de la gente habla de los dos indistintamente, nosotros lo haremos tambin. Ya que solo existe un candado por objeto, si un objeto tiene el candado, otro hilo no puede coger el candado hasta que el primero lo suelte (o devuelva) el candado. Esto significa que otro hilo pueda entrar al cdigo sincronizado (lo que significa que no puede entrar a ninguna parte sincronizada de ese objeto) hasta que el candado haya sido soltado. Normalmente, soltar un candado significa que el hilo tiene el candado, es decir, que el hilo actual en el mtodo sincronizado sale del mtodo sincronizado. En este punto, el candado es libre hasta que otro hilo entre al mtodo sincronizado de ese mtodo. Recuerda que la clave de la sincronizacin es el candado:

Solo mtodos (o bloques) pueden ser sincronizados, ni las variables ni las clases. Cada objeto tiene solo un candado. No todos los mtodos en una clase tiene que ser sincronizados. Una clase puede tener hilos synchronized e hilos no sincronizados. Si dos hilos estn a punto de ejecutar un mtodo sincronizado en una clase, y ambos hilos estn invocando al hilo a la misma vez, solo un hilo podr ejecutar el mtodo. El otro mtodo tendr que esperar hasta que el primero termine su invocacin. En otras palabras, una vez que un hilo adquiere el candado de un objeto, otro hilo no podr entrar a ningn mtodo sincronizado de esa clase (de ese objeto). Si una clase tiene mtodos sincronizados y no sincronizados, mltiples hilos podrn acceder a sus mtodos no sincronizados. Si tienes mtodos que no acceden a los datos que estas intentando proteger, entonces no necesitas protegerlos porque ellos no necesitan ser protegidos. La sincronizacin puede causar un revs en algunos casos (o incluso deadlock si se usa incorrectamente), as que tendrs que tener cuidado de no sobreusarlos. Si un hilo se va a dormir, mantiene su candado, no lo suelta. email: brujulato@yahoo.es Pgina 339

Curso gratuito de Java en emuladores L2 Java

2012

Un hilo puede adquirir ms de un candado. Por ejemplo, un hilo puede entrar a un mtodo sincronizado y adquirir un candado, luego invoca un mtodo sincronizado en otro objeto, as que adquiere el candado tambin. Tan pronto como termine, suelta los candados. Tambin, si un mtodo adquiere un candado y entonces intenta invocar a otro mtodo sincronizado del mismo objeto, no hay problema. La JVM sabe que este hilo tiene el candado de este objeto, as que el hilo es libre de llamar a todos los mtodos sincronizados del mismo objeto usando el candado que ya posee. Puedes sincronizar un bloque de cdigo adems de un mtodo.

Ya que la sincronizacin daa la concurrencia, no vas a querer sincronizar el cdigo ms de lo necesario para proteger tus datos. As que si el rango de un mtodo es ms del que necesita, puedes reducir su vida sincronizando parte del mtodo en lugar de todo el mtodo, solo un bloque. A esto lo llamamos, extraamente, un bloque sincronizado, y se parece a esto:

Cuando en un hilo se ejecuta cdigo de un bloque sincronizado, incluyendo cualquier mtodo invocado de ese mtodo sincronizado, al cdigo se le dice que se ejecuta en un contexto sincronizado. La pregunta real es, sincronizado dnde? O sincronizado en el candado de que objeto? Cuando sincronizas un mtodo, el objeto usado para invocar el mtodo es el objeto cuyo candado debe ser adquirido. Pero cuando sincronizas un bloque de cdigo, especificas el objeto que quieres usar como candado, as por ejemplo, podras usar un tercer objeto como candado para esa pieza de cdigo. Eso te da la posibilidad de tener ms de un candado para el cdigo sincronizado en un solo objeto. Tambin puedes sincronizar la instancia actual (this) en el cdigo de arriba. Ya que esa es la misma instancia que la del mtodo sincronizado, significa que puedes siempre reemplazar un mtodo sincronizado con un mtodo no sincronizado contenido en el mtodo sincronizado, en otras palabras, esto:

es lo mismo que esto:

email: brujulato@yahoo.es

Pgina 340

Curso gratuito de Java en emuladores L2 Java

2012

Estos mtodos tienen exactamente el mismo efecto, en trminos prcticos. Los bytecodes compilados pueden no ser exactamente los mismos en los dos mtodos, pero podran serlo, y cualquier diferencia no es realmente importante. La primera forma es ms corta y ms familiar para la mayora de la gente, pero la segunda es ms flexible. Y que hay de los mtodos estticos? Pueden ser sincronizados? Los mtodos estticos pueden ser sincronizados. Solo hay una copia del dato esttico que estas intentando proteger, as que solo necesitas un candado por clase para sincronizar los mtodos estticos, un candado para toda la clase. Esta clase de candado existe, cada clase cargada tiene su correspondiente instancia de java.lang.Class representado esa clase. Esa es la instancia java.lang.Class cuyo candado se usa para proteger los mtodos estticos de la clase (si estn sincronizados). No hay nada especial que tengas que hacer para sincronizar ese mtodo esttico:

Nuevamente, esto podra ser reemplazado con cdigo que use un bloque sincronizado. Si el mtodo est definido en una clase llamada MyClass, el equivalente seria:

Tiempo muerto! Que es esa cosa "MyClass.class"? A eso se le llama una clase literal. Es una caracterstica especial de Java que le dice al compilador (que le dice a la JVM): ve a buscarme la instancia de Class que representa la clase invocada MyClass. Puedes hacer esto tambin con este cdigo:

email: brujulato@yahoo.es

Pgina 341

Curso gratuito de Java en emuladores L2 Java

2012

Sin embargo esto no entra en el examen de SCJP. Pero es fcil y rpido usar una clase literal, solo escribir el nombre de la clase y aadir el .class al final. No se necesitan comillas. Ahora que tienes una expresin para el objeto Class que necesitas sincronizar.

Prctica, sincronizando un bloque de cdigo En este ejercicio intentaremos sincronizar un bloque de cdigo. En el bloque de cdigo conseguiremos un candado en un objeto, as que otros hilos no podrn modificarlo mientras el bloque se est ejecutando. Se crearan tres hilos que intentaran manipular el mismo objeto. Cada hilo mostrara una simple letra 100 veces y luego incrementaran la letra en una. El objeto usara StringBuffer. Podramos sincronizar un objeto String, pero las cadenas no pueden ser modificadas una vez que son creadas, as que no se podra incrementar la letra sin generar un nuevo objeto String. La salida final debera tener 100 Aes, 100 Bes, y 100 Ces, todo alineado. 1. Crea una clase que herede Thread. 2. Sobrescribe el mtodo run() de Thread. Aqu es donde ira el bloque sincronizado. 3. Los tres hilos comparten el mismo objeto, necesitaremos crear un constructor que acepte un StringBuffer como argumento. 4. El bloque sincronizado obtendr el candado en el StringBuffer en el paso 3. 5. Dentro del bloque, saldr 100 veces el StringBuffer y luego incrementara la letra en el StringBuffer. Puedes mirar el captulo 5 para recordar los mtodos de StringBuffer. 6. Finalmente, en el mtodo main(), crea un objeto StringBuffer usando la letra A, luego crea tres instancias de nuestra clase y luego usa el mtodo start() para iniciarlas.

email: brujulato@yahoo.es

Pgina 342

Curso gratuito de Java en emuladores L2 Java

2012

La consola devuelve:
... Hilo: Hilo 1 Valor de sb:A Valor de x:95 Hilo: Hilo 1 Valor de sb:A Valor de x:96 Hilo: Hilo 1 Valor de sb:A Valor de x:97 Hilo: Hilo 1 Valor de sb:A Valor de x:98 Hilo Hilo 1 terminado! Ahora sb vale B Hilo: Hilo 3 Valor de sb:B Valor de x:0 Hilo: Hilo 3 Valor de sb:B Valor de x:1 Hilo: Hilo 3 Valor de sb:B Valor de x:2 ... Hilo: Hilo 3 Valor de sb:B Valor de x:97 Hilo: Hilo 3 Valor de sb:B Valor de x:98 Hilo Hilo 3 terminado! Ahora sb vale C Hilo: Hilo 2 Valor de sb:C Valor de x:0 Hilo: Hilo 2 Valor de sb:C Valor de x:1

email: brujulato@yahoo.es

Pgina 343

Curso gratuito de Java en emuladores L2 Java


... Hilo: Hilo 2 Valor de sb:C Valor de x:97 Hilo: Hilo 2 Valor de sb:C Valor de x:98 Hilo Hilo 2 terminado! Ahora sb vale D

2012

Si no sincronizo el mtodo, se obtiene:


... Hilo: Hilo: Hilo: Hilo: Hilo: Hilo: Hilo: ... Hilo Hilo Hilo Hilo Hilo Hilo Hilo 2 1 3 2 1 3 2 Valor Valor Valor Valor Valor Valor Valor de de de de de de de sb:A sb:A sb:A sb:A sb:A sb:A sb:A Valor Valor Valor Valor Valor Valor Valor de de de de de de de x:3 x:3 x:22 x:4 x:4 x:23 x:5

Con lo que puedo confirmar que el ejercicio es correcto.

Qu ocurre si un hilo no consigue el candado? Si un hilo intenta entrar a un mtodo sincronizado y el candado ya ha sido tomado, el hilo se dice que est bloqueado en el candado del objeto. Realmente el hilo va a una especia de piscina de un objeto en particular y all se sienta a esperar a que el candado sea liberado y el hilo pueda convertirse en Runnable. Que el candado sea liberado no quiere decir que ese hilo en particular lo consiga. Puede haber tres hilos esperando para el mismo candado, por ejemplo, no hay garantizas de que el hilo haya esperado ms que el primero. Cuando pienses en bloqueos, es importante prestar atencin a los objetos que estn siendo bloqueados.

La invocacin de hilos no estticos sincronizados en la misma clase solo bloquearan a otro hilo si estn invocados usando la misma instancia. Eso ocurre porque cada candado tiene su propio candado en this, y si son invocados desde instancias diferentes, se consiguen distintos candados que no interfieren el uno con el otro. La invocacin a hilos estticos sincronizados en la misma clase siempre bloquearan al otro, los candados pertenecen a la misma instancia de Class. Un mtodo sincronizado y no esttico no bloqueara a otro nunca. Los mtodos estticos se bloquean en una instancia de la clase mientras el mtodo no esttico bloquee la instancia this, estas acciones no interfieren con otra de ningn modo. Para los bloques sincronizados, tienes que mirar exactamente que objeto est siendo usado para bloquear. (Lo que est dentro de los parntesis despus de la palabra synchronized). Los hilos que sincronizan sobre el mismo objeto se bloquearan el uno al otro. Los hilos que estn sincronizados en diferentes objetos no.

La siguiente tabla muestra la relacin entre mtodos y si el hilo abandona si candado como resultado de la invocacin:

email: brujulato@yahoo.es

Pgina 344

Curso gratuito de Java en emuladores L2 Java

2012

Cuando es necesario sincronizar?


La sincronizacin puede ser bastante complicada, y puedes preguntarte porque quieres hacer esto despus de todo, puede ayudarte. Pero recuerda antes "la carrera de condiciones" con el ejemplo de Lucy y Fred haciendo reintegros desde sus cuentas. Cuando usamos hilos, normalmente necesitamos usar algunas sincronizaciones en alguna parte para asegurarnos de que nuestros mtodos no se interrumpen entre ellos de manera equivocada corrompiendo nuestros datos. Normalmente, en cualquier momento ms de un hilo tiene acceso a un dato mutable (que puede cambiar), sincronizas ese dato para protegerlo, para asegurarte de que dos hilos no van a cambiarlo al mismo tiempo (o que uno no est cambindolo a la misma vez que el otro lo est leyendo, que tambin brinda confusiones). No necesitas preocuparte de las variables locales, cada hilo tiene su propia copia de las variables locales. Dos hilos ejecutando el mismo mtodo a la misma vez usaran diferentes copias de la variable local, y no se molestarn mutuamente. Sin embargo, tienes que preocuparte de los campos estticos y no estticos, si en ellos hay datos que pueden ser alterados. Para un dato alterable en un campo no esttico, normalmente usaras un mtodo no esttico para acceder a el. Al sincronizar ese mtodo, te aseguras de que cualquier hilo que intente ejecutar ese mtodo usando la misma instancia, as previenes accesos simultneos. Pero a un hilo que funciona con una instancia distinta no le afectara, porque adquiere el candado de otra instancia. Eso es lo que queremos, hilos que funcionen con diferentes datos pueden ignorarse entre si y ejecutarse si quieren, no importa. Para un dato alterable en un campo esttico, normalmente usaras mtodos estticos para acceder a el. Nuevamente, al sincronizar el mtodo te aseguras que cualquier hilo que intente acceder al dato, estar a salvo de los accesos simultneos, porque ambos hilos adquirirn el candado del objeto de la clase para el mtodo esttico definido en ella. Otra vez, eso es lo que queremos. Sin embargo, que ocurre si tienes un mtodo no esttico que accede a un campo esttico? O un mtodo esttico que accede a un campo no esttico (usando una instancia)? En estos casos, las cosas se complican rpidamente, y surgen muchas oportunidades de que las cosas no funcionen como esperabas. Si tienes un mtodo esttico accediendo a un campo no esttico y sincronizas el mtodo, adquieres el candado del objeto. Pero qu ocurre si hay otro mtodo que tambin accede al mtodo no esttico, esta vez usando un mtodo no esttico? Probablemente sincronice con la instancia actual (this) en su lugar. Recuerda que un mtodo esttico sincronizado y un mtodo no esttico sincronizado no se bloquearan mutuamente, pueden ejecutarse al mismo tiempo. De igual manera, si accedes a un campo esttico usando un mtodo no esttico, los dos hilos pueden invocar ese mtodo usando las dos distintas instancias. Lo que quiere decir que no se molestaran entre ellos, porque usan diferentes candados. Lo que significa que los dos hilos estn accediendo simultneamente al mismo campo esttico, exactamente la clase de cosas que queremos evitar.

email: brujulato@yahoo.es

Pgina 345

Curso gratuito de Java en emuladores L2 Java

2012

Se hace muy difcil imaginar todas las cosas que pueden suceder aqu. Para mantener las cosas de manera simple: para hacer una clase segura para multihilos, los mtodos que acceden a los datos alterables tienen que ser sincronizados. El acceso a campos estticos podra ser hecho desde mtodos sincronizados. El acceso a campos no estticos podra ser hecho desde mtodos no estticos sincronizados. Por ejemplo:

Qu ocurre si necesitas acceder a mtodos estticos y no estticos? Bien, hay una manera de hacer eso, pero esta ms all de lo que necesitas para el examen. Vivirs mejor y ms feliz sino lo haces. Crees que te mentira?

Clases multihilos seguras Cuando se ha sincronizado una clase para proteger sus datos (usando las reglas dadas, o usando alternativas ms complicadas), decimos que es segura para trabajar con multihilos. Muchas clases en el API de Java que usan sincronizacin internamente para hacerlas segura frente a ejecuciones multihilos. Por ejemplo, StringBuffer y StringBuilder son casi idnticas, excepto que los mtodos en StringBuffer estn sincronizados para cuando sea necesario, mientras estos en StringBuilder no lo son. Generalmente, esto hace a StringBuffer seguro para entornos multihilos, mientras que no en el caso de StringBuilder. (Por contra, StringBuilder es un poco ms rpido porque no tiene en cuenta la sincronizacin). Sin embargo, aunque la clase sea segura para ejecucin en hilos, vamos a llamarla por su nombre tcnico "thread-safe", es a menudo peligroso relegar en estas clases para proveer la proteccin que necesitas. (Vamos... que las comillas que rodean a "thread-safe" tienen truco, no?) Todavia tienes que pensar cuidadosamente en como usas estas clases, por ejemplo, considera la siguiente clase:

email: brujulato@yahoo.es

Pgina 346

Curso gratuito de Java en emuladores L2 Java

2012

El mtodo Collections.synchronizedList() devuelve una List donde cuyos mtodos estn todos sincronizados y "thread-safe" de acuerdo a la documentacin (como un Vector, pero este es el siglo 21, no vamos a usar Vector aqu). La pregunta es, puede la clase NameList ser usada con seguridad desde mltiples hilos? Es tentador decir que si, ya que los datos en nombres estn en una coleccin sincronizada, la clase NameList es segura tambin. Sin embargo no es el caso, el metodo removeFirst() puede arrojar una NoSuchElementException. Cul es el problema? No verifica correctamente el size() (tamao) de los nombres despus de borrar nada, para estar seguros si queda algo ah para borrar. Como podra este cdigo fallar? Intentemos usar NameList de esta manera:

que puede pasar aqu si uno de los hilos borra un nombre y lo imprime, entonces el otro hilo intentara borrar un nombre y obtendr un null. Si pensamos en las invocaciones a names.size() y names.get(0), ocurren en este orden:

Thread t1 ejecuta names.size(), que devuelve 1. Thread t1 ejecuta names.remove(0), que devuelve Ozymandias. Thread t2 ejecuta names.size(), que devuelve 0. Thread t2 no ejecuta remove(0).

La salida es

email: brujulato@yahoo.es

Pgina 347

Curso gratuito de Java en emuladores L2 Java


Ozymandias null Sin embargo, si ejecutamos otra vez el programa podemos conseguir algo distinto:

2012

Thread t1 ejecuta names.size(), que devuelve 1. Thread t2 ejecuta names.size(), que devuelve 1. Thread t1 ejecuta names.remove(0), que devuelve Ozymandias. Thread t2 ejecuta names.remove(0), que arroja una excepcion porque la lista esta ahora vacia.

Hay que darse cuenta que en una clase "thread-safe" como la devuelta por synchronizedList(), cada mtodo individual esta sincronizado, as que names.size() est sincronizado, y names.remove(0) est sincronizado. Pero nada impide que otro hilo haga algo ms en la lista, entre estas dos invocaciones. Y ese es el problema. La solucin est aqu: no relegues en colecciones sincronizadas, en vez de eso, sincroniza t mismo el cdigo:

Ahora todo el mtodo removeFirst() est sincronizado y una vez que un hilo invoque names.size(), no habr manera de que otro hilo pueda cortar y robar el nombre. El otro hilo tendr que esperar hasta que el primer hilo complete el mtodo removeFirst(). La moraleja aqu es que slo porque una clase se haya descrito como "thread-safe" no quiere decir que siempre sea as. Si un mtodo individual esta sincronizado, puede no ser suficiente, puede ser mejor sincronizar a un nivel ms alto (por ejemplo, ponerlo en bloques o mtodos que invoquen otros mtodos). Una vez que haces eso, la sincronizacin original (en este caso, la sincronizacin dentro del objeto devuelto por Collections.synchronizedList() puede ser redundante.

email: brujulato@yahoo.es

Pgina 348

Curso gratuito de Java en emuladores L2 Java


Thread DeadLock

2012

Quizs la cosa que ms asusta que puede ocurrir a un programa Java es un deadlock. Un deadlock ocurre cuando dos hilos estn bloqueados, cada cual esperando el candado del otro. Ninguno puede ejecutarse hasta que el otro hilo le pase su candado, as que estarn sentados all esperndose mutuamente hasta la eternidad. Esto puede ocurrir por ejemplo, cuando un hilo A con cdigo sincronizado, adquiere el candado de B, y luego entra otro mtodo (todava dentro del cdigo sincronizado) que tambin est sincronizado. Pero el hilo A no puede entrar al bloque sincronizado del bloque C, porque otro hilo D tiene el candado. as que el hilo A se va a la sala de espera, esperando a que D se de prisa y suelte el candado (complementando el mtodo sincronizado). Pero el hilo A espera un largo rato, ya que mientras el hilo D coge el candado de C, entra en un mtodo sincronizado de B. Obviamente, D no puede conseguir el candado de B porque lo tiene A. Y A no soltara el candado hasta que D suelte el candado de C. Pero D no soltar el candado de C hasta que B pueda recoger el candado y continuar. Y all estn sentados. El siguiente ejemplo demuestra un deadlock:

Asume que read() esta iniciado por un hilo y write() esta iniciado por otro. Si hay dos hilos diferentes que pueden leer y escribir independientemente, hay un riesgo de deadlock en la lnea 8 o 16. El hilo reader tiene un resourceA, el hilo writer tendr resourceB, y ambos tendrn que esperarse mutuamente. Cdigo como este casi nunca termina en deadlock porque la CPU tiene que cambiar del lector al escribidor (reader y writer) en un punto particular del cdigo, y las oportunidades de deadlock son muy pequeas. La aplicacin funciona perfectamente el 99.9% del tiempo.

email: brujulato@yahoo.es

Pgina 349

Curso gratuito de Java en emuladores L2 Java

2012

Este ejemplo es muy fcil de arreglar, slo con intercambiar los candados del lector o el escribidor en las lneas 16 y 17 (o las lneas 8 y 9). Situaciones ms complejas de deadlock pueden tomar ms tiempo del figurado. No importa como de pequea sea la oportunidad de que tu cdigo llegue a un deadlock, si obtienes un deadlock, estas muerto. Hay diseos que te ayudan a prevenir deadlock, incluyendo estrategias para adquirir candados en un determinado orden. Pero eso queda contigo, ya que est ms all de las pretensiones de este manual y del examen. Si aprendes todo lo que hay en este captulo, sabrs ms sobre hilos que la mayora de los programadores de Java.

21.6 Interaccin sobre los hilos


La ltima cosa que necesitamos saber es como los puedes pueden interactuar con otros hilos para comunicarles, entre otras cosas, su estatus con el candado. La clase Object tiene tres mtodos, wait(), notify() y notifyAll(), que ayuda a los hilos para que puedan comunicar el estatus de un evento al que el hilo est atendiendo. Por ejemplo, si un hilo es un reparto de email y otro es un procesador de email, el procesador de mail tiene que esperar para ver si se procesa el email. Usando el mtodo notify() y wait(), el hilo que podra procesar la verificacin del email, y si no encuentra ninguno, puede decir "Hey, no voy a malgastar mi tiempo verificando un email cada dos segundos. Voy a colgarme, y cuando el email llegue, que me avisen, y as voy a verificarlo". En otras palabras usar wait() y notify() te permite poner el hilo en la sala de espera, hasta que otro hilo notifique la razn para salir. Una clave que hay que recordar (y recordarlo para el examen) sobre wait y notify es:

wait(), notify() y notifyAll() debe invocarse desde un contexto sincronizado! Un hilo no puede invocar un mtodo wait() o notify() en un objeto a menos que posea su candado.

Aqu presentaremos un ejemplo de dos hilos que dependen uno del otro para proceder con su ejecucin, y mostraremos como usar wait() y notify() para hacer una interaccin seguro y en el momento apropiado. Imagina una mquina controlada por computadora que corta trozos de tela en diferentes formas y una aplicacin que permite a los usuarios especificar la forma de cortar. La versin actual de la aplicacin tiene un hilo, el cual es un bucle, primero pide al usuario instrucciones, y luego ordena al hardware para cortar la forma solicitada:

email: brujulato@yahoo.es

Pgina 350

Curso gratuito de Java en emuladores L2 Java

2012

Este diseo no es ptimo porque el usuario no puede hacer nada mientras la mquina tiene tarea, y mientras, hay otros perfiles que definir para el corte. Necesitamos mejorar la situacin. Una solucin simple es separar el proceso en dos hilos, uno que interacte con el usuario y el otro con el hardware de la mquina. El hilo del usuario enva instrucciones al hilo del hardware y vuelve a interactuar con el usuario inmediatamente. El hilo del hardware recibe las instrucciones del hilo del usuario y comienza a dirigir la mquina inmediatamente. Ambos hilos usan un objeto en comn para comunicarse, que les mantiene en el proceso del diseo actual. El siguiente pseudo cdigo muestra este diseo:

El problema ahora es conseguir el hilo del hardware para procesar los pasos de la maquina lo antes posible. Tambin, el hilo del usuario no debera de notificar hasta que hayan sido al hardware. La solucin es usar wait() y notify(), y tambin sincronizar algo de cdigo. Los mtodos wait() y notify(), recurdalo, son mtodos de instancia de Object. De la misma manera que cada objeto tiene un candado, cada objeto puede tener una lista de hilos que estn esperando para una seal (una notificacin) de otro objeto. Un hilo consigue la lista de espera ejecutando wait() en el objeto donde quiere actuar. Desde ese momento, no ejecutar ninguna instruccin hasta que el mtodo notify() del objeto sea invocado. Si hay muchos hilos esperando al mismo objeto, slo ser elegido uno (no hay garantas del orden) para ser ejecutado. Si no hay hilos esperando, entonces no se har ninguna accin en particular. Vayamos a ver algn cdigo real que muestre un objeto esperando a otro para una notificacin (toma notas, es un algo complejo):

email: brujulato@yahoo.es

Pgina 351

Curso gratuito de Java en emuladores L2 Java

2012

Este programa contiene dos objetos con hilos: ThreadA contiene el hilo principal y ThreadB tiene un hilo que calcula la suma de todos los nmeros desde el 0 al 99. Tan pronto como la lnea 4 invoca el mtodo start(), ThreadA continuar con la siguiente lnea de su propia clase, lo que significa que podra llegar a la lnea 11 antes de que ThreadB haya terminado el clculo. Para evitar esto, usamos el mtodo wait() en la lnea 9. Fjate que la lnea 6 sincroniza con el objeto b, esto es porque para invocar wait() en el objeto, ThreadA debe tener el candado de b. Para que un hilo pueda invocar wait() o notify(), el hilo tiene que tener el candado de los otros hilos para poder usarlo, pero necesitara continuar con su ejecucin. Lo normal es encontrar cdigo como este:

El cdigo anterior espera hasta que notify() sea invocado en anotherObject.

Este cdigo notifica a un solo hilo que est esperando para el objeto "this". La cerradura puede ser adquirida mucho antes en el cdigo, como en el mtodo de invocacin. Fjate que si el hilo invoca wait() no tiene el candado, arrojara una IllegalMonitorStateException. Esta excepcin no est verificada, as que no tienes que usar explcitamente un catch-try. Deberas tener claro si un hilo tiene el candado de un objeto en el bloque de cdigo.

email: brujulato@yahoo.es

Pgina 352

Curso gratuito de Java en emuladores L2 Java

2012

Fjate que las lneas 7-10 hay un try-catch encerrando al wait(). Un hilo en estado de wait puede ser interrumpido de la misma manera que un hilo que est en estado de sleep, as que tienes que tener cuidado con las excepciones:

En el ejemplo de la fbrica, la manera de usar estos mtodos es tener al hilo del hardware esperando a que la forma est disponible y el hilo del usuario usar notify() despus de que se hayan escritos los pasos. Los pasos de la maquina pueden contener pasos globales, como mover la piel al rea de corte, y el nmero de subpasos como la direccin y longitud de un corte. Por ejemplo:

Es importante que el hilo de usuario no modifique los pasos de la mquina mientras el hilo del hardware esta usndolo, as que la lectura y escritura debera estar sincronizada. El resultado del cdigo podra ser este:

email: brujulato@yahoo.es

Pgina 353

Curso gratuito de Java en emuladores L2 Java

2012

El hilo de la mquina, una vez que se inicia, ir inmediatamente al estado de wait y esperar pacientemente hasta que el operador enve su primera notificacin. Llegados a este punto el hilo del operador que posee el candado del objeto, as que el hilo del hardware se queda estancado por un rato. A menudo, slo despus de que el hilo del operador abandone el bloque sincronizado que el hilo del hardware puede realmente iniciar el proceso de los pasos de la mquina. Mientras una forma est siendo procesada por el hardware, el usuario puede interactuar con el sistema y especificar otra forma para cortar. Cuando el usuario ha terminado con la forma y es hora de cortarla, el hilo del operador intenta entrar al bloque sincronizado, quizs bloqueado hasta que el hilo de la maquina haya terminado con los pasos previos. Cuando el hilo de la mquina haya terminado, repite el bucle, yendo otra vez al estado de wait (y soltando el candado). Slo entonces el hilo del operador puede entrar al bloque sincronizado y sobrescribir los pasos de la mquina con otros nuevos. Tener dos hilos es mejor que tener uno, aunque esta implementacin an puede hacer a un usuario esperar. Una mejora podra ser tener los cortes en una cola, reduciendo la posibilidad de hacer esperar al usuario. Existe otra forma para wait() que acepta el nmero de milisegundos a esperar. Si el hilo no es interrumpido, continuar de manera normal si es notificado o el tiempo ha pasado. Esta continuacin normal consiste en salir del estado de espera, pero para continuar la ejecucin tendr que tener el candado del objeto.

El uso de notifyAll() cuando hay muchos hilos esperando En la mayora de los escenarios, es preferible notificar a todos los hilos que estn esperando en lugar de a un objeto en particular. As que, puedes usar notifyAll() en el objeto para permitir a todos los hilos salir del rea de descanso y volver al estado de runnable. Esto es especialmente importante si tienes varios hilos esperando para el mismo objeto, pero por diferentes razones, y quieres asegurarte de que el hilo correcto (adems de los dems) sean notificados.

Todos los hilos seran notificados y comenzaran a competir para conseguir el candado. Como el candado se usa y se libera en cada hilo, todos ellos tendra ocasion de entrar en accion sin necesidad de nuevas notificaciones.

email: brujulato@yahoo.es

Pgina 354

Curso gratuito de Java en emuladores L2 Java

2012

Como dijimos antes, un objeto puede tener muchos hilo esperando, y usar notify() afectar los hilos de uno en uno. Cul de ellos, exactamente, no se especifica y depende de la implementacin de JVM, as que nunca delegues en darle la preferencia a un hilo para que sea notificado. En casos en los que puede haber un montn esperando, la mejor manera de hacerlo es usando notifyAll(). Veamos algo de cdigo, En este ejemplo hay una clase que ejecuta un clculo y muchos lectores estn esperando a recibir el clculo completo.

El programa inicia tres hilos que estn esperando a recibir el clculo terminado (lneas 18 a 24), y luego comienza el clculo con su calculador. Fjate que el mtodo run() en la lnea 30 usa notify() en lugar de notifyAll(), solo un lector ser notificado en ligar de todos los que hay.

El uso de wait() en un bucle En los ejemplos anteriores tenamos un problema en comn. En cada uno, haba al menos un hilo invocando wait(), y otro hilo invocando notify() o notifyAll(). Esto funciona bien mientras email: brujulato@yahoo.es Pgina 355

Curso gratuito de Java en emuladores L2 Java

2012

los hilos que estn esperando, han sido iniciados por otro hilo invocando notify() o notifyAll(). Pero qu pasa si, por ejemplo, el Calculator se ejecuta primero y luego invoca notify() antes de que los lectores estn esperando? Esto podra pasar, ya que no podemos garantizar el orden en que las diferentes partes de los hilos sern ejecutados. Desafortunadamente, cuando los lectores estn ejecutndose, inmediatamente pasan al modo de espera. No hacen nada para ver si a lo que estaban esperando, ya ha sucedido, pudiendo seguir ejecutndose (para que esperar entonces?) as que si el Calculator ya ha lanzado el notifyAll(), no va a ejecutar nuevamente notifyAll() y los lectores estarn esperando en vano (y para siempre). Probablemente esto no es lo que un programador quiere que ocurra. Casi siempre, cuando quieres esperar a algo, tienes que ser capaz de saber si ya ha sucedido. Generalmente la mejor manera de resolver esto es poner del alguna manera un bucle que verifique las condiciones, y solo hacer la espera si la cosa a la que se est esperando an no ha sucedido. Aqu hay una versin modificada, ms segura de la mquina de cortar pieles:

La operacin se mantendr en un bucle para siempre, consiguiendo ms formas de corte de los usuarios, calculando nuevas instrucciones para todas las formas, y envindolas a la mquina. Pero ahora la lgica para notify() ha sido movido a addJob() en la clase Machine:

email: brujulato@yahoo.es

Pgina 356

Curso gratuito de Java en emuladores L2 Java

2012

Una mquina espera una lista de tareas. Si un operador aade una nueva tarea a la lista, invoca a addJob() y aade la tarea a la lista. Mientras tanto, el mtodo run() se mantiene en un bucle buscando tareas en la lista. Si no hay tareas, empieza a esperar. Si se le notifica, parar de esperar y luego se verifica la condicin del bucle: esta la lista todava vaca? En la prctica esta doble verificacin no es necesaria probablemente, ya que una vez invocado notify() es cuando se ha aadido una tarea a la lista. Sin embargo, es una buena idea requerirle al hilo una verificacin para ver si la condicin de isEmpty() se ha despertado, porque es posible que un hilo haya enviado accidentalmente un notify(). Tambin es posible que la situacin invocada de repente despierte, un hilo puede despertar incluso sin invocar notify() o notifyAll(). (Al menos, cdigo que tu sepas que haya invocado esos mtodos. Algunas veces puede pasar una situacin denominada "despertar espontaneo", un hilo se despierta sin razn, o al menos, alguna razn conocida). Lo que esto significa es, que cuando tu hilo se despierta de un wait(), no sabes con total certeza, que es lo que lo ha despertado. Poner un wait() a un mtodo en un bucle wait() y re-verificar la condicin que lo hizo despertar, te asegura que si no fue una razn vlida el motivo del despertar, volver a entrar en modo de espera si, (y nicamente si) la cosa que estbamos esperando no ha pasado aun. En la clase Machine, la cosa por la que estamos esperando es para la lista de tareas, que no este vaca. Si esta vaca, esperamos, si no, no. Fjate que ambos mtodos run() y el mtodo addJob() sincronizan el mismo objeto, la lista de tareas. Es por dos razones. Una es porque estamos invocando wait() y notify() en esta instancia, as que necesitamos sincronizar para prevenir IllegalStateException. La otra es, los datos en la lista de tareas son guardados en unos campos alterables, accedidos para intercambiar datos con seguridad. Afortunadamente, el mismo bloque sincronizado te permite usar wait() y notify() y tambin provee de un hilo seguro para nuestro acceso a nuestros datos alterables. El hecho es que la principal razn de por qu se necesita la sincronizacin es para usar wait() y notify() en primer lugar, casi siempre vas a necesitar compartir datos alterables entre los hilos a la misma vez, y eso quiere decir que necesitas sincronizacin. Date cuenta que email: brujulato@yahoo.es Pgina 357

Curso gratuito de Java en emuladores L2 Java

2012

el bloque sincronizado addJob() es lo suficientemente grande para incluir la llamada a jobs.isEmpty(), que accede a los datos compartidos. La moral aqu es que cuando usas wait() y notify() o notifyAll(), deberas casi siempre tener un bucle while alrededor de wait() que verifique una condicin y obligue al hilo esperar hasta que la condicin se cumpla. Deberas asegurarte de hacer uso de la sincronizacin para wait() y notifty(), para proteger otros datos que ests compartiendo entre hilos. Si ves cdigo que suele fallar al hacer esto, lo normal es que haya algo equivocado en el cdigo, aunque hayas pasado mucho tiempo dedicado a buscar el problema. Atencin, los mtodos wait(), notify() y notifyAll() son mtodos exclusivos de java.lang.Object, no de java.lang.Thread o java.lang.Runnable. Asegrate de saber que mtodos estn definidos en Thread, en Object y cuales en Runnable (solo es run() as que es fcil). Una de las claves de los mtodos en Thread, asegrate de saber cules son estticos, sleep() y yield(), cules no, join() y start(). La siguiente tabla muestra los mtodos que necesitas saber:

email: brujulato@yahoo.es

Pgina 358

Curso gratuito de Java en emuladores L2 Java


22 Compilando nuestro cdigo

2012

T quieres tener tus clases organizadas. Necesitas tener poderosas maneras para que tus clases puedan encontrarse unas a otras. Quieres asegurarte de que cuando ests buscando una clase en particular, encuentras la que quieres, y no otra clase que tenga el mismo nombre. En este captulo vamos a explorar algunas de las capacidades avanzadas de los archivos que provee la Java: java.exe y javac.exe. Repasaremos el uso de los paquetes en java, y como se buscan las clases que estn en los paquetes.

22.1 Compilando con javac El comando javac se usa para invocar el compilador de Java. En el capitulo 12 hablamos sobre el mecanismo de asercin y de cuando puedes usar la opcin -source cuando compilas un fichero. Hay muchas opciones que puedes especificar cundo ejecutas javac, opciones para info en la limpieza de errores (debug) o los mensajes de compilacin. Para el examen debes entender las opciones -classpath y -d, que las veremos en breve. Adems, es importante que entiendas la estructura de este comando. Aqu se muestra una vista: javac [options] [source files] Hay lneas de comando adicionales llamadas @argfiles, pero no necesitas estudiarlas para el examen. Las [options] y las [source files] son partes opcionales del comando, y ambos tienen mltiples entradas. Lo siguiente son comandos javac validos: javac javac -classpath com:. -g Foo.java Bar.java -help

La primera invocacin no compila nada, pero imprime una lista de opciones vlidas. La segunda invocacin le pasa al compilador dos opciones, (-classpath tiene en s mismo el argumento com:. y -g) y le pasa al compilador los ficheros .java para compilar (Foo.java y Bar.java). Si le especificas mltiples opciones y/o ficheros, debern estar separados por espacios.

Compilando con -d Por defecto, el compilador pone los ficheros .class en el mismo directorio que el fichero fuente. Esto est bien para pequeos proyectos, pero una vez que ests trabajando para un proyecto considerable, vas a querer tus ficheros .java separados de los ficheros .class. (Esto te ayuda con el control de la versin, las pruebas, el desarrollo...). La opcin -d te permite decirle al compilador en que directorio poner los ficheros class generados (d significa destino). Digamos que tienes la siguiente estructura de directorio:

email: brujulato@yahoo.es

Pgina 359

Curso gratuito de Java en emuladores L2 Java

2012

El siguiente comando, emitido desde el directorio myProject, compilar MyClass.java y pondr el fichero resultante en el directorio classes. (Atencin: se asume que MyClass no tiene un paquete, hablaremos de esto en breve).
cd myProject javac -d classes source/MyClass.java

Este comando tambin muestra un fichero .java de un subdirectorio del directorio desde donde se invoc el comando. Ahora veamos cmo funcionan los paquetes con la opcin -d. Suponte que tenemos la siguiente estructura .java como sistema de directorio:

Si estuvieras en el directorio fuente, compilaras MyClass.java y pondras resultado MyClass.class en classes/com/wickedlysmart invocando este comando: email: brujulato@yahoo.es

el

Pgina 360

Curso gratuito de Java en emuladores L2 Java


javac -d ../classes com/wickedlysmart/MyClass.java

2012

Este comando se leera: "Seleccionar como directorio de destino, cd vuelve al directorio myProject, luego cd dentro del directorio de la clase, que ser tu destino. Despus compila el fichero MyClass.java. Finalmente, pone el resultado MyCLass.class dentro del directorio en la estructura que coincide con su paquete, en este caso, clsses/com/wickedlysmart". Ya que MyClass.java est en un paquete, el compilador saba dnde poner el resultado .class, dentro de classes/com/wickedlysmart. De manera sorprendente, el comando javac puede a veces ayudarte en la construccin de directorios que necesitas. Suponte que tenemos:

y el siguiente comando:
javac -d ../classes com/wickedlysmart/MyClass.java

En este caso, el compilador construir dos directorios llamados com y com/wickedlysmart para poner el resultado de la compilacin, MyClass.class, en el paquete correcto del directorio (com/wickedlysmart/) que lo construye dentro del directorio existente /classes. La ultima cosa sobre -d que tienes que saber para el examen, es que si el directorio de destino que especificas no existe, vas a obtener un error de compilacin. Si, en el ejemplo anterior, el directorio de classes no existiese, el compilador podra arrojar algo como esto:
java:5: error while writing MyClass: classes/MyClass.class (No such file or directory)

email: brujulato@yahoo.es

Pgina 361

Curso gratuito de Java en emuladores L2 Java


22.2 Lanzando aplicaciones con java

2012

El comando Java se usa para invocar a la mquina virtual. En el captulo 5 hablamos sobre el mecanismo de asercin y de cuando puedes usar flags tales como -ae o -da cuando lanzamos la aplicacin. Hay muchas otras opciones que puedes especificar cuando ejecutas el comando java, pero para el examen solo necesitaras entender -classpath (y su gemelo -cp) y -d, los cuales vamos a ver en las prximas lneas. Esta es una muestra:
java [options] class [args]

Los parmetros [options] y [args] son opcionales, y pueden tener mltiples valores. Tienes que especificar exactamente un fichero .class para ejecutar, y el comando java asume que se trata de un fichero .class, as que no tienes que especificar la extensin. Por ejemplo:
java -DmyProp=myValue MyClass x 1

Dejamos los detalles para luego, este comando puede ser ledo como "Crea un system property llamado myPop y ponle su valor a myValue. Luego ejecuta el fichero llamado MyClass.class y enviale los argumentos de cadena cuyos valores son x y 1". Miraremos las propiedades del sistema y la lnea de argumentos ms de cerca.

Usando las propiedades de sistema. A partir de Java 5 hay una clase llamada java.util.Properties que puede ser usada para acceder a la informacin persistente del sistema, tales como la versin actual del sistema operativo, y el compilador Java, y la mquina virtual. Adems de proveer la informacin por defecto, tambin puedes aadir y obtener tus propias propiedades. Observa esto:

Si compilamos este fichero e invocramos as:


java -DcmdProp=cmdVal TestProps

Obtendras algo como esto:


... os.name=Mac OS X myProp=myValue ... java.specification.vendor=Sun Microsystems Inc. user.language=en java.version=1.5.0_02

email: brujulato@yahoo.es

Pgina 362

Curso gratuito de Java en emuladores L2 Java


... cmdProp=cmdVal ...

2012

donde los ... representan un montn de nombres=valores. (El nombre y el valor algunas veces se les llama por la clave de la propiedad). Se aadi una propiedad name=value al sistema de propiedades: myProp = myValue mediante setProperty. Cuando se usa la opcin -d, si tu valor contiene espacios en blanco, la entrada seria reemplazada por comillas dobles, como esto:
java -DcmdProp="cmdVal take 2" TestProps

Tan solo en el caso de que te hayas perdido, cuando usas -D el name=value debe seguir inmediatamente, no se permiten espacios. El mtodo getProperty() se usa para obtener una sola propiedad. Puede ser invocada con un solo argumento (una cadena que represente el nombre (o clave)), o puede ser invocado por dos argumentos (una cadena que represente el nombre (o la clave), y el valor por defecto de la cadena usada como propiedad si la propiedad no existe ya). En ambos casos, getProperty() devuelve la propiedad como una cadena.

Manejando los argumentos en la lnea de comandos Volvamos a un ejemplo de lanzar una aplicacin y pasarle los argumentos desde la lnea de comandos. Si tienes el siguiente cdigo:

y se invoca:
java CmdArgs x 1

la salida mostrara:
0 element = x 1 element = 1

Al igual que los arrays, args se basa en el ndex cero. Los argumentos en la lnea de comando siguen directamente al nombre de la clase. El primer argumento se le asigna args[0], el segundo el 1, y asi. Finalmente, hay algo de flexibilidad en el mtodo main() que se usa para iniciar una aplicacin Java. Los modificadores de main pueden ser alterados ligeramente, la cadena del array no email: brujulato@yahoo.es Pgina 363

Curso gratuito de Java en emuladores L2 Java

2012

tiene que llamarse args, y a partir de Java 5, puede ser declarada usando la sintaxis var-args. Las siguientes declaraciones son vlidas:
static public void main(String[] args) public static void main(String... x) static public void main(String bang_a_gong[])

La bsqueda de otras clases En la mayora de los casos, cuando usamos java y javac, queremos estos comandos para buscar otras clases que sern necesarias para completar la operacin. La mayora de los casos obvios es cuando creamos clases que Sun provee con J2SE (ahora llamado a veces Java SE), por ejemplo cuando usamos las clases en java.lang o java.util. El siguiente caso comn es cuando queremos compilar un fichero o ejecutar una clase que usa otras clases que han sido creadas fuera de lo que Sun provee, por ejemplo nuestras propias clases personales. Recuerda que para cualquier clase dada, la mquina virtual Java necesitara encontrar exactamente el mismo soporte de clases que javac necesita para encontrar en la compilacin. En otras palabras, si javac necesita acceder a java.util.HashMap entonces java necesitara encontrar java.util.HashMap tambin. Java.exe y javac.exe usan el mismo algoritmo bsico de bsqueda: 1. Ambos tienen la misma lista de sitios (directorios) donde buscar las clases. 2. Amos buscan en la lista de directorio en el mismo orden. 3. Tan pronto como encuentran la clase que buscan, paran de buscar. En el caso que su bsqueda contenga dos o ms ficheros con el mismo nombre, el primer fichero que encuentre ser el que use. 4. En el primer sitio donde buscan es en los directorios que contienen las clases que vienen con el estndar J2SE. 5. El segundo sitio donde buscan es en los directorios definidos en los classpaths. 6. Los classpath deben ser considerados como "rutas de bsqueda". Es la lista de directorios donde pueden encontrarse las clases. 7. Hay dos sitios donde se pueden declaran los classpath: 1. Como variable de entorno en el sistema operativo. Este se usa por defecto, si java o javac son invocados. 2. Puede ser declarado como opcin en la lnea de comando para java o javac. Los classpath declarados en la lnea de comando como opciones, sobrescriben al declarado en la variable de entorno, pero solo persisten en la invocacin.

Declarando y usando classpaths Los classpath consisten en una variable cantidad de directorios, separados por delimitadores. Para los sistemas operativos basados en Unix, se usa una barra inclinada para construir las rutas, y el separador es el "dos puntos (:)", por ejemplo:
-classpath /com/foo/acct:/com/foo

email: brujulato@yahoo.es

Pgina 364

Curso gratuito de Java en emuladores L2 Java

2012

Especifica dos directorios en las que las clases pueden ser encontradas: /com/foo/acct y /com/foo. En ambos casos, estos directorios estn unidos a la raz del fichero de sistema, que se especifica por las barras inclinadas. Es importante recordar que cuando especificas un subdirectorio, no estas especificando el directorio por encima de el. Por ejemplo, en el ejemplo anterior, /com no ser una ruta de bsqueda. Una situacin muy comn sucede en las quejas de java o javac, que no puede encontrar una clase, y aun has visto que ese fichero con la clase, est en el directorio actual! Cuando buscas clases, los comandos java y javac no buscan en el directorio por defecto. Tienes que decirles donde buscar. La manera de decirla a java o javac que tienen que buscar en el directorio por defecto es aadir un punto (.) al classpath:
-classpath /com/foo/acct:/com/foo:.

Este classpath es idntico al previo excepto por el punto al final de la declaracin, esto le dice a java y javac de buscar las clases en el directorio actual. (Recuerda que estamos hablando sobre ficheros con clases, cuando le dices a javac donde tiene que compilar los ficheros java, javac mirar el directorio actual por defecto.) Tambin es importante recordar que los classpath se miran de izquierda a derecha. En una situacin donde haya clases con nombres duplicados se localicen en diferentes directorios en el siguiente classpath, pueden ocurrir diferentes resultados:
-classpath /com:/foo:.

no es lo mismo que:
-classpath .:/foo:/com

Para terminar, el comando java te permite abreviar -classpath con -cp. La documentacin de Java es inconsistente sobre si javac permite la abreviacin -cp. En la mayora de las maquinas si, pero no hay garantas.

Los paquetes y su bsqueda Cuando comienzas a poner clases en paquetes, y luego usas classpath para encontrar estas clases, la cosa puede resultar engaosa. Los creadores de examen saban esto, y han intentado crear una serie de preguntas malvadas sobre paquetes que pueden confundirte. Empecemos por ver los paquetes. En este cdigo:

estamos diciendo que MyClass es un miembro del paquete com.foo. Esto significa que es un nombre totalmente cualificado de donde la est la clase ahora. Una vez una clase est en un paquete, el paquete es parte de su propio cualificado nombre, es un nombre atmico, indivisible, nunca puede ser dividido. No puedes dividir en la lnea de comandos, y no puedes dividirlo en un estamento import.

email: brujulato@yahoo.es

Pgina 365

Curso gratuito de Java en emuladores L2 Java


Ahora veremos cmo puedo usar com.foo.MyClass en otra clase:

2012

y esta otra:

Es fcil confundirse cuando estas usando estamentos import. El cdigo es perfectamente legal. El estamento import es como un alias del nombre cualificado de la clase. Defines el nombre completo de la clase en un estamento import (o con un comodn al importar el paquete). Una vez que has definido el nombre cualificado, puedes usar el alias en tu cdigo, pero el alias se refiere al nombre cualificado. Ahora que hemos visto los paquetes, veamos cmo funcionan en conjunto con los classpath y la lnea de comandos. Primero vamos a empezar con la idea de que estas buscando la clase usando su nombre cualificado, ese nombre que define la estructura del directorio. Por ejemplo, relativo a tu directorio, el cdigo fuente del cdigo es:

que tendra que estar localizado aqu:


com/foo/MyClass.class

Para encontrar una clase en un paquete, tienes que tener un directorio en tu classpath que tenga el paquete en la entrada ms a la izquierda (la raz del paquete) como un subdirectorio. Este concepto es importante, as que vamos a ver otro ejemplo:

En este caso estamos usando dos clases del paquete com.wickedlysmart. Para esclarecer, hemos importado el nombre cualificado completo de la clase Utils, pero no lo hemos hecho para Date. La nica diferencia es que ya que hemos importado Utils , no tenemos que escribir email: brujulato@yahoo.es Pgina 366

Curso gratuito de Java en emuladores L2 Java

2012

su nombre cualificado dentro de la clase. En ambos casos el paquete es com.wickedlysmart. En tiempo de compilacin o que se ejecute TestClass, el classpath tendr que incluir un directorio con los siguientes atributos:

Un subdirectorio llamado com (esto sera el directorio raiz). Un subdirectorio dentro de com que se llame wickedlysmart. Dos ficheros en wickedlysmart que se llaman Utils.class y Date.class.

Para terminar, el directorio que tiene todos estos atributos tiene que ser accesible (via classpath) de una de estas maneras: 1. La ruta para el directorio debe ser absoluta, en otras palabras, desde la raz (el fichero de sistema, no del paquete). 2. La ruta del directorio tiene que ser correcta relativa al documento del directorio actual.

Rutas absolutas y relativas Una classpath es una coleccin de una o ms rutas. Cada ruta en un classpath es tambin una ruta absoluta o una ruta relativa. Una ruta absoluta en Unix comienza con una barra inclinada (/) (en Windows seria c:/). La barra indica que esta ruta comienza desde el directorio raz del sistema. Ya que comenzamos desde la raz, no importa que no se trate del directorio actual, la ruta es siempre la misma. Una ruta relativa es la que no comienza con la barra inclinada. Aqu hay un ejemplo de una estructura completa y un classpath:

En este ejemplo, dirB y dirB/dirC son rutas relativas (no comienzan por la barra inclinada). Ambas rutas son significativas solo cuando el directorio actual es dirA. Si el directorio es dirA, y estas buscando clases, y usas el classpath descrito arriba, cul de estos directorios se har la bsqueda? dirA? dirB? o dirC? Demasiado fcil? que tal la misma pregunta si el directorio actual es la raiz (/)? Cuando el directorio actual es dirA, entonces dirB y dirC sern destinos de bsqueda, pero no dirA (recuerda, no hemos especificado el directorio actual aadiendo el punto (.) al classpath). Cuando el directorio actual es la raz, ya que dirB no es un subdirectorio de root,

email: brujulato@yahoo.es

Pgina 367

Curso gratuito de Java en emuladores L2 Java

2012

no se buscara en ningn directorio. Bien, ahora que tal si el directorio actual es dirB? Nuevamente, tampoco se harn bsquedas. En otras palabras, Java mirar en dirB buscando un directorio llamado dirB (el cual, no encontrara), sin darse cuenta que ya est en el dirB. Usaremos la misma estructura de directorio con un classpath distinto:

En este caso, donde se realizara la bsqueda si el directorio es dirA? Qu ocurre si el directorio actual es la raz? En este caso, ambas rutas en el classpath son absolutas. No importa en qu directorio nos encontremos, ya que las rutas absolutas especificadas sern siempre las mismas. Especialmente, solo dirC es donde se har la bsqueda, sin importar el directorio actual. La primera ruta (/dirB) es invlida ya que dirB no es subdirectorio de la raz, as que nunca se har una bsqueda ah. Y una vez ms, para darle nfasis, el punto (.) no est en el classpath, el directorio actual solo se usar para hacer la bsqueda si esta descrito en alguna parte del classpath (en este caso, dirC).

22.3 Ficheros .jar Una vez que hayas construido y testeado tu aplicacin, puedes querer lanzarla al pblico y dejar que otros usuarios puedan instalar la aplicacin. Un mecanismo que Java provee para este propsito son los ficheros jar. Los ficheros jar son usados para comprimir los datos (parecido a los ficheros zip) y datos de archivo. Digamos que tienes una aplicacin que usa muchas clases que estn localizadas en diferentes paquetes. Aqu se muestre una parte del directorio:

email: brujulato@yahoo.es

Pgina 368

Curso gratuito de Java en emuladores L2 Java

2012

Puedes crear un solo jar que contenga todas las clases en myApp, y tambin la estructura de directorios. Una vez este fichero jar est creada, puede ser movido de lugar a lugar, y de mquina a mquina, y todas las clases del fichero jar pueden ser accedidas va classpath y usado por java y javac. Todo esto puede ocurrir incluso sin descomprimir el fichero jar. Aunque no necesitas saber cmo hacer un fichero jar para el examen, vamos al directorio ws y luego hagamos un fichero jar llamado MyJar:
cd ws jar -cf MyJar.jar myApp

El comando jar creara un fichero llamado MyJar.jar que contendr el directorio myApp y todos los subdirectorios. Podis mirar el contenido del jar con el siguiente comando externo (esto tampoco entra en el examen):
jar -tf MyJar.jar

lo que listara el contenido del jar, como esto:


META-INF/ META-INF/MANIFEST.MF myApp/ myApp/.DS_Store myApp/utils/ myApp/utils/Dates.class myApp/utils/Conversions.class myApp/engine/ myApp/engine/rete.class myApp/engine/minmax.class

Ahora volvemos con las cosas del examen. Encontrar un fichero jar usando un classpath es similar a encontrar un fichero en un paquete jar, debes incluir el nombre del jar al final de la ruta. Digamos que quieres compilar UseStuff.java en el directorio test, y UseStuff.java necesita acceder a una clase contenida enmyApp.jar. Para compilar UseStuff.java diras:

email: brujulato@yahoo.es

Pgina 369

Curso gratuito de Java en emuladores L2 Java


cd test javac -classpath ws/myApp.jar UseStuff.java

2012

Comparar el uso de un fichero jar al uso de una clase en un paquete. Si UseStuff.java necesitase usar clases en el paquete myApp.utils, y la clase no est en el jar, podras decir:
cd test javac -classpath ws UseStuff.java

Recuerda que cuando se usa un classpath, el ltimo directorio en la ruta debe ser el super directorio del directorio raz para el paquete. En el ejemplo anterior myApp es el directorio raz del paquete myApp.utils. Fjate que myApp puede ser el directorio raz de ms de un paquete ((myApp.utils y myApp.engine), y el comando java y javac pueden encontrar lo que necesitan a travs de paquetes como estos. En otras palabras, si ws est en el classpath y ws es el super directorio de myApp, entonces las clases de myApp.utils y myApp.engine sern encontradas.

Usando .../jre/lib/ext con los ficheros jar Cuando instalas Java, terminas con un enorme rbol de directorios, incluyendo ficheros jar que contienen las clases que vienen con el estndar de J2SE. Como vimos antes, java y javac tienen una lista de lugares a los que tienen que acceder cuando buscan los ficheros con las clases. Enterrados en lo ms profundo del rbol de subdirectorios hay un rbol que se llama jre/lib/ext. Si pones los ficheros jar dentro del subdirectorio ext, java y javac pueden encontrarlos, y usar las clases que contienen esos ficheros. No tienes que mencionar estos subdirectorios en un estamento classpath, la bsqueda en estos directorios es una funcin que est construida dentro de Java. Sun recomiendo, sin embargo, que uses esta caracterstica slo para tus propios testeos y desarrollo, y no para un software que intentas distribuir. Atencin, cuando uses un import estas declarando solo un paquete. Cuando dices import java.util.*; estas diciendo "Usar el nombre corto para todas las clases en import java.util." No estas pillando las clases import java.util.jar o import java.util.regex! Estos paquetes son totalmente independientes unos de otros, la nica cosa que ellos comparten es el directorio raz, pero no los paquetes. Para terminar, no puedes decir import java.*; con la esperanza de importar mltiples paquetes, solo recuerda, un import solo importa un paquete Atencin, es posible crear variables de entorno que provean de un alias para los classpath extensos. El classpath para algunos de los ficheros jar en J2SE pueden ser bastante extensos, y es muy comn para estos un alias que sea usado cuando se define un classpath. Si ves algo como JAVA_HOME o $JAVA_HOME en una pregunta de examen, quiere decir "La parte de un classpath absoluto en los directorios que estamos especificando explcitamente". Puedes asumir que JAVA_HOME significa esto literalmente, y esto se agrega como prefijo a la ruta de clase parcial que ves.

email: brujulato@yahoo.es

Pgina 370

Curso gratuito de Java en emuladores L2 Java


22.4 Importaciones estticas

2012

Hemos estado usando importaciones estticas en todo el manual. ltimamente, el nico valor import que queda es el que te ahorra de escribir y hace tu cdigo ms fcil de leer. A partir de Java 5, el import fue mejorado para proveer incluso de ahorrarte de escribir... aunque alguno argumentara que esto lo paga la legibilidad. Esta nueva caracterstica se le conoce como import esttico. Los import estticos pueden ser usados cuando quieres usar los miembros estticos de una clase. (Puedes usar esta caracterstica en el API y en tus propias clases). Aqu hay un ejemplo del antes y despus: Antes del import esttico:

despus con import estticos:

Ambas clases producen el mismo resultado:


2147483647 2a

Veamos que pasa en el cdigo usando import esttico: 1. Aunque a la caracterstica se llama comnmente "importe esttico", la sintaxis tiene que ser import static seguido del nombre completo cualificado del miembro esttico que se quiere importar, o un comodn. En este caso estamos haciendo un import esttico del objeto out de la clase System. 2. En este caso podemos querer usar varios de los miembros estticos de java.lang.Integer. Esta importacin esttica de estamentos usa el comodn para decir "Quiero importar todos los miembros estticos de esta clase". 3. Ahora finalmente vemos el beneficio de las importaciones estticas. No tenemos que escribir el System en System.out.println! Luego, tampoco tenemos que escribir Intenger en Integer.MAX_VALUE. as que en esta lnea de cdigo podemos usar un atajo para el mtodo esttico y una constante. 4. Finalmente, hacemos un atajo ms, esta vez para el mtodo en la clase Integer.

email: brujulato@yahoo.es

Pgina 371

Curso gratuito de Java en emuladores L2 Java

2012

Hemos sido un poco sarcsticos en esta caracterstica, pero no somos los nicos. No estamos convencidos de que ahorrarnos teclear haga el cdigo ms fcil de leer, pero hay suficientes desarrolladores que pidieron que fuera aadido al lenguaje. Aqu hay un par de reglas para usar import estticos:

Tienes que decir import static, no static import. Vigilar ambigedades al nombrar miembros estticos. Por ejemplo, si importas de la clase Integer y de la clase Long, MAX_VALUE, dar error de compilacin, ya que ambas clases usan la misma constante, y Java no sabe a quin te estas refiriendo. Puedes importar referencias estticas de objetos, constantes (recuerda que estas son estticas y finales) y mtodos estticos.

email: brujulato@yahoo.es

Pgina 372

Curso gratuito de Java en emuladores L2 Java

2012

Este manual se ha construido usando las fuentes de varios libros y sitio web: Programador certificado de Java 2, segunda edicin Sun Cerfified Programmer for Java 6 exam (traducido por kimeraweb) Java para estudiantes Las referencias regex desde Oracle

Todos los crditos para kimeraweb.

Este manual es de libre distribucin y en el se encuentran adaptaciones propias basadas en la experiencia de Java.

Si deseas usar este manual para obtener el certificado de programador de Oracle, deberas completar los ejercicios de compresin llamados killtest.

Sin ms que aadir hasta futuras actualizaciones, espero que este manual te haya servido al menos para tener una referencia del lenguaje Java.

email: brujulato@yahoo.es

Pgina 373