Está en la página 1de 82

PROGRAMACION EN JAVA

Cuando se programa en Java, se coloca todo el cdigo en mtodos, de la misma forma que se escriben funciones en lenguajes como C.

Comentarios
En Java hay tres tipos de comentarios:

// comentarios para una sola lnea /* comentarios de una o ms lneas */ /** comentario de documentacin, de una o ms lneas */

Los dos primeros tipos de comentarios son los que todo programador conoce y se utilizan del mismo modo. Los comentarios de documentacin, colocados inmediatamente antes de una declaracin (de variable o funcin), indican que ese comentario ha de ser colocado en la documentacin que se genera automticamente cuando se utiliza la herramienta de Java, javadoc. Dichos comentarios sirven como descripcin del elemento declarado permitiendo generar una documentacin de nuestras clases escrita al mismo tiempo que se genera el cdigo. En este tipo de comentario para documentacin, se permite la introduccin de algunos tokens o palabras clave, que harn que la informacin que les sigue aparezca de forma diferente al resto en la documentacin.

Identificadores
Los identificadores nombran variables, funciones, clases y objetos; cualquier cosa que el programador necesite identificar o usar.

En Java, un identificador comienza con una letra, un subrayado (_) o un smbolo de dlar ($). Los siguientes caracteres pueden ser letras o dgitos. Se distinguen las maysculas de las minsculas y no hay longitud mxima. Seran identificadores vlidos:
identificador nombre_usuario Nombre_Usuario _variable_del_sistema $transaccion

y su uso sera, por ejemplo:


int contador_principal; char _lista_de_ficheros; float $cantidad_en_Ptas;

Palabras clave Las siguientes son las palabras clave que estn definidas en Java y que no se pueden utilizar como indentificadores:
abstract boolean break byte byvalue case catch char class const continue default do double else extends false final finally float for goto if implements import instanceof int interface long native new null package private protected public return short static super switch synchronized this threadsafe throw transient true try void while

Palabras Reservadas Adems, el lenguaje se reserva unas cuantas palabras ms, pero que hasta ahora no tienen un cometido especfico. Son:
cast operator future outer generic rest inner var

Literales
Un valor constante en Java se crea utilizando una representacin literal de l. Java utiliza cinco tipos de elementos: enteros, reales en coma flotante, booleanos, caracteres y cadenas, que se pueden poner en cualquier lugar del cdigo fuente de Java. Cada uno de estos literales tiene un tipo correspondiente asociado con l. Enteros:
byte short int long Por ejemplo: 8 bits 16 bits 32 bits 64 bits 21 077 complemento complemento complemento complemento 0xDC00 a dos a dos a dos a dos (byte b=20;) (short s=25;) (int i=900;) (long l=0xC;)

Reales en coma flotante:


float double Por ejemplo: 32 bits 64 bits 3.14 2e12 IEEE 754 IEEE 754 3.1E12

double miPi = 314.16e-2 ; float temperatura = (float)36.6;

Booleanos:
true false

Caracteres:
Por ejemplo: unicode a \t \u???? [????] es un nmero

Cadenas:
Por ejemplo: "Esto es una cadena literal"

Descripcin Caracter Unicode Numero octal Barra invertida Continuacin Retroceso Retorno de carro Alimentacin de formularios Tabulacin horizontal Lnea nueva Comillas simples Comillas dobles Nmeros arbigos ASCII Alfabeto ASCII en maysculas Alfabeto ASCII en minsculas

Representacin \udddd \ddd \\ \ \b \r \f \t \n \ \" 0-9 A.-Z a.-z

Valor Unicode

\u005C \ \u0008 \u000D \u000C \u0009 \u000A \u0027 \u0022 \u0030 a \u0039 \u0041 a \u005A \u0061 a \u007A

Tabla 7: Caracteres especiales Java


Ejemplos : char letraMayuscula = 'A'; char letraV = '\u0056';

Arrays
Se pueden declarar en Java arrays de cualquier tipo:

char s[]; int iArray[];

Incluso se pueden construir arrays de arrays:


int tabla[][] = new int[4][5];

Los lmites de los arrays se comprueban en tiempo de ejecucin para evitar desbordamientos y la corrupcin de memoria. En Java un array es realmente un objeto, porque tiene redefinido el operador []. Tiene una funcin miembro: length. Se puede utilizar este mtodo para conocer la longitud de cualquier array.
int a[][] = new int[10][3]; a.length; /* 10 */ a[0].length; /* 3 */

Para crear un array en Java hay dos mtodos bsicos. Crear un array vaco:
int lista[] = new int[50];

o se puede crear ya el array con sus valores iniciales:


String nombres[] = { "Juan","Pepe","Pedro","Maria" };

Esto que es equivalente a:


String nombres[]; nombres = new String[4]; nombres[0] = new String( nombres[1] = new String( nombres[2] = new String( nombres[3] = new String(

"Juan" ); "Pepe" ); "Pedro" ); "Maria" );

No se pueden crear arrays estticos en tiempo de compilacin:


int lista[50]; // generar un error en tiempo de compilacin

Tampoco se puede rellenar un array sin declarar el tamao con el operador new:
int lista[]; for( int i=0; i < 9; i++ ) lista[i] = i;

Es decir, todos los arrays en Java son estticos. Para convertir un array en el equivalente a un array dinmico en C/C++, se usa la clase insercin, borrado, etc. en el array.

vector, que permite operaciones de

Crear un vector
Para usar la clase Vector hemos de poner al principo del archivo del cdigo fuente la siguiente sentencia import
import java.util.*;

Cuando creamos un vector u objeto de la clase Vector, podemos especificar su dimensin inicial, y cuanto crecer si rebasamos dicha dimensin.
Vector vector=new Vector(20, 5);

Tenemos un vector con una dimensin inicial de 20 elementos. Si rebasamos dicha dimensin y guardamos 21 elementos la dimensin del vector crece a 25. Al segundo constructor, solamente se le pasa la dimensin inicial.
Vector vector=new Vector(20);

Si se rebasa la dimensin inicial guardando 21 elementos, la dimensin del vector se duplica. El programador ha de tener cuidado con este constructor, ya que si se pretende guardar un nmero grande de elementos se tiene que especificar el incremento de la capacidad del vector, si no se quiere desperdiciar intilmente la memoria el ordenador. Con el tercer constructor, se crea un vector cuya dimensin inicial es 10.
Vector vector=new Vector();

La dimensin del vector se duplica si se rebasa la dimensin inicial, por ejemplo, cuando se pretende guardar once elementos.

Aadir elementos al vector


Hay dos formas de aadir elementos a un vector. Podemos aadir un elemento a continuacin del ltimo elemento del vector, mediante la funcin miembro addElement.
v.addElement("uno");

Podemos tambin insertar un elemento en una determinada posicin, mediante insertElementAt. El segundo parmetro o ndice, indica el lugar que ocupar el nuevo objeto. Si tratamos de insertar un elemento en una posicin que no existe todava obtenemos una excepcin del tipo ArrayIndexOutOfBounds. Por ejemplo, si tratamos de insertar un elemento en la posicin 9 cuando el vector solamente tiene cinco elementos. Para insertar el string "tres" en la tercera posicin del vector v, escribimos
v.insertElementAt("tres", 2);

En la siguiente porcin de cdigo, se crea un vector con una capacidad inicial de 10 elementos, valor por defecto, y se le aaden o insertan objetos de la clase String.
Vector v=new Vector(); v.addElement("uno"); v.addElement("dos"); v.addElement("cuatro"); v.addElement("cinco");

v.addElement("seis"); v.addElement("siete"); v.addElement("ocho"); v.addElement("nueve"); v.addElement("diez"); v.addElement("once"); v.addElement("doce"); v.insertElementAt("tres", 2);

Para saber cuantos elementos guarda un vector, se llama a la funcin miembro size. Para saber la dimensin actual de un vector se llama a la funcin miembro capacity. Por ejemplo, en la porcin de cdigo hemos guardado 12 elementos en el vector v. La dimensin de v es 20, ya que se ha superado la dimensin inicial de 10 establecida en la llamada al tercer constructor cuando se ha creado el vector v.
System.out.println("n de elementos "+v.size()); System.out.println("dimensin "+v.capacity());

Podemos eliminar todos los elementos de un vector, llamando a la funcin miembro removeAllElements. O bien, podemos eliminar un elemento concreto, por ejemplo el que guarda el string "tres".
v.removeElement("tres");

Podemos eliminar dicho elemento, si especificamos su ndice.


v.removeElementAt(2);

Acceso a los elementos de un vector


El acceso a los elementos de un vector no es tan sencillo como el acceso a los elementos de un array. En vez de dar un ndice, usamos la funcin miembro elementAt. Por ejemplo, v.elementAt(4) sera equivalente a v[4], si v fuese un array. Para acceder a todos lo elementos del vector, escribimos un cdigo semejante al empleado para acceder a todos los elementos de un array.
for(int i=0; i<v.size(); i++){ System.out.print(v.elementAt(i)+"\t"); }

Operadores
Los operadores son un tipo de tokens que indican una evaluacin o computacin para ser realizada en objetos o datos, y en definitiva sobre identificadores o constantes.

Adems de realizar la operacin, un operador devuelve un valor, ya que son parte fundamental de las expresiones. El valor y tipo que devuelve depende del operador y del tipo de sus operandos. Por ejemplo, los operadores aritmticos devuelven un nmero como resultado de su operacin. Los operadores realizan alguna funcin sobre uno, dos o tres operandos. Los operadores que requieren un operando son llamados operadores unarios. Por ejemplo, el operador "++" es un operador unario que incrementa el valor de su operando en una unidad. Los operadores unarios en Java pueden utilizar tanto la notacin prefija como la posfija. La notacin prefija indica que el operador aparece antes que su operando. ++contador // Notacin prefija, se evala a: contador+1 La notacin posfija indica que el operador aparece despus de su operando: contador++ // Notacin posfija, se evala a: contador Los operadores que requieren dos operandos se llaman operadores binarios. Por ejemplo el operador "=" es un operador binario que asigna el valor del operando del lado derecho al operando del lado izquierdo. Todas los operadores binarios en Java utilizan notacin infija, lo cual indica que el operador aparece entre sus operandos. operando1 operador operando2 Por ltimo, los operadores ternarios son aquellos que requieren tres operandos. El lenguaje Java tiene el operador ternario, "?":, que es una sentencia similar a la if-else. Este operador ternario usa notacin infija; y cada parte del operador aparece entre operandos: expresin ? operacin1 : operacin2 Los operadores de Java se pueden dividir en las siguientes cuatro categoras:

Aritmticos.
De comparacin y condicionales. A nivel de bits y lgicos. De asignacin.

B. Operadores aritmticos El lenguaje Java soporta varios operadores aritmticos para los nmeros enteros y en coma flotante. Se incluye + (suma), - (resta), * (multiplicacin), / (divisin), y % (mdulo, es decir, resto de una divisin entera). Por ejemplo: sumaEste + aEste; //Suma los dos enteros divideEste % entreEste; //Calcula el resto de dividir 2 enteros Operador + * / % Uso op1 + op2 op1 - op2 op1 * op2 op1 / op2 op1 % op2 Descripcin Suma op1 y op2 Resta op2 de op1 Multiplica op1 por op2 Divide op1 por op2 Calcula el resto de dividir op1 entre op2

Tabla 9: Operadores aritmticos binarios de Java El tipo de los datos devueltos por una operacin aritmtica depende del tipo de sus operandos; si se suman dos enteros, se obtiene un entero como tipo devuelto con el valor de la suma de los dos enteros. Estos operadores se deben utilizar con operandos del mismo tipo, o si no realizar una conversin de tipos de uno de los dos operandos al tipo del otro. El lenguaje Java sobrecarga la definicin del operador + para incluir la concatenacin de cadenas. El siguiente ejemplo utiliza + para concatenar la cadena "Contados ", con el valor de la variable contador y la cadena " caracteres.": System.out.print("Contados" + contador + "caracteres."); Esta operacin automticamente convierte el valor de contador a una cadena de caracteres. Los operadores + y - tienen versiones unarias que realizan las siguientes operaciones: Operador + Uso +op -op Descripcin Convierte op a entero si es un byte, short o char Niega aritmticamente op

Tabla 10: Versiones unarias de los operadores "+" y "-" El operador - realiza una negacin del nmero en complemento A2, es decir, cambiando de valor todos sus bits y sumando 1 al resultado final:

42 -> 00101010 -42 -> 11010110

Existen dos operadores aritmticos que funcionan como atajo de la combinacin de otros: ++ que incrementa su operando en 1, y -- que decrementa su operando en 1. Ambos operadores tienen una versin prefija, y otra posfija. La utilizacin la correcta es crtica en situaciones donde el valor de la sentencia es utilizado en mitad de un clculo ms complejo, por ejemplo para control de flujos: Operador ++ ++ --Uso op++ ++op op---op Descripcin Incrementa op en 1; se evala al valor anterior al incremento Incrementa op en 1; se evala al valor posterior al incremento Decrementa op en 1; se evala al valor anterior al incremento Decrementa op en 1; se evala al valor posterior al incremento Tabla 11: Operaciones con "++" y "--" C. Operadores de comparacin y condicionales Un operador de comparacin compara dos valores y determina la relacin existente entre ambos. Por ejemplo, el operador != devuelve verdadero (true) si los dos operandos son distintos. La siguiente tabla resume los operadores de comparacin de Java: Operador Uso > op1 > op2 >= op1 >= op2 < op1 < op2 <= op1 <= op2 == op1 == op2 != op1 != op2 Devuelve verdadero si op1 es mayor que op2 op1 es mayor o igual que op2 op1 es menor que op2 op1 es menor o igual que op2 op1 y op2 son iguales op1 y op2 son distintos

Tabla 12: Operadores de comparacin Los operadores de comparacin suelen ser usados con los operadores condicionales para construir expresiones complejas que sirvan para la toma de decisiones. Un operador de este tipo es &&, el cual realiza la operacin booleana and. Por ejemplo, se pueden utilizar dos operaciones diferentes de comparacin con && para determinar si ambas relaciones son ciertas. La siguiente lnea de cdigo utiliza esta tcnica para determinar si la variable index de una matriz se encuentra entre dos lmites (mayor que cero y menor que la constante NUMERO_ENTRADAS): ( 0 < index ) && ( index < NUMERO_ENTRADAS )

Se debe tener en cuenta que en algunos casos, el segundo operando de un operador condicional puede no ser evaluado. En caso de que el primer operando del operador && valga falso, Java no evaluar el operando de la derecha: (contador < NUMERO_ENTRADAS) && ( in.read() != -1 ) Si contador es menor que NUMERO_ENTRADAS, el valor de retorno de && puede ser determinado sin evaluar el operando de la parte derecha. En este caso in.read no ser llamado y un carcter de la entrada estndar no ser ledo. Si el programador quiere que se evale la parte derecha, deber utilizar el operador & en lugar de &&. De la misma manera se relacionan los operadores || y | para la exclusin lgica (OR). Java soporta cinco operadores condicionales, mostrados en la siguiente tabla: Operador && & || | ! Uso op1 && op2 op1 & op2 op1 || op2 op1 | op2 ! op Devuelve verdadero si... op1 y op2 son ambos verdaderos, condicionalmente evala op2 op1 y op2 son ambos verdaderos, siempre evala op1 y op2 op1 o op2 son verdaderos, condicionalmente evala op2 op1 o op2 son verdaderos, siempre evala op1 y op2 op es falso Tabla 13: Operadores condicionales Adems Java soporta un operador ternario, el ?:, que se comporta como una versin reducida de la sentencia if-else: expresion ? operacion1 : operacion2 El operador ?: evala la expresion y devuelve operacin1 si es cierta, o devuelve operacin2 si expresion es falsa.

D. Operadores de bit Un operador de bit permite realizar operaciones de bit sobre los datos. Existen dos tipos: los que desplazan (mueven) bits, y operadores lgicos de bit.

a.) Operadores de desplazamiento de bits

Operador >> << >>>

Uso op1 >> op2 op1 << op2 op1 >>> op2

Operacin Desplaza los bits de op1 a la derecha op2 veces Desplaza los bits de op1 a la izquierda op2 veces Desplaza los bits de op1 a la derecha op2 veces (sin signo)

Tabla 14: Operadores de desplazamiento de bits Los tres operadores de desplazamiento simplemente desplazan los bits del operando de la parte izquierda el nmero de veces indicado por el operando de la parte derecha. El desplazamiento ocurre en la direccin indicada por el operador. Por ejemplo, la siguiente sentencia, desplaza los bits del entero 13 a la derecha una posicin: 13 >> 1; La representacin en binario del nmero 13 es 1101. El resultado de la operacin de desplazamiento es 1101 desplazado una posicin a la derecha, 110 o 6 en decimal. Se debe tener en cuenta que el bit ms a la derecha se pierde en este caso. Un desplazamiento a la derecha una posicin es equivalente a dividir el operando del lado izquierdo por 2, mientras que un desplazamiento a la izquierda de una posicin equivale a multiplicar por 2, pero un desplazamiento es ms eficiente, computacionalmente hablando, que una divisin o multiplicacin. El desplazamiento sin signo >>> funciona de la siguiente manera:

Si se desplaza con signo el nmero -1 (1111), seguir valiendo -1, dado que la extensin de signo sigue introduciendo unos en los bits ms significativos.
Con el desplazamiento sin signo se consigue introducir ceros por la izquierda, obteniendo el nmero 7 (0111).

Este tipo de desplazamientos es especialmente til en la utilizacin de mscaras grficas.

b.) Operadores de lgica de bits

La lgica de bits (lgica de Bool) se utiliza para modelizar condiciones biestado y trabajar con ellas (cierto/falso, true/false, 1/0).

En Java hay cuatro operadores de lgica de bits: Operador & | ^ ~ Uso op1 & op2 op1 | op2 op1 ^ op2 ~op2 Operacin AND OR OR Exclusivo Complemento

Tabla 15: Operadores de lgica de bits El operador & realiza la operacin AND de bit. Aplica la funcin AND sobre cada par de bits de igual peso de cada operando. La funcin AND es evaluada a cierto si ambos operandos son ciertos. Por ejemplo vamos a aplicar la operacin AND a los valores 12 y 13: 12 & 13 El resultado de esta operacin es 12. Por qu?. La representacin en binario de 12 es 1100, y de 13 es 1101. La funcin AND pone el bit de resultado a uno si los dos bits de los operandos son 1, sino, el bit de resultado es 0: 1101 & 1100 -----1100

El operador | realiza la operacin OR de bit. Aplica la funcin OR sobre cada par de bits de igual peso de cada operando. La funcin OR es evaluada a cierto si alguno de los operandos es cierto. El operador ^ realiza la operacin OR exclusivo de bit (XOR). Aplica la funcin XOR sobre cada par de bits de igual peso de cada operando. La funcin XOR es evaluada a cierto si los operandos tienen el mismo valor. Para finalizar, el operador de complemento invierte el valor de cada bit del operando. Convierte el falso en cierto, y el cierto en falso: Entre otras cosas, la manipulacin bit es til para gestionar indicadores booleanos (banderas). Supongamos, por ejemplo, que se tiene varios indicadores booleanos en nuestro programa, los cuales muestran el estado de varios componentes del programa: esVisible, esArrastrable, etc... En lugar de definir una variable booleana para cada indicador, se puede definir una nica variable para todos ellos. Cada bit de dicha variable representar el estado vigente de uno de los indicadores. Se debern utilizar entonces manipulaciones de bit para establecer y leer cada indicador. Primero, se deben preparar las constantes de cada indicador. Esos indicadores deben ser diferentes unos de otros (en sus bits) para asegurar que el bit de activacin no se solape

con otro indicador. Despus se debe definir la variable de banderas, cuyos bits deben de poder ser configurados segn el estado vigente en cada indicador. El siguiente ejemplo inicia la variable de banderas flags a 0, lo que significa que todos los indicadores estn desactivados (ninguno de los bits es 1): final int VISIBLE = 1; final int ARRASTRABLE = 2; final int SELECCIONABLE = 4; final int MODIFICABLE = 8; int flags = 0; Para activar el indicador VISIBLE, se deber usar la sentencia: flags = flags | VISIBLE; Para comprobar la visibilidad se deber usar la sentencia: if ( (flags & VISIBLE) == 1 ) //Lo que haya que hacer

E. Operadores de asignacin El operador de asignacin bsico es el =, que se utiliza para asignar un valor a otro. Por ejemplo: int contador = 0; Inicia la variable contador con un valor 0. Java adems proporciona varios operadores de asignacin que permiten realizar un atajo en la escritura de cdigo. Permiten realizar operaciones aritmticas, lgicas, de bit y de asignacin con un nico operador. Supongamos que necesitamos sumar un nmero a una variable y almacenar el resultado en la misma variable, como a continuacin: i = i + 2; Se puede abreviar esta sentencia con el operador de atajo +=, de la siguiente manera: i += 2;

La siguiente tabla muestra los operadores de atajo de asignacin y sus equivalentes largos: Operador += -= *= /= %= &= Uso op1 += op2 op1 -= op2 op1 *= op2 op1 /= op2 op1 %= op2 op1 &= op2 Equivalente a op1 = op1 + op2 op1 = op1 - op2 op1 = op1 * op2 op1 = op1 / op2 op1 = op1 % op2 op1 = op1 & op2

Tabla 16: Operadores de atajo de asignacin F. Precedencia de operadores Cuando en una sentencia aparecen varios operadores el compilador deber de elegir en qu orden aplica los operadores. A esto se le llama precedencia. Los operadores con mayor precedencia son evaluados antes que los operadores con una precedencia relativa menor. Cuando en una sentencia aparecen operadores con la misma precedencia:

Los operadores de asignacin son evaluados de derecha a izquierda.


Los operadores binarios, (menos los de asignacin) son evaluados de izquierda a derecha.

Se puede indicar explcitamente al compilador de Java cmo se desea que se evale la expresin con parntesis balanceados ( ). Para hacer que el cdigo sea ms fcil de leer y mantener, es preferible ser explcito e indicar con parntesis que operadores deben ser evaluados primero. La siguiente tabla muestra la precedencia asignada a los operadores de Java. Los operadores de la tabla estn listados en orden de precedencia: cuanto ms arriba aparezca un operador, mayor es su precedencia. Los operadores en la misma lnea tienen la misma precedencia: Tipo de operadores Operadores posfijos Operadores unarios Creacin o conversin Multiplicacin Suma Desplazamiento Operadores de este tipo [ ] . (parametros) expr++ expr-++expr --expr +expr -expr ~ ! new (tipo) expr */% +<<

Comparacin Igualdad AND a nivel de bit OR a nivel de bit XOR a nivel de bit AND lgico OR lgico Condicional Asignacin

< <= = instanceof == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= = =

Tabla 17: Precedencia de operadores Por ejemplo, la siguiente expresin produce un resultado diferente dependiendo de si se realiza la suma o divisin en primer lugar: x + y / 100 Si no se indica explcitamente al compilador el orden en que se quiere que se realicen las operaciones, entonces el compilador decide basndose en la precedencia asignada a los operadores. Como el operador de divisin tiene mayor precedencia que el operador de suma el compilador evaluar y/100 primero. As: x + y / 100 Es equivalente a: x + (y / 100)

Separadores
Slo hay un par de secuencias con otros caracteres que pueden aparecer en el cdigo Java; son los separadores simples, que van a definir la forma y funcin del cdigo. Los separadores admitidos en Java son:

() - parntesis. Para contener listas de parmetros en la definicin y llamada a mtodos. Tambin se utiliza para definir precedencia en expresiones, contener expresiones para control de flujo y rodear las conversiones de tipo. {} - llaves. Para contener los valores de matrices inicializadas automticamente. Tambin se utiliza para definir un bloque de cdigo, para clases, mtodos y mbitos locales.

[] - corchetes. Para declarar tipos matriz. Tambin se utiliza cuando se referencian valores de matriz. ; - punto y coma. Separa sentencias. , - coma. Separa identificadores consecutivos en una declaracin de variables. Tambin se utiliza para encadenar sentencias dentro de una sentencia for. . - punto. Para separar nombres de paquete de subpaquetes y clases. Tambin se utiliza para separar una variable o mtodo de una variable de referencia.

CONTROL DE FLUJO
El lenguaje Java soporta las estructuras de control: Sentencia Toma de decisin Bucle Miscelneo Clave if-else, switch-case for, while, do-while break, continue, label:, return, goto

Tabla 18: Estructuras de control Aunque goto es una palabra reservada, actualmente el lenguaje Java no soporta la sentencia goto. Se puede utilizar las sentencias de bifurcacin en su lugar. B. Las sentencias condicionales: if y switch
a.) La sentencia if - else

La sentencia if-else de Java dota a los programas de la habilidad de ejecutar distintos conjuntos de sentencias segn algn criterio. La sintaxis de la sentencia if-else es:

if ( condicin ) Bloque de cdigo a ejecutar si la condicin es cierta else Bloque de cdigo a ejecutar si la condicin es falsa

La parte del else es opcional, y un bloque de cdigo puede ser simplemente la sentencia vaca ; para representar que en ese caso no se ha de ejecutar nada. Supongamos que un programa debe realizar diferentes acciones dependiendo de si el usuario oprime el botn aceptar o el botn cancelar en una ventana de dialogo. Nuestro programa puede realizar esto usando la sentencia if - else:

// La respuesta es Aceptar o Cancelar if (respuesta == Aceptar) { // cdigo para realizar la accin Aceptar System.out.println( "Su peticion esta siendo atendida" ); } else { // cdigo para realizar la accin Cancelar System.out.println( "Cancelando accion" ); }

Se pueden anidar expresiones if-else, para poder implementar aquellos casos con mltiples acciones. Esto es lo que se suele denominar como sentencias else if. Por ejemplo, supongamos que se desea escribir un programa que clasifique segn el contenido de una variable valor, asigne una letra a una variable clasificacion: A para un valor del 100-91, B de 90-81, C para 80-71 y F si no es ninguno de los anteriores: int valor; char clasificacion; if (valor > 90) {clasificacion='A';} else if (valor > 80) {clasificacion='B';} else if (valor > 70)

{clasificacion='C';} else {clasificacion='F';}

Se pueden escribir los if en las mismas lneas que los else, pero desde este tutorial se insta a utilizar la forma indentada (como se ha podido ver en el ejemplo), pues es ms clara para el lector. Este sistema de programacin (else if) no es demasiado recomendable, y por ello el lenguaje Java incluye la sentencia switch, que veremos a continuacin, para dirigir el flujo de control de variables con mltiples valores.
b.) La sentencia switch

Mediante la sentencia switch se puede seleccionar entre varias sentencias segn el valor de cierta expresin. La forma general de switch es la siguiente: switch ( expresionMultivalor ) { case valor1 : conjuntoDeSentencias; break; case valor2 : conjuntoDeSentencias; break; case valor3: conjuntoDeSentencias; break; default: conjuntoDeSentencias; break; }

La sentencia switch evala la expresinMultivalor y ejecuta el conjuntoDeSentencias que aparece junto a la clusula case cuyo valor corresponda con el de la expresinMultivalor. Cada sentencia case debe ser nica y el valor que evala debe ser del mismo tipo que el devuelto por la expresinMultivalor de la sentencia switch. Las sentencias break que aparecen tras cada conjuntoDeSentencias provocan que el control salga del switch y contine con la siguiente instruccin al switch. Las sentencias break son necesarias porque sin ellas se ejecutaran secuencialmente las sentencias case siguientes. Existen ciertas situaciones en las que se desea ejecutar secuencialmente algunas o todas las sentencias case, para lo que habr que eliminar algunos break.

Finalmente, se puede usar la sentencia default para manejar los valores que no son explcitamente contemplados por alguna de las sentencias case. Su uso es altamente recomendado. Por ejemplo, supongamos un programa con una variable entera meses cuyo valor indica el mes actual, y se desea imprimir el nombre del mes en que estemos. Se puede utilizar la sentencia switch para realizar esta operacin: int meses; switch ( meses ){ case 1: System.out.println( "Enero" ); break; case 2: System.out.println( "Febrero" ); break; case 3: System.out.println( "Marzo" ); break; //Demas meses // . . . case 12: System.out.println( "Diciembre" ); break; default: System.out.println( "Mes no valido" ); break; }

Por supuesto, se puede implementar esta estructura como una sentencia if else if: int meses; if ( meses == 1 ) { System.out.println( "Enero" ); } else if ( meses == 2 ) { System.out.println( "Febrero" ); } // Y as para los dems meses

El decidir si usar la sentencia if o switch depende del criterio de cada caso. Se puede decidir cul usar basndonos en la legibilidad, aunque se recomienda utilizar switch para sentencias con ms de tres o cuatro posibilidades. C. Sentencias de iteracin o bucles: for, do, while
a.) Bucle while

El bucle while es el bucle bsico de iteracin. Sirve para realizar una accin sucesivamente mientras se cumpla una determinada condicin. La forma general del bucle while es la siguiente: while ( expresinBooleana ) { sentencias; };

Las sentencias se ejecutan mientras la expresinBooleana tenga un valor de verdadero. Se utiliza, por ejemplo para estar en un bucle del que no hay que salir hasta que no se cumpla una determinada condicin. Por ejemplo, multiplicar un nmero por 2 hasta que sea mayor que 100: int i = 1; while ( i <= 100 ) { i = i * 2; }

Con l se podran eliminar los bucles do-while y for por ser extensiones de ste, pero que se incluyen en el lenguaje para facilitar la programacin.
b.) Bucle do-while

El bucle do-while es similar al bucle while, pero en el bucle while la expresin se evala al principio del bucle y en el bucle do-while la evaluacin se realiza al final. La forma general del bucle do-while es la siguiente: do { sentencias; } while ( expresinBooleana );

La sentencia do-while es el constructor de bucles menos utilizado en la programacin, pero tiene sus usos, cuando el bucle deba ser ejecutado por lo menos una vez. Por ejemplo, cuando se lee informacin de un archivo, se sabe que siempre se debe leer por lo menos un carcter:

int c; do { c = System.in.read( ); // Sentencias para tratar el carcter c } while ( c != -1 ); // No se puede leer ms (Fin fichero)

c.) Bucle for

Mediante la sentencia for se resume un bucle do-while con una iniciacin previa. Es muy comn que en los bucles while y do-while se inicien las variables de control de nmero de pasadas por el bucle, inmediatamente antes de comenzar los bucles. Por eso el bucle for est tan extendido. La forma general de la sentencia for es la siguiente: for ( iniciacin ; terminacin ; incremento ) sentencias; La iniciacin es una sentencia que se ejecuta una vez antes de entrar en el bucle. La terminacin es una expresin que determina cundo se debe terminar el bucle. Esta expresin se evala al final de cada iteracin del bucle. Cuando la expresin se evala a falso, el bucle termina. El incremento es una expresin que es invocada en cada iteracin del bucle. En realidad puede ser una accin cualquiera, aunque se suele utilizar para incrementar una variable contador: for ( i = 0 ; i < 10 ; i++ ) Algunos (o todos) estos componentes pueden omitirse, pero los puntos y coma siempre deben aparecer (aunque sea sin nada entre s). Se debe utilizar el bucle for cuando se conozcan las restricciones del bucle (su instruccin de iniciacin, criterio de terminacin e instruccin de incremento).

Por ejemplo, los bucles for son utilizados comnmente para iterar sobre los elementos de una matriz, o los caracteres de una cadena: // cad es una cadena (String) for ( int i = 0; i < cad.length() ; i++){ // hacer algo con el elemento i-simo de cad }

D. Sentencias de salto: break, continue y return


a.) Sentencia break

La sentencia break provoca que el flujo de control salte a la sentencia inmediatamente posterior al bloque en curso. Ya se ha visto anteriormente la sentencia break dentro de la sentencia switch. El uso de la sentencia break con sentencias etiquetadas es una alternativa al uso de la sentencia goto, que no es soportada por el lenguaje Java. Se puede etiquetar una sentencia poniendo una identificador Java vlido seguido por dos puntos antes de la sentencia: nombreSentencia: sentenciaEtiquetada La sentencia break se utiliza para salir de una sentencia etiquetada, llevando el flujo del programa al final de la sentencia de programa que indique: break nombreSentencia2; Un ejemplo de esto sera el programa: void gotoBreak() { System.out.println("Ejemplo de break como 'goto' "); a: for( int i=1; i<10; i++ ){ System.out.print(" i="+i); for( int j=1; j<10; j++ ){ if ( j==5 ) break a; //Sale de los dos bucles!!! System.out.print(" j="+j);

} System.out.print("No llega aqu"); } }

Al interpretar break a, no solo se rompe la ejecucin del bucle interior (el de j), sino que se salta al final del bucle i, obtenindose: i=1 j=1 j=2 j=3 Nota: Se desaconseja esta forma de programacin, basada en goto, y con saltos de flujo no controlados.
b.) Sentencia continue

Del mismo modo que en un bucle se puede desear romper la iteracin, tambin se puede desear continuar con el bucle, pero dejando pasar una determinada iteracin. Se puede usar la sentencia continue dentro de los bucles para saltar a otra sentencia, aunque no puede ser llamada fuera de un bucle. Tras la invocacin a una sentencia continue se transfiere el control a la condicin de terminacin del bucle, que vuelve a ser evaluada en ese momento, y el bucle contina o no dependiendo del resultado de la evaluacin. En los bucles for adems en ese momento se ejecuta la clusula de incremento (antes de la evaluacin). Por ejemplo el siguiente fragmento de cdigo imprime los nmeros del 0 al 9 no divisibles por 3:

for ( int i = 0 ; i < 10 ; i++ ) { if ( ( i % 3 ) == 0 ) continue; System.out.print( " " + i ); }

Del mismo modo que break, en las sentencias continue se puede indicar una etiqueta de bloque al que hace referencia. Con ello podemos referirnos a un bloque superior, si estamos en bucles anidados. Si dicha etiqueta no es indicada, se presupone que nos referimos al bucle en el que la sentencia continue aparece. Por ejemplo, el siguiente fragmento de cdigo: void gotoContinue( ) {

f: for ( int i=1; i <5; i++ ) { for ( int j=1; j<5; j++ ) { if ( j>i ) { System.out.println(" "); continue f; } System.out.print( " " + (i*j) ); } } }

En este cdigo la sentencia continue termina el bucle de j y continua el flujo en la siguiente iteracin de i. Ese mtodo imprimira: 1 2 4 3 6 9 4 8 12 16 Nota: Se desaconseja esta forma de programacin, basada en goto, y con saltos de flujo no controlados.
c.) Sentencia return

La ltima de las sentencias de salto es la sentencia return, que puede usar para salir del mtodo en curso y retornar a la sentencia dentro de la cual se realiz la llamada. Para devolver un valor, simplemente se debe poner el valor (o una expresin que calcule el valor) a continuacin de la palabra return. El valor devuelto por return debe coincidir con el tipo declarado como valor de retorno del mtodo. Cuando un mtodo se declara como void se debe usar la forma de return sin indicarle ningn valor. Esto se hace para no ejecutar todo el cdigo del programa: int contador; boolean condicion;

int devuelveContadorIncrementado(){ return ++contador; } void metodoReturn(){ //Sentencias if ( condicion == true ) return; //Ms sentencias a ejecutar si condicin no vale true }

CLASES (no aceptable)


Las clases son lo ms simple de Java. Todo en Java forma parte de una clase, es una clase o describe como funciona una clase. El conocimiento de las clases es fundamental para poder entender los programas Java. Todas las acciones de los programas Java se colocan dentro del bloque de una clase o un objeto. Todos los mtodos se definen dentro del bloque de la clase, Java no soporta funciones o variables globales. Esto puede despistar a los programadores de C++, que pueden definir mtodos fuera del bloque de la clase, pero esta posibilidad es ms un intento de no separarse mucho y ser compatible con C, que un buen diseo orientado a objetos. As pues, el esqueleto de cualquier aplicacin Java se basa en la definicin de una clase. Todos los datos bsicos, como los enteros, se deben declarar en las clases antes de hacer uso de ellos. En C la unidad fundamental son los ficheros con cdigo fuente, en Java son las clases. De hecho son pocas las sentencias que se pueden colocar fuera del bloque de una clase. La palabra clave import (equivalente al #include) puede colocarse al principio de un fichero, fuera del bloque de la clase. Sin embargo, el compilador reemplazar esa sentencia con el contenido del fichero que se indique, que consistir, como es de suponer, en ms clases.

Tipos de Clases
Hasta ahora slo se ha utilizado la palabra clave public para calificar el nombre de las clases que hemos visto, pero hay tres modificadores ms. Los tipos de clases que podemos definir son:

abstract
Una clase abstract tiene al menos un mtodo abstracto. Una clase abstracta no se instancia, sino que se utiliza como clase base para la herencia.

final
Una clase final se declara como la clase que termina una cadena de herencia. No se puede heredar de una clase final. Por ejemplo, la clase Math es una clase final.

public
Las clases public son accesibles desde otras clases, bien sea directamente o por herencia. Son accesibles dentro del mismo paquete en el que se han declarado. Para acceder desde otros paquetes, primero tienen que ser importadas.

synchronizable
Este modificador especifica que todos los mtodos definidos en la clase son sincronizados, es decir, que no se puede acceder al mismo tiempo a ellos desde distintos threads; el sistema se encarga de colocar los flags necesarios para evitarlo. Este mecanismo hace que desde threads diferentes se puedan modificar las mismas variables sin que haya problemas de que se sobreescriban.

VARIABLES Y METODOS DE INSTANCIA


Una clase en Java puede contener variables y mtodos. Las variables pueden ser tipos primitivos como int, char, etc. Los mtodos son funciones. Por ejemplo, en el siguiente trozo de cdigo podemos observarlo:
public MiClase { int i; public MiClase() { i = 10; } public void Suma_a_i( int j ) { i = i + j; } }

La clase MiClase contiene una variable (i) y dos mtodos, MiClase que es el constructor de la clase y Suma_a_i( int j ).

Ambito de una variable


Los bloques de sentencias compuestas en Java se delimitan con dos llaves. Las variables de Java slo son vlidas desde el punto donde estn declaradas hasta el final de la sentencia compuesta que la engloba. Se pueden anidar estas sentencias compuestas, y

cada una puede contener su propio conjunto de declaraciones de variables locales. Sin embargo, no se puede declarar una variable con el mismo nombre que una de mbito exterior. El siguiente ejemplo intenta declarar dos variables separadas con el mismo nombre. En C y C++ son distintas, porque estn declaradas dentro de mbitos diferentes. En Java, esto es ilegal.
Class Ambito { int i = 1; { int i = 2; } } // mbito exterior // crea un nuevo mbito // error de compilacin

Mtodos y Constructores
Los mtodos son funciones que pueden ser llamadas dentro de la clase o por otras clases. El constructor es un tipo especfico de mtodo que siempre tiene el mismo nombre que la clase. Cuando se declara una clase en Java, se pueden declarar uno o ms constructores opcionales que realizan la inicializacin cuando se instancia (se crea una ocurrencia) un objeto de dicha clase. Utilizando el cdigo de ejemplo anterior, cuando se crea una nueva instancia de MiClase, se crean (instancian) todos los mtodos y variables, y se llama al constructor de la clase:
MiClase mc; mc = new MiClase();

La palabra clave new se usa para crear una instancia de la clase. Antes de ser instanciada con new no consume memoria, simplemente es una declaracin de tipo. Despus de ser instanciado un nuevo objeto mc, el valor de i en el objeto mc ser igual a 10. Se puede referenciar la variable (de instancia) i con el nombre del objeto:
mc.i++; // incrementa la instancia de i de mc
Al tener mc todas las variables y mtodos de MiClase, se puede usar la primera sintaxis para llamar al mtodo Suma_a_i() utilizando el nuevo nombre de clase mc:

mc.Suma_a_i( 10 );
y ahora la variable mc.i vale 21.

Finalizadores
Java no utiliza destructores (al contrario que C++) ya que tiene una forma de recoger automticamente todos los objetos que se salen del alcance. No obstante proporciona un mtodo que, cuando se especifique en el cdigo de la clase, el reciclador de memoria (garbage collector) llamar:
// Cierra el canal cuando este objeto es reciclado protected void finalize() { close();

ALCANCE DE OBJETOS Y RECICLADO DE MEMORIA


Los objetos tienen un tiempo de vida y consumen recursos durante el mismo. Cuando un objeto no se va a utilizar ms, debera liberar el espacio que ocupaba en la memoria de forma que las aplicaciones no la agoten (especialmente las grandes). En Java, la recoleccin y liberacin de memoria es responsabilidad de un thread llamado automatic garbage collector (recolector automtico de basura). Este thread monitoriza el alcance de los objetos y marca los objetos que se han salido de alcance. Veamos un ejemplo:
String s; s = new String( "abc" ); s = "def"; // // // // no se ha asignado todavia memoria asignada se ha asignado nueva memoria (nuevo objeto)

Ms adelante veremos en detalle la clase String, pero una breve descripcin de lo que hace esto es; crear un objeto String y rellenarlo con los caracteres "abc" y crear otro (nuevo) String y colocarle los caracteres "def". En esencia se crean dos objetos:
Objeto String "abc" Objeto String "def"

Al final de la tercera sentencia, el primer objeto creado de nombre s que contiene "abc" se ha salido de alcance. No hay forma de acceder a l. Ahora se tiene un nuevo objeto llamado s y contiene "def". Es marcado y eliminado en la siguiente iteracin del thread reciclador de memoria.

CLASES (opcin2) II.5. CLASES Y OBJETOS A. Introduccin Durante los captulos anteriores se han dado unas nociones bsicas de la sintaxis de Java. A partir de ahora es cuando entramos la verdadera potencia de Java como lenguaje orientado a objetos: las clases y los objetos. Aquellas personas que nunca hayan programado en un lenguaje orientado a objeto, o que no conozcan las nociones bsicas de paradigma conviene que lean el captulo "I.1 Introduccin a la programacin orientada a objetos" de este tutorial, ya que a partir de ahora los conceptos que en l se exponen se darn por entendidos.

Durante todo este captulo se va a trabajar en la construccin de una clase MiPunto, que modeliza un punto en un espacio plano: class MiPunto{ int x, y; int metodoSuma( int paramX, int paramY ) { return ( paramX + paramY ); } double distancia(int x, int y) { int dx= this.x pX; int dy = this.y pY; return Math.sqrt(dx*dx + dy*dy); } void metodoVacio( ) { } void inicia( int paramX, int paramY ) { x = paramX; y = paramY; } void inicia2( int x, int y ) { x = x; // Ojo, no modificamos la variable de instancia!!! this.y = y; // Modificamos la variable de instancia!!! } MiPunto( int paramX, int paramY ) { this.x = paramX; // Este this se puede omitir y = paramY; // No hace falta this } MiPunto() {

inicia(-1,-1); //Por defecto ; this(-1,-1) hace lo mismo } }

B. Definicin de una clase


a.) Introduccin

El elemento bsico de la programacin orientada a objetos en Java es la clase. Una clase define la forma y comportamiento de un objeto. Para crear una clase slo se necesita un archivo fuente que contenga la palabra clave reservada class seguida de un identificador legal y un bloque delimitado por dos llaves para el cuerpo de la clase. class MiPunto { } Un archivo de Java debe tener el mismo nombre que la clase que contiene, y se les suele asignar la extensin ".java". Por ejemplo la clase MiPunto se guardara en un fichero que se llamase MiPunto.java. Hay que tener presente que en Java se diferencia entre maysculas y minsculas; el nombre de la clase y el de archivo fuente han de ser exactamente iguales. Aunque la clase MiPunto es sintcticamente correcta, es lo que se viene a llamar una clase vaca, es decir, una clase que no hace nada. Las clases tpicas de Java incluirn variables y mtodos de instancia. Los programas en Java completos constarn por lo general de varias clases de Java en distintos archivos fuente. Una clase es una plantilla para un objeto. Por lo tanto define la estructura de un objeto y su interfaz funcional, en forma de mtodos. Cuando se ejecuta un programa en Java, el sistema utiliza definiciones de clase para crear instancias de las clases, que son los objetos reales. Los trminos instancia y objeto se utilizan de manera indistinta. La forma general de una definicin de clase es: class Nombre_De_Clase { tipo_de_variable nombre_de_atributo1; tipo_de_variable nombre_de_atributo2; // . . . tipo_devuelto nombre_de_mtodo1( lista_de_parmetros ) {

cuerpo_del_mtodo1; } tipo_devuelto nombre_de_mtodo2( lista_de_parmetros ) { cuerpo_del_mtodo2; } // . . . }

Los tipos tipo_de_variable y tipo_devuelto, han de ser tipos simples Java o nombres de otras clases ya definidas. Tanto Nombre_De_Clase, como los nombre_de_atributo y nombre_de_mtodo, han de ser identificadores Java vlidos.
b.) Los atributos

Los datos se encapsulan dentro de una clase declarando variables dentro de las llaves de apertura y cierre de la declaracin de la clase, variables que se conocen como atributos. Se declaran igual que las variables locales de un mtodo en concreto. Por ejemplo, este es un programa que declara una clase MiPunto, con dos atributos enteros llamados x e y. class MiPunto { int x, y; }

Los atributos se pueden declarar con dos clases de tipos: un tipo simple Java (ya descritos), o el nombre de una clase (ser una referencia a objeto, vase el punto C.a de este mismo apartado). Cuando se realiza una instancia de una clase (creacin de un objeto) se reservar en la memoria un espacio para un conjunto de datos como el que definen los atributos de una clase. A este conjunto de variables se le denomina variables de instancia.
c.) Los mtodos

Los mtodos son subrutinas que definen la interfaz de una clase, sus capacidades y comportamiento. Un mtodo ha de tener por nombre cualquier identificador legal distinto de los ya utilizados por los nombres de la clase en que est definido. Los mtodos se declaran al mismo nivel que las variables de instancia dentro de una definicin de clase.

En la declaracin de los mtodos se define el tipo de valor que devuelven y a una lista formal de parmetros de entrada, de sintaxis tipo identificador separadas por comas. La forma general de una declaracin de mtodo es: tipo_devuelto nombre_de_mtodo( lista-formal-de-parmetros ) { cuerpo_del_mtodo; }

Por ejemplo el siguiente mtodo devuelve la suma de dos enteros: int metodoSuma( int paramX, int paramY ) { return ( paramX + paramY ); }

En el caso de que no se desee devolver ningn valor se deber indicar como tipo la palabra reservada void. As mismo, si no se desean parmetros, la declaracin del mtodo debera incluir un par de parntesis vacos (sin void): void metodoVacio( ) { }; Los mtodos son llamados indicando una instancia individual de la clase, que tendr su propio conjunto nico de variables de instancia, por lo que los mtodos se pueden referir directamente a ellas. El mtodo inicia() para establecer valores a las dos variables de instancia sera el siguiente: void inicia( int paramX, int paramY ) { x = paramX; y = paramY; }

C. La instanciacin de las clases: Los objetos


a.) Referencias a Objeto e Instancias

Los tipos simples de Java describan el tamao y los valores de las variables. Cada vez que se crea una clase se aade otro tipo de dato que se puede utilizar igual que uno de los tipos simples. Por ello al declarar una nueva variable, se puede utilizar un nombre de clase como tipo. A estas variables se las conoce como referencias a objeto.

Todas las referencias a objeto son compatibles tambin con las instancias de subclases de su tipo. Del mismo modo que es correcto asignar un byte a una variable declarada como int, se puede declarar que una variable es del tipo MiClase y guardar una referencia a una instancia de este tipo de clase: MiPunto p; Esta es una declaracin de una variable p que es una referencia a un objeto de la clase MiPunto, de momento con un valor por defecto de null. La referencia null es una referencia a un objeto de la clase Object, y se podr convertir a una referencia a cualquier otro objeto porque todos los objetos son hijos de la clase Object.
b.) Constructores

Las clases pueden implementar un mtodo especial llamado constructor. Un constructor es un mtodo que inicia un objeto inmediatamente despus de su creacin. De esta forma nos evitamos el tener que iniciar las variables explcitamente para su iniciacin. El constructor tiene exactamente el mismo nombre de la clase que lo implementa; no puede haber ningn otro mtodo que comparta su nombre con el de su clase. Una vez definido, se llamar automticamente al constructor al crear un objeto de esa clase (al utilizar el operador new). El constructor no devuelve ningn tipo, ni siquiera void. Su misin es iniciar todo estado interno de un objeto (sus atributos), haciendo que el objeto sea utilizable inmediatamente; reservando memoria para sus atributos, iniciando sus valores... Por ejemplo: MiPunto( ) { inicia( -1, -1 ); }

Este constructor denominado constructor por defecto, por no tener parmetros, establece el valor -1 a las variables de instancia x e y de los objetos que construya. El compilador, por defecto ,llamar al constructor de la superclase Object() si no se especifican parmetros en el constructor. Este otro constructor, sin embargo, recibe dos parmetros: MiPunto( int paraX, int paraY ) { inicia( paramX, paramY ); }

La lista de parmetros especificada despus del nombre de una clase en una sentencia new se utiliza para pasar parmetros al constructor. Se llama al mtodo constructor justo despus de crear la instancia y antes de que new devuelva el control al punto de la llamada. As, cuando ejecutamos el siguiente programa: MiPunto p1 = new MiPunto(10, 20); System.out.println( "p1.- x = " + p1.x + " y = " + p1.y ); Se muestra en la pantalla: p1.- x = 10 y = 20 Para crear un programa Java que contenga ese cdigo, se debe de crear una clase que contenga un mtodo main(). El intrprete java se ejecutar el mtodo main de la clase que se le indique como parmetro. Para ms informacin sobre cmo crear y ejecutar un programa, as como los tipos de programas que se pueden crear en Java, vase el captulo "II.12. Creacin de programas Java" de este tutorial.
c.) El operador new

El operador new crea una instancia de una clase (objetos) y devuelve una referencia a ese objeto. Por ejemplo: MiPunto p2 = new MiPunto(2,3); Este es un ejemplo de la creacin de una instancia de MiPunto, que es controlador por la referencia a objeto p2. Hay una distincin crtica entre la forma de manipular los tipos simples y las clases en Java: Las referencias a objetos realmente no contienen a los objetos a los que referencian. De esta forma se pueden crear mltiples referencias al mismo objeto, como por ejemplo: MiPunto p3 =p2; Aunque tan slo se cre un objeto MiPunto, hay dos variables (p2 y p3) que lo referencian. Cualquier cambio realizado en el objeto referenciado por p2 afectar al objeto referenciado por p3. La asignacin de p2 a p3 no reserva memoria ni modifica el objeto. De hecho, las asignaciones posteriores de p2 simplemente desengancharn p2 del objeto, sin afectarlo: p2 = null; // p3 todava apunta al objeto creado con new

Aunque se haya asignado null a p2, p3 todava apunta al objeto creado por el operador new. Cuando ya no haya ninguna variable que haga referencia a un objeto, Java reclama automticamente la memoria utilizada por ese objeto, a lo que se denomina recogida de basura. Cuando se realiza una instancia de una clase (mediante new) se reserva en la memoria un espacio para un conjunto de datos como el que definen los atributos de la clase que se indica en la instanciacin. A este conjunto de variables se le denomina variables de instancia. La potencia de las variables de instancia es que se obtiene un conjunto distinto de ellas cada vez que se crea un objeto nuevo. Es importante el comprender que cada objeto tiene su propia copia de las variables de instancia de su clase, por lo que los cambios sobre las variables de instancia de un objeto no tienen efecto sobre las variables de instancia de otro. El siguiente programa crea dos objetos MiPunto y establece los valores de x e y de cada uno de ellos de manera independiente para mostrar que estn realmente separados. MiPunto p4 = new MiPunto( 10, 20 ); MiPunto p5 = new MiPunto( 42, 99 ); System.out.println("p4.- x = " + p4.x + " y = " + p4.y); System.out.println("p5.- x = " + p5.x + " y = " + p5.y); Este es el aspecto de salida cuando lo ejecutamos. p4.- x = 10 y = 20 p5.- x = 42 y = 99 D. Acceso al objeto
a.) El operador punto (.)

El operador punto (.) se utiliza para acceder a las variables de instancia y los mtodos contenidos en un objeto, mediante su referencia a objeto: referencia_a_objeto.nombre_de_variable_de_instancia referencia_a_objeto.nombre_de_mtodo( lista-de-parmetros ); Hemos creado un ejemplo completo que combina los operadores new y punto para crear un objeto MiPunto, almacenar algunos valores en l e imprimir sus valores finales:

MiPunto p6 = new MiPunto( 10, 20 ); System.out.println ("p6.- 1. X=" + p6.x + " , Y=" + p6.y); p6.inicia( 30, 40 ); System.out.println ("p6.- 2. X=" + p6.x + " , Y=" + p6.y); Cuando se ejecuta este programa, se observa la siguiente salida: p6.- 1. X=10 , Y=20 p6.- 2. X=30 , Y=40 Durante las impresiones (mtodo println()) se accede al valor de las variables mediante p6.x y p6.y, y entre una impresin y otra se llama al mtodo inicia(), cambiando los valores de las variables de instancia. Este es uno de los aspectos ms importantes de la diferencia entre la programacin orientada a objetos y la programacin estructurada. Cuando se llama al mtodo p6.inicia(), lo primero que se hace en el mtodo es sustituir los nombres de los atributos de la clase por las correspondientes variables de instancia del objeto con que se ha llamado. As por ejemplo x se convertir en p6.x. Si otros objetos llaman a inicia(), incluso si lo hacen de una manera concurrente, no se producen efectos laterales, ya que las variables de instancia sobre las que trabajan son distintas.
b.) La referencia this

Java incluye un valor de referencia especial llamado this, que se utiliza dentro de cualquier mtodo para referirse al objeto actual. El valor this se refiere al objeto sobre el que ha sido llamado el mtodo actual. Se puede utilizar this siempre que se requiera una referencia a un objeto del tipo de una clase actual. Si hay dos objetos que utilicen el mismo cdigo, seleccionados a travs de otras instancias, cada uno tiene su propio valor nico de this. Un refinamiento habitual es que un constructor llame a otro para construir la instancia correctamente. El siguiente constructor llama al constructor parametrizado MiPunto(x,y) para terminar de iniciar la instancia: MiPunto() { this( -1, -1 ); // Llama al constructor parametrizado }

En Java se permite declarar variables locales, incluyendo parmetros formales de mtodos, que se solapen con los nombres de las variables de instancia.

No se utilizan x e y como nombres de parmetro para el mtodo inicia, porque ocultaran las variables de instancia x e y reales del mbito del mtodo. Si lo hubisemos hecho, entonces x se hubiera referido al parmetro formal, ocultando la variable de instancia x: void inicia2( int x, int y ) { x = x; // Ojo, no modificamos la variable de instancia!!! this.y = y; // Modificamos la variable de instancia!!! }

E. La destruccin del objeto


a.) La destruccin de los objetos

Cuando un objeto no va a ser utilizado, el espacio de memoria de dinmica que utiliza ha de ser liberado, as como los recursos que posea, permitiendo al programa disponer de todos los recursos posibles. A esta accin se la da el nombre de destruccin del objeto. En Java la destruccin se puede realizar de forma automtica o de forma personalizada, en funcin de las caractersticas del objeto.
b.) La destruccin por defecto: Recogida de basura

El intrprete de Java posee un sistema de recogida de basura, que por lo general permite que no nos preocupemos de liberar la memoria asignada explcitamente. El recolector de basura ser el encargado de liberar una zona de memoria dinmica que haba sido reservada mediante el operador new, cuando el objeto ya no va a ser utilizado ms durante el programa (por ejemplo, sale del mbito de utilizacin, o no es referenciado nuevamente). El sistema de recogida de basura se ejecuta peridicamente, buscando objetos que ya no estn referenciados.
c.) La destruccin personalizada: finalize

A veces una clase mantiene un recurso que no es de Java como un descriptor de archivo o un tipo de letra del sistema de ventanas. En este caso sera acertado el utilizar la finalizacin explcita, para asegurar que dicho recurso se libera. Esto se hace mediante la destruccin personalizada, un sistema similar a los destructores de C++. Para especificar una destruccin personalizada se aade un mtodo a la clase con el nombre finalize: class ClaseFinalizada{

ClaseFinalizada() { // Constructor // Reserva del recurso no Java o recurso compartido } protected void finalize() { // Liberacin del recurso no Java o recurso compartido } }

El intrprete de Java llama al mtodo finalize(), si existe cuando vaya a reclamar el espacio de ese objeto, mediante la recogida de basura. Debe observarse que el mtodo finalize() es de tipo protected void y por lo tanto deber de sobreescribirse con este mismo tipo.

HERENCIA
La Herencia es el mecanismo por el que se crean nuevos objetos definidos en trminos de objetos ya existentes. Por ejemplo, si se tiene la clase Ave, se puede crear la subclase Pato, que es una especializacin de Ave.
class Pato extends Ave { int numero_de_patas; }

La palabra clave extends se usa para generar una subclase (especializacin) de un objeto. Una Pato es una subclase de Ave. Cualquier cosa que contenga la definicin de Ave ser copiada a la clase Pato, adems, en Pato se pueden definir sus propios mtodos y variables de instancia. Se dice que Pato deriva o hereda de Ave. Adems, se pueden sustituir los mtodos proporcionados por la clase base. Utilizando nuestro anterior ejemplo de MiClase, aqu hay un ejemplo de una clase derivada sustituyendo a la funcin Suma_a_i():
import MiClase; public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); } }

Ahora cuando se crea una instancia de MiNuevaClase, el valor de i tambin se inicializa a 10, pero la llamada al mtodo Suma_a_i() produce un resultado diferente:
MiNuevaClase mnc; mnc = new MiNuevaClase(); mnc.Suma_a_i( 10 );
En Java no se puede hacer herencia mltiple. Por ejemplo, de la clase aparato con motor y de la clase animal no se puede derivar nada, sera como obtener el objeto toro mecnico a partir de una mquina motorizada (aparato con motor) y un toro (aminal). En realidad, lo que se pretende es copiar los mtodos, es decir, pasar la funcionalidad del toro de verdad al toro mecnico, con lo cual no sera necesaria la herencia mltiple sino simplemente la comparticin de funcionalidad que se encuentra implementada en Java a travs de interfaces.

CONTROL DE ACCESO
Cuando se crea una nueva clase en Java, se puede especificar el nivel de acceso que se quiere para las variables de instancia y los mtodos definidos en la clase: public
public void CualquieraPuedeAcceder(){}
Cualquier clase desde cualquier lugar puede acceder a las variables y mtodos de instacia pblicos.

protected
protected void SoloSubClases(){}
Slo las subclases de la clase y nadie ms puede acceder a las variables y mtodos de instancia protegidos.

private
private String NumeroDelCarnetDeIdentidad;
Las variables y mtodos de instancia privados slo pueden ser accedidos desde dentro de la clase. No son accesibles desde las subclases.

friendly (sin declaracin especfica)


void MetodoDeMiPaquete(){}
Por defecto, si no se especifica el control de acceso, las variables y mtodos de instancia se declaran friendly (amigas), lo que significa que son accesibles

por todos los objetos dentro del mismo paquete, pero no por los externos al paquete. Es lo mismo que protected.

Los mtodos protegidos (protected) pueden ser vistos por las clases derivadas, como en C++, y tambin, en Java, por los paquetes (packages). Todas las clases de un paquete pueden ver los mtodos protegidos de ese paquete. Para evitarlo, se deben declarar como private protected, lo que hace que ya funcione como en C++ en donde slo se puede acceder a las variables y mtodos protegidos de las clases derivadas.

VARIABLES Y METODOS ESTATICOS


En un momento determinado se puede querer crear una clase en la que el valor de una variable de instancia sea el mismo (y de hecho sea la misma variable) para todos los objetos instanciados a partir de esa clase. Es decir, que exista una nica copia de la variable de instancia. Se usar para ello la palabra clave static.
class Documento extends Pagina { static int version = 10; }

El valor de la variable version ser el mismo para cualquier objeto instanciado de la clase Documento. Siempre que un objeto instanciado de Documento cambie la variable version, sta cambiar para todos los objetos. De la misma forma se puede declarar un mtodo como esttico, lo que evita que el mtodo pueda acceder a las variables de instancia no estticas:
class Documento extends Pagina { static int version = 10; int numero_de_capitulos; static void annade_un_capitulo() { numero_de_capitulos++; // esto no funciona } static void modifica_version( int i ) { version++; // esto si funciona } }

La modificacin de la variable numero_de_capitulos no funciona porque se est violando una de las reglas de acceso al intentar acceder desde un mtodo esttico a una variable no esttica. Todas las clases que se derivan, cuando se declaran estticas, comparten la misma pgina de variables; es decir, todos los objetos que se generen comparten la misma zona de memoria. Las funciones estticas se usan para acceder solamente a variables estticas.
class UnaClase { int var; UnaClase() { var = 5; } UnaFuncion()

{ var += 5; } }

En el cdigo anterior, si se llama a la funcin UnaFuncion a travs de un puntero a funcin, no se podra acceder a var, porque al utilizar un puntero a funcin no se pasa implcitamente el puntero al propio objeto (this). Sin embargo, s se podra acceder a var si fuese esttica, porque siempre estara en la misma posicin de memoria para todos los objetos que se creasen de UnaClase.

this Y super
Al acceder a variables de instancia de una clase, la palabra clave this hace referencia a los miembros de la propia clase. Volviendo al ejemplo de MiClase, se puede aadir otro constructor de la forma siguiente:
public class MiClase { int i; public MiClase() { i = 10; } // Este constructor establece el valor de i public MiClase( int valor ) { this.i = valor; // i = valor } public void Suma_a_i( int j ) { i = i + j; } }

Aqu this.i se refiere al entero i en la clase MiClase. Si se necesita llamar al mtodo padre dentro de una clase que ha reemplazado ese mtodo, se puede hacer referencia al mtodo padre con la palabra clave super:
import MiClase; public class MiNuevaClase extends MiClase { public void Suma_a_i( int j ) { i = i + ( j/2 ); super.Suma_a_i( j ); } }

En el siguiente cdigo, el constructor establecer el valor de i a 10, despus lo cambiar a 15 y finalmente el mtodo Suma_a_i() de la clase padre (MiClase) lo dejar en 25:
MiNuevaClase mnc; mnc = new MiNuevaClase(); mnc.Suma_a_i( 10 );

CLASES ABSTRACTAS

Una de las caractersticas ms tiles de cualquier lenguaje orientado a objetos es la posibilidad de declarar clases que definen como se utiliza solamente, sin tener que implementar mtodos. Esto es muy til cuando la implementacin es especfica para cada usuario, pero todos los usuarios tienen que utilizar los mismos mtodos. Un ejemplo de clase abstracta en Java es la clase Graphics:
public abstract class Graphics { public abstract void drawLine( int x1,int y1,int x2, int y2 ); public abstract void drawOval( int x,int y,int width, int height ); public abstract void drawArc( int x,int y,int width, int height,int startAngle,int arcAngle ); . . . }

Los mtodos se declaran en la clase Graphics, pero el cdigo que ejecutar el mtodo est en algn otro sitio:
public class MiClase extends Graphics { public void drawLine( int x1,int y1,int x2,int y2 ) { <cdigo para pintar lneas -especfico de la arquitectura-> } }

Cuando una clase contiene un mtodo abstracto tiene que declararse abstracta. No obstante, no todos los mtodos de una clase abstracta tienen que ser abstractos. Las clases abstractas no pueden tener mtodos privados (no se podran implementar) ni tampoco estticos. Una clase abstracta tiene que derivarse obligatoriamente, no se puede hacer un new de una clase abstracta. Una clase abstracta en Java es lo mismo que en C++ virtual func() = 0; lo que obliga a que al derivar de la clase haya que implementar forzosamente los mtodos de esa clase abstracta.

INTERFACES
Los mtodos abstractos son tiles cuando se quiere que cada implementacin de la clase parezca y funcione igual, pero necesita que se cree una nueva clase para utilizar los mtodos abstractos. Los interfaces proporcionan un mecanismo para abstraer los mtodos a un nivel superior. Un interface contiene una coleccin de mtodos que se implementan en otro lugar. Los mtodos de una clase son public, static y final. La principal diferencia entre interface y abstract es que un interface proporciona un mecanismo de encapsulacin de los protocolos de los mtodos sin forzar al usuario a utilizar la herencia.

Por ejemplo:
public interface VideoClip { // comienza la reproduccion del video void play(); // reproduce el clip en un bucle void bucle(); // detiene la reproduccion void stop(); }

Las clases que quieran utilizar el interface VideoClip utilizarn la palabra implements y proporcionarn el cdigo necesario para implementar los mtodos que se han definido para el interface:
class MiClase implements VideoClip { void play() { <cdigo> } void bucle() { <cdigo> } void stop() { <cdigo> }

Al utilizar implements para el interface es como si se hiciese una accin de copiar-ypegar del cdigo del interface, con lo cual no se hereda nada, solamente se pueden usar los mtodos. La ventaja principal del uso de interfaces es que una clase interface puede ser implementada por cualquier nmero de clases, permitiendo a cada clase compartir el interfaz de programacin sin tener que ser consciente de la implementacin que hagan las otras clases que implementen el interface.
class MiOtraClase implements VideoClip { void play() { <cdigo nuevo> } void bucle() { <cdigo nuevo> } void stop() { <cdigo nuevo> }

METODOS NATIVOS
Java proporciona un mecanismo para la llamada a funciones C y C++ desde nuestro cdigo fuente Java. Para definir mtodos como funciones C o C++ se utiliza la palabra clave native.

public class Fecha { int ahora; public Fecha() { ahora = time(); } private native int time(); static { System.loadLibrary( "time" ); } }

Una vez escrito el cdigo Java, se necesitan ejecutar los pasos siguientes para poder integrar el cdigo C o C++:

Utilizar javah para crear un fichero de cabecera (.h) Utilizar javah para crear un fichero de stubs, es decir, que contiene la declaracin de las funciones Escribir el cdigo del mtodo nativo en C o C++, es decir, rellenar el cdigo de la funcin, completando el trabajo de javah al crear el fichero de stubs Compilar el fichero de stubs y el fichero .c en una librera de carga dinmica (DLL en Windows '95 o libXX.so en Unix) Ejecutar la aplicacin con el appletviewer

Ms adelante trataremos en profundidad los mtodos nativos, porque aaden una gran potencia a Java, al permitirle integrar a travs de librera dinmica cualquier algoritmo desarrollado en C o C++, lo cual, entre otras cosas, se utiliza como mtodo de proteccin contra la descompilacin completa del cdigo Java.

PAQUETES
La palabra clave package permite agrupar clases e interfaces. Los nombres de los paquetes son palabras separadas por puntos y se almacenan en directorios que coinciden con esos nombres. Por ejemplo, los ficheros siguientes, que contienen cdigo fuente Java:
Applet.java, AppletContext.java, AppletStub.java, AudioClip.java

contienen en su cdigo la lnea:


package java.applet;

Y las clases que se obtienen de la compilacin de los ficheros anteriores, se encuentran con el nombre nombre_de_clase.class, en el directorio:
java/applet

Import
Los paquetes de clases se cargan con la palabra clave import, especificando el nombre del paquete como ruta y nombre de clase (es lo mismo que #include de C/C++). Se pueden cargar varias clases utilizando un asterisco.

import java.Date;

import java.awt.*;
Si un fichero fuente Java no contiene ningn package, se coloca en el paquete por defecto sin nombre. Es decir, en el mismo directorio que el fichero fuente, y la clase puede ser cargada con la sentencia import:

import MiClase;

Paquetes de Java
El lenguaje Java proporciona una serie de paquetes que incluyen ventanas, utilidades, un sistema de entrada/salida general, herramientas y comunicaciones. En la versin actual del JDK, los paquetes Java que se incluyen son:

java.applet
Este paquete contiene clases diseadas para usar con applets. Hay una clase Applet y tres interfaces: AppletContext, AppletStub y AudioClip.

java.awt
El paquete Abstract Windowing Toolkit (awt) contiene clases para generar widgets y componentes GUI (Interfaz Grfico de Usuario). Incluye las clases Button, Checkbox, Choice, Component, Graphics, Menu, Panel, TextArea y TextField.

java.io
El paquete de entrada/salida contiene las clases de acceso a ficheros: FileInputStream y FileOutputStream.

java.lang
Este paquete incluye las clases del lenguaje Java propiamente dicho: Object, Thread, Exception, System, Integer, Float, Math, String, etc.

java.net
Este paquete da soporte a las conexiones del protocolo TCP/IP y, adems, incluye las clases Socket, URL y URLConnection.

java.util
Este paquete es una miscelnea de clases tiles para muchas cosas en programacin. Se incluyen, entre otras, Date (fecha), Dictionary (diccionario), Random (nmeros aleatorios) y Stack (pila FIFO).

REFERENCIAS EN JAVA
Las referencias en Java no son punteros ni referencias como en C++. Este hecho crea un poco de confusin entre los programadores que llegan por primera vez a Java. Las referencias en Java son identificadores de instancias de las clases Java. Una referencia dirige la atencin a un objeto de un tipo especfico. No tenemos por qu saber cmo lo hace ni necesitamos saber qu hace ni, por supuesto, su implementacin.

Pensemos en una referencia como si se tratase de la llave electrnica de la habitacin de un hotel. Vamos a utilizar precisamente este ejemplo del Hotel para demostrar el uso y la utilizacin que podemos hacer de las referencias en Java. Primero crearemos la clase Habitacion, implementada en el fichero Habitacion.java, mediante instancias de la cual construiremos nuestro Hotel:
public class Habitacion { private int numHabitacion; private int numCamas; public Habitacion() { habitacion( 0 ); } public Habitacion( int numeroHab ) { habitacion( numeroHab ); } public Habitacion( int numeroHab,int camas ) { habitacion( numeroHab ); camas( camas ); } public synchornized int habitacion() { return( numHabitacion ); } public synchronized void habitacion( int numeroHab ) { numHabitacion = numeroHab; } public synchronized int camas() { return( camas ); } public syncronized void camas( int camas ) { numCamas = camas; } }

El cdigo anterior sera el corazn de la aplicacin. Vamos pues a construir nuestro Hotel creando Habitaciones y asignndole a cada una de ellas su llave electrnica; tal como muestra el cdigo siguiente, Hotel1.java:
public class Hotel1 { public static void main( String args[] ) { Habitacion llaveHab1; Habitacion llaveHab2; llaveHab1 = new Habitacion( 222 ); 5 // // } llaveHab2 = new Habitacion( 1144,3 ); ^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^ A B y D C }

// paso 1

// pasos 2, 3, 4 y

Para explicar el proceso, dividimos las acciones en los cinco pasos necesarios para poder entrar en nuestra habitacin. Aunque no se incluye, podemos tambin considerar el caso de que necesitemos un cerrajero, para que cuando perdamos la llave, nos abra la puerta; y que en nuestro caso sera el garbage collector, que recicla la habitacin una vez que se hayan perdido todas las llaves. El primer paso es la creacin de la llave, es decir, definir la variable referencia, por defecto nula. El resto de los pasos se agrupan en una sola sentencia Java. La parte B en el cdigo anterior indica al gerente del Hotel que ya dispone de una nueva habitacin. La parte C llama al decorador de interiores para que "vista" la habitacin segn un patrn determinado, para que no desentonen unas habitaciones con otras y no se pierdan las seas de identidad del hotel. El cdigo electrnico que nos permitir acceder a la habitacin se genera en la parte D, una vez conocido el interior de la habitacin y se programa en la llave en la parte A. Si dejamos el ejemplo real a un lado y nos vamos a lo que ocurre en la ejecucin del cdigo, vemos que el operador new busca espacio para una instancia de un objeto de una clase determinada e inicializa la memoria a los valores adecuados. Luego invoca al mtodo constructor de la clase, proporcionndole los argumentos adecuados. El operador new devuelve una referencia a s mismo, que es inmediatamente asignada a la variable referencia. Podemos tener mltiples llaves para una misma habitacin:
. . . Habitacion llaveHab3,llaveHab4; llaveHab3 = llaveHab1; llaveHab4 = llavehab2;

De este modo conseguimos copias de las llaves. Las habitaciones en s mismas no se han tocado en este proceso. As que, ya tenemos dos llaves para la habitacin 222 y otras dos para la habitacin 1144. Una llave puede ser programada para que funcione solamente con una habitacin en cualquier momento, pero podemos cambiar su cdigo electrnico para que funcione con alguna otra habitacin; por ejemplo, para cambiar una habitacin anteriormente utilizada por un empedernido fumador por otra limpia de olores y con vistas al mar. Cambiemos pues la llave duplicada de la habitacin del fumador (la 222) por la habitacin con olor a sal marina, 1144:
. . . llaveHab3 = llaveHab2;

Ahora tenemos una llave para la habitacin 222 y tres para la habitacin 1144. Mantendremos una llave para cada habitacin en la conserjera, para poder utilizarla como llave maestra, en el caso de que alguien pierda su llave propia. Alguien con la llave de una habitacin puede hacer cambios en ella, y los compaeros que tengan llave de esa misma habitacin, no tendrn conocimiento de esos cambios

hasta que vuelvan a entrar en la habitacin. Por ejemplo, vamos a quitar una de las camas de la habitacin, entrando en ella con la llave maestra:
. . . llaveHab2.camas( 2 );

Ahora cuando los inquilinos entren en la habitacin podrn comprobar el cambio realizado:
. . . llaveHab4.printData();

REFERENCIAS Y ARRAYS
Como en C y C++, Java dispone de arrays de tipos primitivos o de clases. Los arrays en C y C++ son bsicamente un acompaante para los punteros. En Java, sin embargo, son ciudadanos de primera clase. Vamos a expandir nuestro hotel creando todo un ala de habitaciones, Hotel2.java. Crearemos un juego de llaves maestras y luego construiremos las habitaciones:
public class Hotel2 { // Nmero de habitaciones por ala public static final int habPorAla = 12; public static void main( String args[] ) { Habitacion llavesMaestras[]; // paso 1 llavesMaestras = new Habitacion[ habPorAla ]; // pasos 2-5 ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ A B, C, D y E int numPiso = 1; for( int i=0; i < habPorAla; i++ ) // pasos 6-9 llavesMaestras[ i ] = new Habitacion( numPiso * 100 + i, ( 0 == (i%2)) ? 2 : 1 ); for( int i=0; i < habPorAla; i++ ) // pasos 10-11 llavesMaestras[i].printData(); } }

// //

Cada paso en el ejemplo es semejante al que ya vimos antes. El paso 1 especifica que el juego de llaves maestras es un grupo de llaves de habitaciones. Los pasos 2 a 5 son, en este caso, la parte principal. En lugar de crear una habitacin, el gerente ordena construir un grupo contiguo de habitaciones. El nmero de llaves se especifica entre corchetes y todas se crean en blanco. Los pasos 6 a 9 son idnticos a los pasos 2 a 5 del ejemplo anterior, excepto en que en este caso todas las llaves pasan a formar parte del juego maestro. Los nmeros de piso se dan en miles para que cuando se creen las habitaciones, todas tengan el mismo formato. Tambin todas las habitaciones de nmero par tienen una sola cama, mientras que las habitaciones impares tendrn dos camas. Los pasos 10 y 11 nos permiten obtener informacin de cada una de las habitaciones.

Clases embebidas (Inner classes)


Concepto
Una clase embebida es una clase que se define dentro de otra. Es una caracterstica de Java que permite agrupar clases lgicamente relacionadas y controlar la 'visibilidad' de una clase. El uso de las clases embebidas no es obvio y contienen detallas algo ms complejos que escapan del mbito de esta introduccin. Se puede definir una clase embebida de la siguiente forma: class Externa { . . . class Interna { . . . } } La clase Externa puede instanciar y usar la clase Interna como cualquier otra, sin limitacin ni cambio en la sintaxis de acceso: class Externa { . . . class Interna { . . . } void metodo() { Interna i = new Interna(. . .); . . . } } Una diferencia importante es que un objeto de la clase embebida est relacionado siempre con un objeto de la clase que la envuelve, de tal forma que las instancias de la clase embebida deben ser creadas por una instancia de la clase que la envuelve. Desde el exterior estas referencias pueden manejarse, pero calificandolas completamente, es decir nombrando la clase externa y luego la interna. Adems una instancia de la clase embebida tiene acceso a todos los datos miembros de la clase que la envuelve sin usar ningn calificador de acceso especial (como si le pertenecieran). Todo esto se ve en el ejemplo siguiente.

Ejemplo
Un ejemplo donde puede apreciarse fcilmente el uso de clases embebidas es el concepto de iterador. Un iterador es una clase diseada para recorrer y devolver ordenadamente los elementos de algn tipo de contenedor de objetos. En el ejemplo se hace una implementacin muy elemental que trabaja sobre un array.

class Almacen { private Object [] listaObjetos; private numElementos = 0; Almacen (int maxElementos) { listaObjetos = new Object[maxElementos]; } public Object get(int i) { return listaObject[i]; } public void add(Object obj) { listaObjetos[numElementos++] = obj; } public Iterador getIterador() { new Iterador(); } class Iterador { int indice = 0; Object siguiente() { if (indice < numElementos) return listaObjetos[indice++]; else return null; } } } Y la forma de usarse, sera: Almacen alm = new Almacen(10); // se crea un nuevo almacen . . . alm.add(...); // se aaden objetos . . . // para recorrerlo Almacen.Iterador i = alm.getIterador(); // obtengo un iterador para alm Object o; while ( (o = i.siguiente()) != null) { . . . }

MANEJO DE EXCEPCIONES
Vamos a mostrar como se utilizan las excepciones, reconvirtiendo nuestro applet de saludo a partir de la versin iterativa de HolaIte.java:
import java.awt.*; import java.applet.Applet; public class HolaIte extends Applet {

private int i = 0; private String Saludos[] = { "Hola Mundo!", "HOLA Mundo!", "HOLA MUNDO!!" }; public void paint( Graphics g ) { g.drawString( Saludos[i],25,25 ); i++; } }

Normalmente, un programa termina con un mensaje de error cuando se lanza una excepcin. Sin embargo, Java tiene mecanismos para excepciones que permiten ver qu excepcin se ha producido e intentar recuperarse de ella. Vamos a reescribir el mtodo paint() de nuestra versin iterativa del saludo:
public void paint( Graphics g ) { try { g.drawString( Saludos[i],25,25 ); } catch( ArrayIndexOutOfBoundsException e ) { g.drawString( "Saludos desbordado",25,25 ); } catch( Exception e ) { // Cualquier otra excepcin System.out.println( e.toString() ); } finally { System.out.println( "Esto se imprime siempre!" ); } i++; }

La palabra clave finally define un bloque de cdigo que se quiere que sea ejecutado siempre, de acuerdo a si se captur la excepcin o no. En el ejemplo anterior, la salida en la consola, con i=4 sera:
Saludos desbordado Esto se imprime siempre!

GENERAR EXCEPCIONES EN JAVA


Cuando se produce un error se debera generar, o lanzar, una excepcin. Para que un mtodo en Java, pueda lanzar excepciones, hay que indicarlo expresamente.

void MetodoAsesino() throws NullPointerException,CaidaException

Se pueden definir excepciones propias, no hay por qu limitarse a las predefinidas; bastar con extender la clase Exception y proporcionar la funcionalidad extra que requiera el tratamiento de esa excepcin. Tambin pueden producirse excepciones no de forma explcita como en el caso anterior, sino de forma implcita cuando se realiza alguna accin ilegal o no vlida. Las excepciones, pues, pueden originarse de dos modos: el programa hace algo ilegal (caso normal), o el programa explcitamente genera una excepcin ejecutando la sentencia throw (caso menos normal). La sentencia throw tiene la siguiente forma:
throw ObtejoExcepction;

El objeto ObjetoException es un objeto de una clase que extiende la clase Exception. El siguiente cdigo de ejemplo origina una excepcin de divisin por cero:
class melon { public static void main( String[] a ) { int i=0, j=0, k; k = i/j; } } // Origina un error de division-by-zero

Si compilamos y ejecutamos esta aplicacin Java, obtendremos la siguiente salida por pantalla:
> javac melon.java > java melon java.lang.ArithmeticException: / by zero at melon.main(melon.java:5)

Las excepciones predefinidas, como ArithmeticException, se conocen como excepciones runtime. Actualmente, como todas las excepciones son eventos runtime, sera mejor llamarlas excepciones irrecuperables. Esto contrasta con las excepciones que generamos explcitamente, que suelen ser mucho menos severas y en la mayora de los casos podemos recuperarnos de ellas. Por ejemplo, si un fichero no puede abrirse, preguntamos al usuario que nos indique otro fichero; o si una estructura de datos se encuentra completa, podremos sobreescribir algn elemento que ya no se necesite.

EXCEPCIONES PREDEFINIDAS
Las excepciones predefinidas y su jerarqua de clases es la que se muestra en la figura:

Los nombres de las excepciones indican la condicin de error que representan. Las siguientes son las excepciones predefinidas ms frecuentes que se pueden encontrar: ArithmeticException Las excepciones aritmticas son tpicamente el resultado de una divisin por 0:
int i = 12 / 0;

NullPointerException

Se produce cuando se intenta acceder a una variable o mtodo antes de ser definido:
class Hola extends Applet { Image img; paint( Graphics g ) { g.drawImage( img,25,25,this ); } }

IncompatibleClassChangeException El intento de cambiar una clase afectada por referencias en otros objetos, especficamente cuando esos objetos todava no han sido recompilados. ClassCastException El intento de convertir un objeto a otra clase que no es vlida.
y = (Prueba)x; // donde x no es de tipo Prueba

NegativeArraySizeException Puede ocurrir si hay un error aritmtico al intentar cambiar el tamao de un array. OutOfMemoryException No debera producirse nunca! El intento de crear un objeto con el operador new ha fallado por falta de memoria. Y siempre tendra que haber memoria suficiente porque el garbage collector se encarga de proporcionarla al ir liberando objetos que no se usan y devolviendo memoria al sistema. NoClassDefFoundException Se referenci una clase que el sistema es incapaz de encontrar. ArrayIndexOutOfBoundsException Es la excepcin que ms frecuentemente se produce. Se genera al intentar acceder a un elemento de un array ms all de los lmites definidos inicialmente para ese array. UnsatisfiedLinkException Se hizo el intento de acceder a un mtodo nativo que no existe. Aqu no existe un mtodo a.kk
class A {

native void kk(); }


y se llama a a.kk(), cuando debera llamar a A.kk().

InternalException Este error se reserva para eventos que no deberan ocurrir. Por definicin, el usuario nunca debera ver este error y esta excepcin no debera lanzarse.

CREAR EXCEPCIONES PROPIAS


Tambin podemos lanzar nuestras propias excepciones, extendiendo la clase System.exception. Por ejemplo, consideremos un programa cliente/servidor. El cdigo cliente se intenta conectar al servidor, y durante 5 segundos se espera a que conteste el servidor. Si el servidor no responde, el servidor lanzara la excepcin de time-out:
class ServerTimeOutException extends Exception {} public void conectame( String nombreServidor ) throws Exception { int exito; int puerto = 80; exito = open( nombreServidor,puerto ); if( exito == -1 ) throw ServerTimeOutException; }

Si se quieren capturar las propias excepciones, se deber utilizar la sentencia try:


public void encuentraServidor() { ... try { conectame( servidorDefecto ); catch( ServerTimeOutException e ) { g.drawString( "Time-out del Servidor, intentando alternativa", 5,5 ); conectame( servidorAlterno ); } ... }

Cualquier mtodo que lance una excepcin tambin debe capturarla, o declararla como parte de la interface del mtodo. Cabe preguntarse entonces, el porqu de lanzar una excepcin si hay que capturarla en el mismo mtodo. La respuesta es que las excepciones no simplifican el trabajo del control de errores. Tienen la ventaja de que se puede tener muy localizado el control de errores y no tenemos que controlar millones de valores de retorno, pero no van ms all.

CAPTURAR EXCEPCIONES

Las excepciones lanzadas por un mtodo que pueda hacerlo deben recoger en bloque try/catch o try/finally.
int valor; try { for( x=0,valor = 100; x < 100; x ++ ) valor /= x; } catch( ArithmeticException e ) { System.out.println( "Matemticas locas!" ); } catch( Exception e ) { System.out.println( "Se ha producido un error" ); }

try
Es el bloque de cdigo donde se prev que se genere una excepcin. Es como si dijsemos "intenta estas sentencias y mira a ver si se produce una excepcin". El bloque try tiene que ir seguido, al menos, por una clusula catch o una clusula finally

catch
Es el cdigo que se ejecuta cuando se produce la excepcin. Es como si dijsemos "controlo cualquier excepcin que coincida con mi argumento". En este bloque tendremos que asegurarnos de colocar cdigo que no genere excepciones. Se pueden colocar sentencias catch sucesivas, cada una controlando una excepcin diferente. No debera intentarse capturar todas las excepciones con una sola clusula, como esta:
catch( Excepcion e ) { ...
Esto representara un uso demasiado general, podran llegar muchas ms excepciones de las esperadas. En este caso es mejor dejar que la excepcin se propague hacia arriba y dar un mensaje de error al usuario.

Se pueden controlar grupos de excepciones, es decir, que se pueden controlar, a travs del argumento, excepciones semejantes. Por ejemplo:
class Limites extends Exception {} class demasiadoCalor extends Limites {} class demasiadoFrio extends Limites {} class demasiadoRapido extends Limites {} class demasiadoCansado extends Limites {} . . . try { if( temp > 40 ) throw( new demasiadoCalor() ); if( dormir < 8 ) throw( new demasiado Cansado() ); } catch( Limites lim ) { if( lim instanceof demasiadoCalor ) { System.out.println( "Capturada excesivo calor!" ); return; }

if( lim instanceof demasiadoCansado ) { System.out.println( "Capturada excesivo cansancio!" ); return; } } finally System.out.println( "En la clausula finally" );

La clusula catch comprueba los argumentos en el mismo orden en que aparezcan en el programa. Si hay alguno que coincida, se ejecuta el bloque. El operador instanceof se utiliza para identificar exactamente cual ha sido la identidad de la excepcin.

finally
Es el bloque de cdigo que se ejecuta siempre, haya o no excepcin. Hay una cierta controversia entre su utilidad, pero, por ejemplo, podra servir para hacer un log o un seguimiento de lo que est pasando, porque como se ejecuta siempre puede dejarnos grabado si se producen excepciones y nos hemos recuperado de ellas o no. Este bloque finally puede ser til cuando no hay ninguna excepcin. Es un trozo de cdigo que se ejecuta independientemente de lo que se haga en el bloque try. Cuando vamos a tratar una excepcin, se nos plantea el problema de qu acciones vamos a tomar. En la mayora de los casos, bastar con presentar una indicacin de error al usuario y un mensaje avisndolo de que se ha producido un error y que decida si quiere o no continuar con la ejecucin del programa. Por ejemplo, podramos disponer de un dilogo como el que se presenta en el cdigo siguiente:
public class DialogoError extends Dialog { DialogoError( Frame padre ) { super( padre,true ); setLayout( new BorderLayout() ); // Presentamos un panel con continuar o salir Panel p = new Panel(); p.add( new Button( "Continuar?" ) ); p.add( new Button( "Salir" ) ); add( "Center",new Label( "Se ha producido un error. Continuar?" ) ) add( "South",p ); } public boolean action( Event evt,Object obj ) { if( "Salir".equals( obj ) ) { dispose(); System.exit( 1 ); } return false; } }

Y la invocacin, desde algn lugar en que se suponga que se generarn errores, podra ser como sigue:
try { // Cdigo peligroso } catch( AlgunaExcepcion e ) { VentanaError = new DialogoError( this ); VentanaError.show(); }

PROPAGACION DE EXCEPCIONES
La clusula catch comprueba los argumentos en el mismo orden en que aparezcan en el programa. Si hay alguno que coincida, se ejecuta el bloque y sigue el flujo de control por el bloque finally (si lo hay) y concluye el control de la excepcin. Si ninguna de las clusulas catch coincide con la excepcin que se ha producido, entonces se ejecutar el cdigo de la clusula finally (en caso de que la haya). Lo que ocurre en este caso, es exactamente lo mismo que si la sentencia que lanza la excepcin no se encontrase encerrada en el bloque try. El flujo de control abandona este mtodo y retorna prematuramente al mtodo que lo llam. Si la llamada estaba dentro del mbito de una sentencia try, entonces se vuelve a intentar el control de la excepcin, y as continuamente. Veamos lo que sucede cuando una excepcin no es tratada en la rutina en donde se produce. El sistema Java busca un bloque try..catch ms all de la llamada, pero dentro del mtodo que lo trajo aqu. Si la excepcin se propaga de todas formas hasta lo alto de la pila de llamadas sin encontrar un controlador especfico para la excepcin, entonces la ejecucin se detendr dando un mensaje. Es decir, podemos suponer que Java nos est proporcionando un bloque catch por defecto, que imprime un mensaje de error y sale. No hay ninguna sobrecarga en el sistema por incorporar sentencias try al cdigo. La sobrecarga se produce cuando se genera la excepcin. Hemos dicho ya que un mtodo debe capturar las excepciones que genera, o en todo caso, declararlas como parte de su llamada, indicando a todo el mundo que es capaz de generar excepciones. Esto debe ser as para que cualquiera que escriba una llamada a ese mtodo est avisado de que le puede llegar una excepcin, en lugar del valor de retorno normal. Esto permite al programador que llama a ese mtodo, elegir entre controlar la excepcin o propagarla hacia arriba en la pila de llamadas. La siguiente lnea de cdigo muestra la forma general en que un mtodo declara excepciones que se pueden propagar fuera de l:
tipo_de_retorno( parametros ) throws e1,e2,e3 { }

Los nombres e1,e2,... deben ser nombres de excepciones, es decir, cualquier tipo que sea asignable al tipo predefinido Throwable. Observar que, como en la llamada al

mtodo se especifica el tipo de retorno, se est especificando el tipo de excepcin que puede generar (en lugar de un objeto exception). He aqu un ejemplo, tomado del sistema Java de entrada/salida:
byte readByte() throws IOException; short readShort() throws IOException; char readChar() throws IOException; void writeByte( int v ) throws IOException; void writeShort( int v ) throws IOException; void writeChar( int v ) throws IOException;
Lo ms interesante aqu es que la rutina que lee un char, puede devolver un char; no el entero que se requiere en C. C necesita que se devuelva un int, para poder pasar cualquier valor a un char, y adems un valor extra (-1) para indicar que se ha alcanzado el final del fichero. Algunas de las rutinas Java lanzan una excepcin cuando se alcanza el fin del fichero.

En el siguiente diagrama se muestra grficamente cmo se propaga la excepcin que se genera en el cdigo, a travs de la pila de llamadas durante la ejecucin del cdigo:

Cuando se crea una nueva excepcin, derivando de una clase Exception ya existente, se puede cambiar el mensaje que lleva asociado. La cadena de texto puede ser recuperada a travs de un mtodo. Normalmente, el texto del mensaje proporcionar informacin para resolver el problema o sugerir una accin alternativa. Por ejemplo:
class SinGasolina extends Exception { SinGasolina( String s ) { // constructor super( s ); } ....

// Cuando se use, aparecer algo como esto try { if( j < 1 ) throw new SinGasolina( "Usando deposito de reserva" ); } catch( SinGasolina e ) { System.out.println( o.getMessage() ); }

Esto, en tiempo de ejecucin originara la siguiente salida por pantalla:


> Usando deposito de reserva

Otro mtodo que es heredado de la superclase Throwable es printStackTrace(). Invocando a este mtodo sobre una excepcin se volcar a pantalla todas las llamadas hasta el momento en donde se gener la excepcin (no donde se maneje la excepcin). Por ejemplo:
// Capturando una excepcin en un mtodo class testcap { static int slice0[] = { 0,1,2,3,4 }; public static void main( String a[] ) { try { uno(); } catch( Exception e ) { System.out.println( "Captura de la excepcion en main()" ); e.printStackTrace(); } } static void uno() { try { slice0[-1] = 4; } catch( NullPointerException e ) { System.out.println( "Captura una excepcion diferente" ); } } }

Cuando se ejecute ese cdigo, en pantalla observaremos la siguiente salida:


> Captura de la excepcion en main() > java.lang.ArrayIndexOutOfBoundsException: -1 at testcap.uno(test5p.java:19) at testcap.main(test5p.java:9)

Con todo el manejo de excepciones podemos concluir que proporciona un mtodo ms seguro para el control de errores, adems de representar una excelente herramienta para organizar en sitios concretos todo el manejo de los errores y, adems, que podemos proporcionar mensajes de error ms decentes al usuario indicando qu es lo que ha fallado y por qu, e incluso podemos, a veces, recuperarnos de los errores. La degradacin que se produce en la ejecucin de programas con manejo de excepciones est ampliamente compensada por las ventajas que representa en cuanto a seguridad de funcionamiento de esos mismos programas.

Entrada/Salida
Adems de la entrada por teclado y salida por pantalla, se necesita entrada/salida por fichero, como son:
FileInputStream DataInputStream FileOutputStream DataOutputStream

Tambin existen otras clases para aplicaciones ms especficas, que no vamos a tratar, por ser de un uso muy concreto:
PipedInputStream BufferedInputStream PushBackInputStream StreamTokenizer PipedOutputStream BufferedOutputStream

Ficheros
Antes de realizar acciones sobre un fichero, necesitamos un poco de informacin sobre ese fichero. La clase File proporciona muchas utilidades relacionadas con ficheros y con la obtencin de informacin bsica sobre esos ficheros. Creacin de un objeto File Para crear un objeto File nuevo, se puede utilizar cualquiera de los tres constructores siguientes:
File miFichero; miFichero = new File( "/etc/kk" );

o
miFichero = new File( "/etc","kk" );

o
File miDirectorio = new File( "/etc" ); miFichero = new File( miDirectorio,"kk" );

El constructor utilizado depende a menudo de otros objetos File necesarios para el acceso. Por ejemplo, si slo se utiliza un fichero en la aplicacin, el primer constructor es el mejor. Si en cambio, se utilizan muchos ficheros desde un mismo directorio, el segundo o tercer constructor sern ms cmodos. Y si el directorio o el fichero es una variable, el segundo constructor ser el ms til. Comprobaciones y Utilidades

Una vez creado un objeto File, se puede utilizar uno de los siguientes mtodos para reunir informacin sobre el fichero:

Nombres de fichero

String getName() String getPath() String getAbsolutePath() String getParent() boolean renameTo( File nuevoNombre )
Comprobaciones

boolean boolean boolean boolean boolean boolean

exists() canWrite() canRead() isFile() isDirectory() isAbsolute()

Informacin general del fichero

long lastModified() long length()


Utilidades de directorio

boolean mkdir() String[] list()

Vamos a desarrollar una pequea aplicacin que muestra informacin sobre los ficheros pasados como argumentos en la lnea de comandos, InfoFichero.java:
import java.io.*; class InfoFichero { public static void main( String args[] ) throws IOException { if( args.length > 0 ) { for( int i=0; i < args.length; i++ ) { File f = new File( args[i] ); System.out.println( "Nombre: "+f.getName() ); System.out.println( "Camino: "+f.getPath() ); if( f.exists() ) { System.out.print( "Fichero existente " ); System.out.print( (f.canRead() ? " y se puede Leer" : "" ) ); System.out.print( (f.canWrite() ? " y se puese Escribir" : "" ) ); System.out.println( "." ); System.out.println( "La longitud del fichero son "+ f.length()+" bytes" ); } else

System.out.println( "El fichero no existe." ); } } else System.out.println( "Debe indicar un fichero." ); } }

STREAMS DE ENTRADA
Hay muchas clases dedicadas a la obtencin de entrada desde un fichero. Este es el esquema de la jerarqua de clases de entrada por fichero:

Objetos FileInputStream
Los objetos FileInputStream tpicamente representan ficheros de texto accedidos en orden secuencial, byte a byte. Con FileInputStream, se puede elegir acceder a un byte, varios bytes o al fichero completo. Apertura de un FileInputStream Para abrir un FileInputStream sobre un fichero, se le da al constructor un String o un objeto File:
FileInputStream mi FicheroSt; miFicheroSt = new FileInputStream( "/etc/kk" );

Tambin se puede utilizar:


File miFichero FileInputStream miFicheroSt; miFichero = new File( "/etc/kk" ); miFicheroSt = new FileInputStream( miFichero );

Lectura de un FileInputStream Una vez abierto el FileInputStream, se puede leer de l. El mtodo read() tiene muchas opciones:

int read();
Lee un byte y devuelve -1 al final del stream.

int read( byte b[] );


Llena todo el array, si es posible. Devuelve el nmero de bytes ledos o -1 si se alcanz el final del stream.

int read( byte b[],int offset,int longitud );


Lee longitud bytes en b comenzando por b[offset]. Devuelve el nmero de bytes ledos o -1 si se alcanz el final del stream.

Cierre de FileInputStream Cuando se termina con un fichero, existen dos opciones para cerrarlo: explcitamente, o implcitamente cuando se recicla el objeto (el garbage collector se encarga de ello). Para cerrarlo explcitamente, se utiliza el mtodo close():
miFicheroSt.close();

Ejemplo: Visualizacin de un fichero Si la configuracin de la seguridad de Java permite el acceso a ficheros, se puede ver el contenido de un fichero en un objeto TextArea. El cdigo siguiente contiene los elementos necesarios para mostrar un fichero:
FileInputStream fis; TextArea ta; public void init() { byte b[] = new byte[1024]; int i; // El buffer de lectura se debe hacer lo suficientemente grande // o esperar a saber el tamao del fichero String s; try { fis = new FileInputStream( "/etc/kk" ); } catch( FileNotFoundException e ) { /* Hacer algo */ } try { i = fis.read( b ); } catch( IOException e ) { /* Hacer algo */ } s = new String( b,0 ); ta = new TextArea( s,5,40 ); add( ta ); }

Hemos desarrollado un ejemplo, Agenda.java, en el que partimos de un fichero agenda que dispone de los datos que nosotros deseamos de nuestros amigos, como son: nombre, telfono y direccin. Si tecleamos un nombre, buscar en el fichero de datos si existe ese nombre y presentar la informacin que se haya introducido. Para probar, intentar que aparezca la informacin de Pepe.

Objetos DataInputStream
Los objetos DataInputStream se comportan como los FileInputStream. Los streams de datos pueden leer cualquiera de las variables de tipo nativo, como floats, ints o chars. Generalmente se utilizan DataInputStream con ficheros binarios. Apertura y cierre de DataInputStream Para abrir y cerrar un objeto DataInputStream, se utilizan los mismos mtodos que para FileInputStream:
DataInputStream miDStream; FileInputStream miFStream; // Obtiene un controlador de fichero miFStream = new FileInputStream "/etc/ejemplo.dbf" ); //Encadena un fichero de entrada de datos miDStream = new DataInputStream( miFStream ); // Ahora se pueden utilizar los dos streams de entrada para // acceder al fichero (si se quiere...) miFStream.read( b ); i = miDStream.readInt(); // Cierra el fichero de datos explcitamente //Siempre se cierra primero el fichero stream de mayor nivel miDStream.close(); miFStream.close();

Lectura de un DataInputStream Al acceder a un fichero como DataInputStream, se pueden utilizar los mismos mtodos read() de los objetos FileInputStream. No obstante, tambin se tiene acceso a otros mtodos diseados para leer cada uno de los tipos de datos:
byte readByte() int readUnsignedByte() short readShort() int readUnsignedShort() char readChar() int readInt() long readLong() float readFloat() double readDouble() String readLine()

Cada mtodo leer un objeto del tipo pedido.

Para el mtodo String readLine(), se marca el final de la cadena con \n, \r, \r\n o con EOF. Para leer un long, por ejemplo:
long numeroSerie; ... numeroSerie = miDStream.readLong();

Streams de entrada de URLs


Adems del acceso a ficheros, Java proporciona la posibilidad de acceder a URLs como una forma de acceder a objetos a travs de la red. Se utiliza implcitamente un objeto URL al acceder a sonidos e imgenes, con el mtodo getDocumentBase() en los applets:
String imagenFich = new String( "imagenes/pepe.gif" ); imagenes[0] = getImage( getDocumentBase(),imagenFich );

No obstante, se puede proporcionar directamente un URL, si se quiere:


URL imagenSrc; imagenSrc = new URL( "http://enterprise.com/~info" ); imagenes[0] = getImage( imagenSrc,"imagenes/pepe.gif" );

Apertura de un Stream de entrada de URL Tambin se puede abrir un stream de entrada a partir de un URL. Por ejemplo, se puede utilizar un fichero de datos para un applet:
ImputStream is; byte buffer[] = new byte[24]; is = new URL( getDocumentBase(),datos).openStream();

Ahora se puede utilizar is para leer informacin de la misma forma que se hace con un objeto FileInputStream:
is.read( buffer,0,buffer.length );

STREAMS DE SALIDA
La contrapartida necesaria de la lectura de datos es la escritura de datos. Como con los Streams de entrada, las clases de salida estn ordenadas jerrquicamente:

Examinaremos las clases FileOutputStream y DataOutputStream para complementar los streams de entrada que se han visto. En los ficheros fuente del directorio $JAVA_HOME/src/java/io se puede ver el uso y mtodos de estas clases, as como de los streams de entrada ($JAVA_HOME es el directorio donde se haya instalado el Java Development Kit, en sistemas UNIX).

Objetos FileOutputStream
Los objetos FileOutputStream son tiles para la escritura de ficheros de texto. Como con los ficheros de entrada, primero se necesita abrir el fichero para luego escribir en l. Apertura de un FileOutputStream Para abrir un objeto FileOutputStream, se tienen las mismas posibilidades que para abrir un fichero stream de entrada. Se le da al constructor un String o un objeto File.
FileOutputStream miFicheroSt; miFicheroSt = new FileOutputStream( "/etc/kk" );

Como con los streams de entrada, tambin se puede utilizar:


File miFichero FileOutputStream miFicheroSt; miFichero = new File( "/etc/kk" ); miFicheroSt = new FileOutputStream( miFichero );

Escritura en un FileOutputStream Una vez abierto el fichero, se pueden escribir bytes de datos utilizando el mtodo write(). Como con el mtodo read() de los streams de entrada, tenemos tres posibilidades:
void write( int b );
Escribe un byte.

void write( byte b[] );


Escribe todo el array, si es posible.

void write( byte b[],int offset,int longitud );


Escribe longitud bytes en b comenzando por b[offset].

Cierre de FileOutputStream Cerrar un stream de salida es similar a cerrar streams de entrada. Se puede utilizar el mtodo explcito:
miFicheroSt.close();

O, se puede dejar que el sistema cierre el fichero cuando se recicle miFicheroSt. Ejemplo: Almacenamiento de Informacin Este programa, Telefonos.java, pregunta al usuario una lista de nombres y nmeros de telfono. Cada nombre y nmero se aade a un fichero situado en una localizacin fija. Para indicar que se ha introducido toda la lista, el usuario especifica "Fin" ante la solicitud de entrada del nombre. Una vez que el usuario ha terminado de teclear la lista, el programa crear un fichero de salida que se mostrar en pantalla o se imprimir. Por ejemplo:
95-4751232,Juanito 564878,Luisa 123456,Pepe 347698,Antonio 91-3547621,Maria

El cdigo fuente del programa es el siguiente:


import java.io.*; class Telefonos { static FileOutputStream fos; public static final int longLinea = 81; public static void main( String args[] ) throws IOException { byte tfno[] = new byte[longLinea]; byte nombre[] = new byte[longLinea]; fos = new FileOutputStream( "telefono.dat" ); while( true ) { System.err.println( "Teclee un nombre ('Fin' termina)" ); leeLinea( nombre ); if( "fin".equalsIgnoreCase( new String( nombre,0,0,3 ) ) ) break; System.err.println( "Teclee el numero de telefono" ); leeLinea( tfno ); for( int i=0; tfno[i] != 0; i++ ) fos.write( tfno[i] ); fos.write( ',' ); for( int i=0; nombre[i] != 0; i++ ) fos.write( nombre[i] ); fos.write( '\n' ); } fos.close(); }

private static void leeLinea( byte linea[] ) throws IOException { int b = 0; int i = 0; while( (i < ( longLinea-1) ) && ( ( b = System.in.read() ) != '\n' ) ) linea[i++] = (byte)b; linea[i] = (byte)0; } }

Streams de salida con buffer


Si se trabaja con gran cantidad de datos, o se escriben muchos elementos pequeos, ser una buena idea utilizar un stream de salida con buffer. Los streams con buffer ofrecen los mismos mtodos de la clase FileOutputStream, pero toda salida se almacena en un buffer. Cuando se llena el buffer, se enva a disco con una nica operacin de escritura; o, en caso necesario, se puede enviar el buffer a disco en cualquier momento. Creacin de Streams de salida con buffer Para crear un stream BufferedOutput, primero se necesita un stream FileOutput normal; entonces se le aade un buffer al stream:
FileOutputStream miFileStream; BufferdOutpurStream miBufferStream; // Obtiene un controlador de fichero miFileStream = new FileOutputStream( "/tmp/kk" ); // Encadena un stream de salida con buffer miBufferStream = new BufferedOutputStream( miFileStream );

Volcado y Cierre de Streams de salida con buffer Al contrario que los streams FileOutput, cada escritura al buffer no se corresponde con una escritura en disco. A menos que se llene el buffer antes de que termine el programa, cuando se quiera volcar el buffer explcitamente se debe hacer mediante una llamada a flush():
// Se fuerza el volcado del buffer a disco miBufferStream.flush(); // Cerramos el fichero de datos. Siempre se ha de cerrar primero el // fichero stream de mayor nivel miBufferStream.close(); miFileStream.close();

Streams DataOutput
Java tambin implementa una clase de salida complementaria a la clase DataInputStream. Con la clase DataOutputStream, se pueden escribir datos binarios en un fichero. Apertura y cierre de objetos DataOutputStream

Para abrir y cerrar objetos DataOutputStream, se utilizan los mismos mtodos que para los objetos FileOutputStream:
DataOutputStream miDataStream; FileOutputStream miFileStream; BufferedOutputStream miBufferStream; // Obtiene un controlador de fichero miFileStream = new FileOutputStream( "/tmp/kk" ); // Encadena un stream de salida con buffer (por eficiencia) miBufferStream = new BufferedOutputStream( miFileStream ); // Encadena un fichero de salida de datos miDataStream = new DataOutputStream( miBufferStream ); // Ahora se pueden utilizar los dos streams de entrada para // acceder al fichero (si se quiere) miBufferStream.write( b ); miDataStream.writeInt( i ); // Cierra el fichero de datos explcitamente. Siempre se cierra // primero el fichero stream de mayor nivel miDataStream.close(); miBufferStream.close(); miFileStream.close();

Escritura en un objeto DataOutputStream Cada uno de los mtodos write() accesibles por los FileOutputStream tambin lo son a travs de los DataOutputStream. Tambin encontrar mtodos complementarios a los de DataInputStream:
void void void void void void void void void writeBoolean( boolean b ); writeByte( int i ); writeShort( int i ); writeChar( int i ); writeInt( int i ); writeFloat( float f ); writeDouble( double d ); writeBytes( String s ); writeChars( string s );

Para las cadenas, se tienen dos posibilidades: bytes y caracteres. Hay que recordar que los bytes son objetos de 8 bits y los caracteres lo son de 16 bits. Si nuestras cadenas utilizan caracteres Unicode, debemos escribirlas con writeChars().

Contabilidad de la salida

Otra funcin necesaria durante la salida es el mtodo size(). Este mtodo simplemente devuelve el nmero total de bytes escritos en el fichero. Se puede utilizar size() para ajustar el tamao de un fichero a mltiplo de cuatro. Por ejemplo, de la forma siguiente:
. . . int numBytes = miDataStream.size() % 4; for( int i=0; i < numBytes; i++ ) miDataStream.write( 0 ); . . .

FICHEROS DE ACCESO ALEATORIO


A menudo, no se desea leer un fichero de principio a fin; sino acceder al fichero como una base de datos, donde se salta de un registro a otro; cada uno en diferentes partes del fichero. Java proporciona una clase RandomAccessFile para este tipo de entrada/salida.

Creacin de un Fichero de Acceso Aleatorio


Hay dos posibilidades para abrir un fichero de acceso aleatorio: Con el nombre del fichero:
miRAFile = new RandomAccessFile( String nombre,String modo );

Con un objeto File:


miRAFile = new RandomAccessFile( File fichero,String modo );

El argumento modo determina si se tiene acceso de slo lectura (r) o de lectura/escritura (r/w). Por ejemplo, se puede abrir un fichero de una base de datos para actualizacin:
RandomAccessFile miRAFile; miRAFile = new RandomAccessFile( "/tmp/kk.dbf","rw" );

Acceso a la Informacin
Los objetos RandomAccessFile esperan informacin de lectura/escritura de la misma manera que los objetos DataInput/DataOutput. Se tiene acceso a todas las operaciones read() y write() de las clases DataInputStream y DataOutputStream. Tambin se tienen muchos mtodos para moverse dentro de un fichero:
long getFilePointer();
Devuelve la posicin actual del puntero del fichero

void seek( long pos );


Coloca el puntero del fichero en una posicin determinada. La posicin se da como un desplazamiento en bytes desde el comienzo del fichero. La posicin 0 marca el comienzo de ese fichero.

long length();
Devuelve la longitud del fichero. La posicin length() marca el final de ese fichero.

Actualizacin de Informacin
Se pueden utilizar ficheros de acceso aleatorio para aadir informacin a ficheros existentes:
miRAFile = new RandomAccessFile( "/tmp/kk.log","rw" ); miRAFile.seek( miRAFile.length() ); // Cualquier write() que hagamos a partir de este punto del cdigo // aadir informacin al fichero

Vamos a ver un pequeo ejemplo, Log.java, que aade una cadena a un fichero existente: import java.io.*; // Cada vez que ejecutemos este programita, se incorporara una nueva // linea al fichero de log que se crea la primera vez que se ejecuta // class Log { public static void main( String args[] ) throws IOException { RandomAccessFile miRAFile; String s = "Informacion a incorporar\nTutorial de Java\n"; // Abrimos el fichero de acceso aleatorio miRAFile = new RandomAccessFile( "/tmp/java.log","rw" ); // Nos vamos al final del fichero miRAFile.seek( miRAFile.length() ); // Incorporamos la cadena al fichero miRAFile.writeBytes( s ); // Cerramos el fichero miRAFile.close(); } }

THREADS: PROGRAMAS MULTITAREA Los procesadores y los Sistemas Operativos modernos permiten la multitarea, es decir, la realizacin simultnea de dos o ms actividades (al menos aparentemente). En la realidad, un ordenador con una sola CPU no puede realizar dos actividades a la vez. Sin embargo los Sistemas Operativos actuales son capaces de ejecutar varios programas "simultneamente" aunque slo se disponga de una CPU: reparten el tiempo entre dos (o ms) actividades, o bien utilizan los tiempos muertos de una actividad (por ejemplo, operaciones de lectura de datos desde el teclado) para trabajar en la otra. En ordenadores con dos o ms procesadores la multitarea es real, ya que cada procesador puede ejecutar un hilo o thread diferente. memoria. Un Sistema Operativo multitarea es capaz de ejecutar ms de un proceso simultneamente. Un thread o hilo es un flujo secuencial simple dentro de un proceso. Un nico proceso puede tener varios hilos ejecutndose. Por ejemplo el programa Netscape sera un proceso, mientras que cada una de las ventanas que se pueden tener abiertas simultneamente trayendo pginas HTML estara formada por al menos un hilo. Un sistema multitarea da realmente la impresin de estar haciendo varias cosas a la vez y eso es una gran ventaja para el usuario. Sin el uso de threads hay tareas que son prcticamente imposibles de ejecutar, particularmente las que tienen tiempos de espera importantes entre etapas. Los threads o hilos de ejecucin permiten organizar los recursos del ordenador de forma que pueda haber varios programas actuando en paralelo. Un hilo de ejecucin puede realizar cualquier tarea que pueda realizar un programa normal y corriente. Bastar con indicar lo que tiene que hacer en el mtodo run(), que es el que define la actividad principal de las threads. Los threads pueden ser daemon o no daemon. Son daemon aquellos hilos que realizan en background (en un segundo plano) servicios generales, esto es, tareas que no forman parte de la esencia del programa y que se estn ejecutando mientras no finalice la aplicacin. Un thread daemon podra ser por ejemplo aqul que est comprobando permanentemente si el usuario pulsa un botn. Un programa de Java finaliza cuando slo quedan corriendo threads de tipo daemon. Por defecto, y si no se indica lo contrario, los threads son del tipo no daemon.

CREACIN DE THREADS
En Java hay dos formas de crear nuevos threads. La primera de ellas consiste en crear una nueva clase que herede de la clase java.lang.Thread y sobrecargar el mtodo run() de dicha clase. El segundo mtodo consiste en declarar una clase que implemente la interface java.lang.Runnable, la cual declarar el mtodo run(); posteriormente se crea un objeto de tipo Thread pasndole como argumento al constructor el objeto creado de la nueva clase (la que implementa la interface Runnable). Como ya se ha apuntado, tanto la clase Thread como la interface Runnable pertenecen al package java.lang, por lo que no es necesario

importarlas. A continuacin se presentan dos ejemplos de creacin de threads con cada uno de los dos mtodos citados.

Creacin de threads derivando de la clase Thread


Considrese el siguiente ejemplo de declaracin de una nueva clase: public class SimpleThread extends Thread { // constructor public SimpleThread (String str) { super(str); } // redefinicin del mtodo run() public void run() { for(int i=0;i<10;i++) System.out.println("Este es el thread : " + getName()); } } En este caso, se ha creado la clase SimpleThread, que hereda de Thread. En su constructor se utiliza un String (opcional) para poner nombre al nuevo thread creado, y mediante super() se llama al constructor de la super-clase Thread. Asimismo, se redefine el mtodo run(), que define la principal actividad del thread, para que escriba 10 veces el nombre del thread creado. Para poner en marcha este nuevo thread se debe crear un objeto de la clase SimpleThread, y llamar al mtodo start(),heredado de la super-clase Thread, que se encarga de llamar a run(). Por ejemplo: SimpleThread miThread = new SimpleThread(Hilo de prueba); miThread.start();

Creacin de threads implementando la interface Runnable


Esta segunda forma tambin requiere que se defina el mtodo run(), pero adems es necesario crear un objeto de la clase Thread para lanzar la ejecucin del nuevo hilo. Al constructor de la clase Thread hay que pasarle una referencia del objeto de la clase que implementa la interface Runnable. Posteriormente, cuando se ejecute el mtodo start() del thread, ste llamar al mtodo run() definido en la nueva clase. A continuacin se muestra el mismo estilo de clase que en el ejemplo anterior implementada mediante la interface Runnable: public class SimpleRunnable implements Runnable { // se crea un nombre String nameThread; // constructor public SimpleRunnable (String str) { nameThread = str; } // definicin del mtodo run() public void run() { for(int i=0;i<10;i++) System.out.println("Este es el thread: " + nameThread); } } El siguiente cdigo crea un nuevo thread y lo ejecuta por este segundo procedimiento: SimpleRunnable p = new SimpleRunnable("Hilo de prueba"); // se crea un objeto de la clase Thread pasndolo el objeto Runnable como argumento Thread miThread = new Thread(p); // se arranca el objeto de la clase Thread miThread.start(); Este segundo mtodo cobra especial inters con las applets, ya que cualquier applet debe heredar de la clase java.applet.Applet, y por lo tanto ya no puede heredar de Thread. Vase el siguiente ejemplo:

class ThreadRunnable extends Applet implements Runnable { private Thread runner=null; // se redefine el mtodo start() de Applet public void start() { if (runner == null) { runner = new Thread(this); runner.start(); // se llama al mtodo start() de Thread } } // se redefine el mtodo stop() de Applet public void stop(){ runner = null; // se libera el objeto runner } } En este ejemplo, el argumento this del constructor de Thread hace referencia al objeto Runnable cuyo mtodo run() debera ser llamado cuando el hilo ejecutado es un objeto de ThreadRunnable. La eleccin de una u otra forma -derivar de Thread o implementar Runnable- depende del tipo de clase que se vaya a crear. As, si la clase a utilizar ya hereda de otra clase (por ejemplo un applet, que siempre hereda de Applet), no quedar ms remedio que implementar Runnable, aunque normalmente es ms sencillo heredar de Thread.

CICLO DE VIDA DE UN THREAD


En el apartado anterior se ha visto cmo crear nuevos objetos que permiten incorporar en un programa la posibilidad de realizar varias tareas simultneamente. En la Figura 6.2 (tomada del Tutorial de Sun) se muestran los distintos estados por los que puede pasar un thread a lo largo de su vida. Un thread puede presentar cuatro estados distintos: 1. Nuevo (New): El thread ha sido creado pero no inicializado, es decir, no se ha ejecutadotodava el mtodo start(). Se producir un mensaje de error (IllegalThreadStateException) si se intenta ejecutar cualquier mtodo de la clase Thread distinto de start(). 2. Ejecutable (Runnable): El thread puede estar ejecutndose, siempre y cuando se le haya asignado un determinado tiempo de CPU. En la prctica puede no estar siendo ejecutado en un instante determinado en beneficio de otro thread. 3. Bloqueado (Blocked o Not Runnable): El thread podra estar ejecutndose, pero hay alguna actividad interna suya que lo impide, como por ejemplo una espera producida por una operacin de escritura o lectura de datos por teclado (E/S). Si un thread est en este estado, no se le asigna tiempo de CPU. 4. Muerto (Dead): La forma habitual de que un thread muera es finalizando el mtodo run(). Tambin puede llamarse al mtodo stop() de la clase Thread, aunque dicho mtodo es considerado peligroso y no se debe utilizar. A continuacin se explicarn con mayor detenimiento los puntos anteriores.

Ejecucin de un nuevo thread


La creacin de un nuevo thread no implica necesariamente que se empiece a ejecutar algo. Hace falta iniciarlo con el mtodo start(), ya que de otro modo, cuando se intenta ejecutar cualquier mtodo del thread -distinto del mtodo start()- se obtiene en tiempo de ejecucin el error IllegalThreadStateException. El mtodo start() se encarga de llamar al mtodo run() de la clase Thread. Si el nuevo thread se ha creado heredando de la clase Thread la nueva clase deber redefinirir el mtodo run() heredado. En el caso de utilizar una clase que implemente la interface Runnable, el mtodo run() de la clase Thread se ocupa de llamar al mtodo run() de la nueva clase Una vez que el mtodo start() ha sido llamado, se puede decir ya que el thread est

corriendo (running), lo cual no quiere decir que se est ejecutando en todo momento, pues ese thread tiene que compartir el tiempo de la CPU con los dems threads que tambin estn running. Por eso ms bien se dice que dicha thread es runnable.

Detener un Thread temporalmente: Runnable - Not Runnable


El sistema operativo se ocupa de asignar tiempos de CPU a los distintos threads que se estn ejecutando simultneamente. Aun en el caso de disponer de un ordenador con ms de un procesador (2 ms CPUs), el nmero de threads simultneos suele siempre superar el nmero de CPUs, por lo que se debe repartir el tiempo de forma que parezca que todos los procesos corren a la vez (quizs ms lentamente), aun cuando slo unos pocos pueden estar ejecutndose en un instante de tiempo. Los tiempos de CPU que el sistema continuamente asigna a los distintos threads en estado runnable se utilizan en ejecutar el mtodo run() de cada thread. Por diversos motivos, un thread puede en un determinado momento renunciar voluntariamente a su tiempo de CPU y otorgrselo al sistema para que se lo asigne a otro thread. Esta renuncia se realiza mediante el mtodo yield(). Es importante que este mtodo sea utilizado por las actividades que tienden a monopolizar la CPU. El mtodo yield() viene a indicar que en ese momento no es muy importante para ese thread el ejecutarse continuamente y por lo tanto tener ocupada la CPU. En caso de que ningn thread est requiriendo la CPU para una actividad muy intensiva, el sistema volver casi de inmediato a asignar nuevo tiempo al thread que fue generoso con los dems. Por ejemplo, en un Pentium II 400 Mhz es posible llegar a ms de medio milln de llamadas por segundo al mtodo yield(), dentro del mtodo run(), lo que significa que llamar al mtodo yield() apenas detiene al thread, sino que slo ofrece el control de la CPU para que el sistema decida si hay alguna otra tarea que tenga mayor prioridad. Si lo que se desea es parar o bloquear temporalmente un thread (pasar al estado Not Runnable), existen varias formas de hacerlo:

1. Ejecutando el mtodo sleep() de la clase Thread. Esto detiene el thread un tiempo preestablecido. De ordinario el mtodo sleep() se llama desde el mtodo run().
2. Ejecutando el mtodo wait() heredado de la clase Object, a la espera de que suceda algo que es necesario para poder continuar. El thread volver nuevamente a la situacin de runnable mediante los mtodos notify() o notifyAll(), que se debern ejecutar cuando cesa la condicin que tiene detenido al thread (ver Apartado 6.3, en la pgina 121). 3. Cuando el thread est esperando para realizar operaciones de Entrada/Salida o Input/Output (E/S I/O). 4. Cuando el thread est tratando de llamar a un mtodo synchronized de un objeto, y dicho objeto est bloqueado por otro thread (vase el Apartado 6.3) Un thread pasa automticamente del estado Not Runnable a Runnable cuando cesa alguna de las condiciones anteriores o cuando se llama a notify() o notifyAll(). La clase Thread dispone tambin de un mtodo stop(), pero no se debe utilizar ya que puede provocar bloqueos del programa (deadlock). Hay una ltima posibilidad para detener un thread, que consiste en ejecutar el mtodo suspend(). El thread volver a ser ejecutable de nuevo ejecutando el mtodo resume(). Esta ltima forma tambin se desaconseja, por razones similares a la utilizacin del mtodo stop(). El mtodo sleep() de la clase Thread recibe como argumento el tiempo en milisegundos que ha de permanecer detenido. Adicionalmente, se puede incluir un nmero entero con un tiempo adicional en nanosegundos. Las declaraciones de estos mtodos son las siguientes: public static void sleep(long millis) throws InterruptedException public static void sleep(long millis, int nanosecons) throws InterruptedException Considrese el siguiente ejemplo:

System.out.println ("Contador de segundos"); int count=0; public void run () { try { sleep(1000); System.out.println(count++); } catch (InterruptedException e){} } Se observa que el mtodo sleep() puede lanzar una InterruptedException que ha de ser capturada. As se ha hecho en este ejemplo, aunque luego no se gestiona esa excepcin. La forma preferible de detener temporalmente un thread es la utilizacin conjunta de los mtodos wait() y notifyAll(). La principal ventaja del mtodo wait() frente a los mtodos anteriormente descritos es que libera el bloqueo del objeto. por lo que el resto de threads que se encuentran esperando para actuar sobre dicho objeto pueden llamar a sus mtodos. Hay dos formas de llamar a wait(): 1. Indicando el tiempo mximo que debe estar parado (en milisegundos y con la opcin de indicar tambin nanosegundos), de forma anloga a sleep(). A diferencia del mtodo sleep(), que simplemente detiene el thread el tiempo indicado, el mtodo wait() establece el tiempo mximo que debe estar parado. Si en ese plazo se ejecutan los mtodos notify() o notifyAll() que indican la liberacin de los objetos bloqueados, el thread continuar sin esperar a concluir el tiempo indicado. Las dos declaraciones del mtodo wait() son como siguen: public final void wait(long timeout) throws InterruptedException public final void wait(long timeout, int nanos) throws InterruptedException 2. Sin argumentos, en cuyo caso el thread permanece parado hasta que sea reinicializado explcitamente mediante los mtodos notify() o notifyAll(). public final void wait() throws InterruptedException Los mtodos wait() y notify() han de estar incluidas en un mtodo synchronized, ya que de otra forma se obtendr una excepcin del tipo IllegalMonitorStateException en tiempo de ejecucin. El uso tpico de wait() es el de esperar a que se cumpla alguna determinada condicin, ajena al propio thread. Cuando sta se cumpla, se utilizar el mtodo notifyAll() para avisar a los distintos threads que pueden utilizar el objeto. Estos nuevos conceptos se explican con ms profundidad en el Apartado 6.3.

Finalizar un Thread
Un thread finaliza cuando el mtodo run() devuelve el control, por haber terminado lo que tena que hacer (por ejemplo, un bucle for que se ejecuta un nmero determinado de veces) o por haberse dejado de cumplir una condicin (por ejemplo, por un bucle while en el mtodo run()). Es habitual poner las siguientes sentencias en el caso de Applets Runnables: public class MyApplet extends Applet implements Runnable { // se crea una referencia tipo Thread private Thread AppletThread; ... // mtodo start() del Applet public void start() { if(AppletThread == null){ // si no tiene un objeto Thread asociado AppletThread = new Thread(this, "El propio Applet"); AppletThread.start(); // se arranca el thread y llama a run() } } // mtodo stop() del Applet public void stop() {

AppletThread = null; // iguala la referencia a null } // mtodo run() por implementar Runnable public void run() { Thread myThread = Thread.currentThread(); while (myThread == AppletThread) { // hasta que se ejecute stop() de Thread ... // cdigo a ejecutar } } } // fin de la clase MyApplet donde AppletThread es el thread que ejecuta el mtodo run() MyApplet. Para finalizar el thread basta poner la referencia AppletThread a null. Esto se consigue en el ejemplo con el mtodo stop() del applet (distinto del mtodo stop() de la clase Thread, que no conviene utilizar). Para saber si un thread est vivo o no, es til el mtodo isAlive() de la clase Thread, que devuelve true si el thread ha sido inicializado y no parado, y false si el thread es todava nuevo (no ha sido inicializado) o ha finalizado. SINCRONIZACIN La sincronizacin nace de la necesidad de evitar que dos o ms threads traten de acceder a los mismos recursos al mismo tiempo. As, por ejemplo, si un thread tratara de escribir en un fichero, y otro thread estuviera al mismo tiempo tratando de borrar dicho fichero, se producira una situacin no deseada. Otra situacin en la que hay que sincronizar threads se produce cuando un thread debe esperar a que estn preparados los datos que le debe suministrar el otro thread. Para solucionar estos tipos de problemas es importante poder sincronizar los distintos threads. Las secciones de cdigo de un programa que acceden a un mismo recurso (un mismo objeto de una clase, un fichero del disco, etc.) desde dos threads distintos se denominan secciones crticas (critical sections). Para sincronizar dos o ms threads, hay que utilizar el modificador synchronized en aquellos mtodos del objeto-recurso con los que puedan producirse situaciones conflictivas. De esta forma, Java bloquea (asocia un bloqueo o lock) con el recurso sincronizado. Por ejemplo: public synchronized void metodoSincronizado() { ...// accediendo por ejemplo a las variables de un objeto ... } La sincronizacin previene las interferencias solamente sobre un tipo de recurso: la memoria reservada para un objeto. Cuando se prevea que unas determinadas variables de una clase pueden tener problemas de sincronizacin, se debern declarar como private (o protected). De esta forma slo estarn accesibles a travs de mtodos de la clase, que debern estar sincronizados. Es muy importante tener en cuenta que si se sincronizan algunos mtodos de un objeto pero otros no, el programa puede no funcionar correctamente. La razn es que los mtodos no sincronizados pueden acceder libremente a las variables miembro, ignorando el bloqueo del objeto. Slo los mtodos sincronizados comprueban si un objeto est bloqueado. Por lo tanto, todos los mtodos que accedan a un recurso compartido deben ser declarados synchronized. De esta forma, si algn mtodo accede a un determinado recurso, Java bloquea dicho recurso, de forma que el resto de threads no puedan acceder al mismo hasta que el primero en acceder termine de realizar su tarea. Bloquear un recurso u objeto significa que sobre ese objeto no pueden actuar simultneamente dos mtodos sincronizados. Existen dos niveles de bloqueo de un recurso. El primero es a nivel de objetos, mientras que el segundo es a nivel de clases. El primero se consigue declarando todos los mtodos de una clase como synchronized. Cuando se ejecuta un mtodo synchronized sobre un objeto concreto, el sistema bloquea dicho objeto, de forma que si otro thread intenta ejecutar algn mtodo sincronizado de ese objeto, este segundo mtodo se mantendr a la espera hasta que finalice el anterior (y desbloquee por lo tanto el objeto). Si existen varios

objetos de una misma clase , como los bloqueos se producen a nivel de objeto, es posible tener distintos threads ejecutando mtodos sobre diversos objetos de una misma clase. El bloqueo de recursos a nivel de clases se corresponde con los mtodos de clase o static, y por lo tanto con las variables de clase o static. Si lo que se desea es conseguir que un mtodo bloquee simultneamente una clase entera, es decir todos los objetos creados de una clase, es necesario declarar este mtodo como synchronized static. Durante la ejecucin de un mtodo declarado de esta segunda forma ningn mtodo sincronizado tendr acceso a ningn objeto de la clase bloqueada. La sincronizacin puede ser problemtica y generar errores. Un thread podra bloquear un determinado recurso de forma indefinida, impidiendo que el resto de threads accedieran al mismo. Para evitar esto ltimo, habr que utilizar la sincronizacin slo donde sea estrictamente necesario. Es necesario tener presente que si dentro un mtodo sincronizado se utiliza el mtodo sleep() de la clase Thread, el objeto bloqueado permanecer en ese estado durante el tiempo indicado en el argumento de dicho mtodo. Esto implica que otros threads no podrn acceder a ese objeto durante ese tiempo, aunque en realidad no exista peligro de simultaneidad ya que durante ese tiempo el thread que mantiene bloqueado el objeto no realizar cambios. Para evitarlo es conveniente sustituir sleep() por el mtodo wait() de la clase java.lang.Object heredado automticamente por todas las clases. Cuando se llama al mtodo wait() (siempre debe hacerse desde un mtodo o bloque synchronized) se libera el bloqueo del objeto y por lo tanto es posible continuar utilizando ese objeto a travs de mtodos sincronizados. El mtodo wait() detiene el thread hasta que se llame al mtodo notify() o notifyAll() del objeto, o finalice el tiempo indicado como argumento del mtodo wait(). El mtodo unObjeto.notify() lanza una seal indicando al sistema que puede activar uno de los threads que se encuentren bloqueados esperando para acceder al objeto unObjeto. El mtodo notifyAll() lanza una seal a todos los threads que estn esperando la liberacin del objeto. Los mtodos notify() y notifyAll() deben ser llamados desde el thread que tiene bloqueado el objeto para activar el resto de threads que estn esperando la liberacin de un objeto. Un thread se convierte en propietario del bloqueo de un objeto ejecutando un mtodo sincronizado del objeto. Los bloqueos de tipo clase, se consiguen ejecutando un mtodo de clase sincronizado (synchronized static). Vanse las dos funciones siguientes, de las que put() inserta un dato y get() lo recoge: public synchronized int get() { while (available == false) { try { // Espera a que put() asigne el valor y lo comunique con notify() wait(); } catch (InterruptedException e) { } } available = false; // notifica que el valor ha sido ledo notifyAll(); // devuelve el valor return contents; } public synchronized void put(int value) { while (available == true) { try { // Espera a que get() lea el valor disponible antes de darle otro wait(); } catch (InterruptedException e) { } } // ofrece un nuevo valor y lo declara disponible contents = value; available = true;

// notifica que el valor ha sido cambiado notifyAll();} El bucle while de la funcin get() contina ejecutndose (avalaible == false) hasta que el mtodo put() haya suministrado un nuevo valor y lo indique con avalaible = true. En cada iteracin del while la funcin wait() hace que el hilo que ejecuta el mtodo get() se detenga hasta que se produzca un mensaje de que algo ha sido cambiado (en este caso con el mtodo notifAll() ejecutado por put()). El mtodo put() funciona de forma similar. Existe tambin la posibilidad de sincronizar una parte del cdigo de un mtodo sin necesidad de mantener bloqueado el objeto desde el comienzo hasta el final del mtodo. Para ello se utiliza la palabra clave syncronized indicando entre parntesis el objeto que se desea sincronizar (synchronized(objetoASincronizar)). Por ejemplo si se desea sincronizar el propio thread en una parte del mtodo run(), el cdigo podra ser: public void run() { while(true) { ... syncronized(this) { // El objeto a sincronizar es el propio thread ... // Cdigo sincronizado } try { sleep(500); // Se detiene el thread durante 0.5 segundos pero el objeto // es accesible por otros threads al no estar sincronizado } catch(InterruptedException e) {} } } Un thread puede llamar a un mtodo sincronizado de un objeto para el cual ya posee el bloqueo, volviendo a adquirir el bloqueo. Por ejemplo: public class VolverAAdquirir { public synchronized void a() { b(); System.out.println("Estoy en a()"); } public synchronized void b() { System.out.println("Estoy en b()"); } }

El anterior ejemplo obtendr como resultado: Estoy en b() Estoy en a() debido a que se ha podido acceder al objeto con el mtodo b() al ser el thread que ejecuta el mtodo a() propietario con anterioridad del bloqueo del objeto. La sincronizacin es un proceso que lleva bastante tiempo a la CPU, luego se debe minimizar su uso, ya que el programa ser ms lento cuanta ms sincronizacin incorpore. PRIORIDADES Con el fin de conseguir una correcta ejecucin de un programa se establecen prioridades en los threads, de forma que se produzca un reparto ms eficiente de los recursos disponibles. As, en un determinado momento, interesar que un determinado proceso acabe lo antes posible sus clculos, de forma que habr que otorgarle ms recursos (ms tiempo de CPU). Esto no sign ifica que el resto de procesos no requieran tiempo de CPU, sino que necesitarn menos. La forma de llevar a cabo esto es gracias a las prioridades. Cuando se crea un nuevo thread, ste hereda la prioridad del thread desde el que ha sido inicializado. Las prioridades viene definidas por variables miembro de la clase Thread, que toman valores enteros que oscilan entre la mxima prioridad MAX_PRIORITY (normalmente tiene el valor 10) y la mnima prioridad MIN_PRIORITY (valor 1), siendo la

prioridad por defecto NORM_PRIORITY (valor 5). Para modificar la prioridad de un thread se utiliza el mtodo setPriority(). Se obtiene su valor con getPriority(). El algoritmo de distribucin de recursos en Java escoge por norma general aquel thread que tiene una prioridad mayor, aunque no siempre ocurra as, para evitar que algunos procesos queden dormidos. Cuando hay dos o ms threads de la misma prioridad (y adems, dicha prioridad es la ms elevada), el sistema no establecer prioridades entre los mismos, y los ejecutar alternativamente dependiendo del sistema operativo en el que est siendo ejecutado. Si dicho SO soporta el time-slicing (reparto del tiempo de CPU), como por ejemplo lo hace Windows 95/98/NT, los threads sern ejecutados alternativamente. Un thread puede en un determinado momento renunciar a su tiempo de CPU y otorgrselo a otro thread de la misma prioridad, mediante el mtodo yield(), aunque en ningn caso a un thread de prioridad inferior.

GRUPOS DE THREADS Todo hilo de Java debe formar parte de un grupo de hilos (ThreadGroup). Puede pertenecer al grupo por defecto o a uno explcitamente creado por el usuario. Los grupos de threads proporcionan una forma sencilla de manejar mltiples threads como un solo objeto. As, por ejemplo es posible parar varios threads con una sola llamada al mtodo correspondiente. Una vez que un thread ha sido asociado a un threadgroup, no puede cambiar de grupo. Cuando se arranca un programa, el sistema crea un ThreadGroup llamado main. Si en la creacin de un nuevo thread no se especifica a qu grupo pertenece, automticamente pasa a pertenecer al threadgroup del thread desde el que ha sido creado (conocido como

current thread group y current thread, respectivamente). Si en dicho programa no se crea ningn ThreadGroup adicional, todos los threads creados pertenecern al grupo main (en este grupo se encuentra el mtodo main()). La Figura 6.3 presenta una posible distribucin de threads distribuidos en grupos de threads. Para conseguir que un thread pertenezca a un grupo concreto, hay que indicarlo al crear el nuevo thread, segn uno de los siguientes constructores:
public Thread (ThreadGroup grupo, Runnable destino) public Thread (ThreadGroup grupo, String nombre) public Thread (ThreadGroup grupo, Runnable destino, String nombre) A su vez, un ThreadGroup debe pertenecer a otro ThreadGroup. Como ocurra en el caso anterior, si no se especifica ninguno, el nuevo grupo pertenecer al ThreadGroup desde el que ha sido creado (por defecto al grupo main). La clase ThreadGroup tiene dos posibles constructores: ThreadGroup(ThreadGroup parent, String nombre); ThreadGroup(String name); el segundo de los cuales toma como parent el threadgroup al cual pertenezca el thread desde el que se crea (Thread.currentThread()). Para ms informacin acerca de estos constructores, dirigirse a la documentacin del API de Java donde aparecen numerosos mtodos para trabajar con grupos de threads a disposicin del usuario (getMaxPriority(), setMaxPriority(), getName(), getParent(), parentOf()). En la prctica los ThreadGroups no se suelen utilizar demasiado. Su uso prctico se limita a efectuar determinadas operaciones de forma ms simple que de forma individual. En cualquier caso, vase el siguiente ejemplo: ThreadGroup miThreadGroup = new ThreadGroup("Mi Grupo de Threads"); Thread miThread = new Thread(miThreadGroup, un thread para mi grupo"); donde se crea un grupo de threads (miThreadGroup) y un thread que pertenece a dicho grupo (miThread).

También podría gustarte